/*
* @Author: 白哉
* @Description: 勇仪推技能
* @Date: 2026年03月06日
* @Modify:
*/
using RuntimeData;
using System;
using System.Collections.Generic;
using System.Linq;
using MemoryPack;
using TH1_Anim;
using TH1_Logic.Core;
using UnityEngine;
namespace Logic.Skill
{
public partial class YuugiPushSkill : SkillBase
{
public YuugiPushSkill()
{
IsPermanent = true;
TurnsLimit = 0;
Score = 4;
}
public override SkillType GetSkillType()
{
return SkillType.YuugiPush;
}
///
/// 推击攻击逻辑(纯数据),动画由UnitAttackAction.Execute负责
///
/// true表示推击已执行,false表示不满足推击条件应走普通攻击
public bool YuugiPushAttack(UnitData self, UnitData target, MapData map,
out int outAttackDmg, out int outCounterDmg, out FragmentType outFragmentType, out bool outPushed)
{
outAttackDmg = 0;
outCounterDmg = 0;
outFragmentType = FragmentType.Attack;
outPushed = false;
if (self == null || target == null) return false;
var fullType = new UnitFullType();
fullType.UnitType = UnitType.BonePile;
if (self.GetAttackRange(map) > 1) return false;
if (self.GetAllAttackValue(map, target) <= target.GetAllDefenseValue(map, self)) return false;
var player = self.Player(map);
if (player == null) return false;
var targetPlayer = target.Player(map);
if (targetPlayer == null) return false;
map.GetCapitalCityDataByPlayerId(player.Id, out var city);
var targetCity = target.City(map);
if (city == null || targetCity == null) return false;
var selfGrid = self.Grid(map);
var targetGrid = target.Grid(map);
if (selfGrid == null || targetGrid == null) return false;
var nextGrid = map.GridMap.GetNextGrid(selfGrid, targetGrid);
var dmg = Table.Instance.CalcDamage(map, self, target);
// 判断能否推动
// 使用 CheckUnitAbleForGrid_OfflineStatus 而非 CheckLandTypeForGrid:
// 前者会对 LandAndPort 单位检查港口归属(必须同联盟),后者只看地形 LandType。
// 之前用纯 LandType 判定会让勇仪(LandAndPort)合法走进敌方港口。
bool canPush = nextGrid != null && !nextGrid.RealUnit(map, out _)
&& Main.UnitLogic.CheckUnitAbleForGrid_OfflineStatus(map, targetPlayer, target, nextGrid)
&& Main.UnitLogic.CheckUnitAbleForGrid_OfflineStatus(map, player, self, targetGrid)
&& Main.UnitLogic.CheckUnitAbleForGrid_OfflineStatus(map, player, self, nextGrid);
// 能推→无额外伤害;不能推→+2伤害
int actualDmg = canPush ? dmg : dmg + 2;
// 只有能推动且实际伤害不会击杀,才走推人
if (canPush && actualDmg < target.Health)
{
// 推击:先移动数据,再攻击,可能有反击
outPushed = true;
Main.UnitLogic.MoveToLogic(map, target, nextGrid, MoveType.PushMove);
Main.UnitLogic.MoveToLogic(map, self, targetGrid, MoveType.AttackMove);
var cDmg = Table.Instance.CalcCounterDamage(map, self, target);
// 使用 push 专用反击判定:绕开通用 CanCounter 中 dmg1 重算导致的误判秒杀问题。
// push 分支入口已保证 actualDmg < target.Health,不可能秒杀,所以跳过该检查;
// 其余合法限制(联盟/IsLimit*CounterAttack/视野/距离)仍然保留。
bool canCounter = CanCounterForPush(map, self, target);
outAttackDmg = dmg;
Main.UnitLogic.DamageSettlement(map, self, target, dmg, DamageType.PushAttack);
if (canCounter && target.IsAlive() && self.IsAlive())
{
outCounterDmg = cDmg;
Main.UnitLogic.DamageSettlement(map, target, self, cDmg, DamageType.CounterAttack);
outFragmentType = !self.IsAlive() ? FragmentType.AttackAndCounterDie : FragmentType.AttackAndCounter;
}
}
else
{
// 不能推动、或伤害足以击杀:直接攻击(含可能的+2)
outPushed = false;
outAttackDmg = actualDmg;
var cDmg = Table.Instance.CalcCounterDamage(map, self, target);
bool canCounter = Main.UnitLogic.CanCounter(map, self, target);
Main.UnitLogic.DamageSettlement(map, self, target, actualDmg, DamageType.PushAttack);
if (target.IsAlive() && self.IsAlive() && canCounter)
{
// 未击杀:对方反击
outCounterDmg = cDmg;
Main.UnitLogic.DamageSettlement(map, target, self, cDmg, DamageType.CounterAttack);
outFragmentType = !self.IsAlive() ? FragmentType.AttackAndCounterDie : FragmentType.AttackAndCounter;
}
else if (!target.IsAlive() && Main.UnitLogic.CheckUnitAbleForGrid_OfflineStatus(map, player, self, targetGrid))
{
outFragmentType = FragmentType.MoveKill;
Main.UnitLogic.MoveToLogic(map, self, targetGrid, MoveType.AttackMove);
// 击杀英雄不生成骨堆
if (!target.TreatedAsHero(map, target))
{
var aroundBuf = RentAroundBuf();
map.GridMap.GetAroundGridData(1, 1, targetGrid, aroundBuf);
var randomList = new List();
foreach (var grid in aroundBuf)
{
if (grid == targetGrid) continue;
if (grid.RealUnit(map,out _)) continue;
if(!map.CheckLandTypeForGrid(fullType, grid))continue;
randomList.Add(grid);
}
ReturnAroundBuf();
if (randomList.Count > 0)
{
var index = map.Net.GetRandom(map).Next(0, randomList.Count - 1);
if (map.AddUnitData(randomList[index].Id, city.Id, fullType, out var bone))
{
bone.GetSkill(SkillType.BonePile, out var skill);
var bonePile = skill as BonePileSkill;
if (bonePile != null)
{
bonePile.TargetType = target.UnitFullType;
bonePile.TargetCityId = targetCity.Id;
}
bone.Health = Mathf.Max(1, UnitData.CeilPositiveToInt(bone.GetMaxHealth() / 4f));
var boneGrid = bone.Grid(map);
if (boneGrid != null)
{
var sightRadius = boneGrid.Feature == TerrainFeature.Mountain ? 2 : 1;
Main.PlayerLogic.UpdateSightByRadius_LogicView(map, player, boneGrid, sightRadius);
}
}
}
}
}
}
return true;
}
// Push 场景专用的反击判定。
// 问题背景:通用 UnitLogic.CanCounter 内部会重算 dmg1 = CalcDamage(self, target),
// push 发生后 self/target 的位置都变了,基于位置的 skill 加成可能让 dmg1 漂移;
// 如果漂移后 dmg1 >= target.Health,CanCounter 会判定"一击必杀 → 不反击",
// 但实际 DamageSettlement 用的还是 push 入口处算好的 dmg(< target.Health),
// target 其实没被秒杀 —— 结果就是"推开了、没死、但也不反击"的 bug。
// 修复思路:push 入口条件 actualDmg < target.Health 已保证非秒杀,
// 所以这里跳过重算 + 秒杀检查,只保留其余合法限制。
private static bool CanCounterForPush(MapData map, UnitData self, UnitData target)
{
if (!target.CanAttackAll(map) && map.IsLeagueUnitByUnit(self.Id, target.Id)) return false;
if (!map.GetPlayerDataByUnitId(target.Id, out var targetPlayer)) return false;
if (!map.GetGridDataByUnitId(self.Id, out var selfGrid)) return false;
if (!map.GetGridDataByUnitId(target.Id, out var targetGrid)) return false;
if (self.IsLimitTargetCounterAttack(map)) return false;
if (target.IsLimitSelfCounterAttack(map)) return false;
// target 所属玩家必须能看见 self 新位置
if (!targetPlayer.Sight.CheckIsInSight(selfGrid.Id)) return false;
// target 的攻击范围必须能覆盖 self
var dist = Table.Instance.CalcDistance(
new Vector2Int(selfGrid.Pos.X, selfGrid.Pos.Y),
new Vector2Int(targetGrid.Pos.X, targetGrid.Pos.Y));
if (dist > target.GetAttackRange(map)) return false;
return true;
}
}
}