daixiawu 491503a06d bug修复
1.修复了部分会导致船只出现在陆地的bug
2.修复了部分会导致已经死亡的单位图像错误残留的bug
3.修复了[车]职阶英雄在敌方城市中心能获得要塞防御加成的bug
4.修复了[车]职阶英雄在被动移动时也会产生溅射伤害的bug
5.修复了移动溅射在被动移动(如被挤开)也会生效的bug,完善了描述
6.修复了勇仪推人致死结果贴图错误
7.修复了洩矢诹访子创造御射宫司大人时会重置和平奇观计数的bug
8.优化了顶部信息栏金币显示排版问题
9.优化了多个窗口重叠时,快捷键“右键”及"Esc"关闭窗口的顺序
10.修复了间谍攻击隐身单位占据的城市中心时出错(反复播放攻击动画)的bug
11.修复了城墙建造后城市中心的单位没有立刻刷新状态的bug
2026-06-07 00:15:12 +08:00

240 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* @Author: 白哉
* @Description: 勇仪推技能
* @Date: 2026年03月06日
* @Modify:
*/
using RuntimeData;
using System;
using System.Collections.Generic;
using System.Linq;
using MemoryPack;
using Logic.CrashSight;
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;
}
/// <summary>
/// 推击攻击逻辑纯数据动画由UnitAttackAction.Execute负责
/// </summary>
/// <returns>true表示推击已执行false表示不满足推击条件应走普通攻击</returns>
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.GetAllDefenseValueIgnoringPositiveBonus(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;
if (!Main.UnitLogic.MoveToLogic(map, target, nextGrid, MoveType.PushMove))
{
LogSystem.LogError($"YuugiPush target move failed. self={self.Id}, target={target.Id}, from={targetGrid.Id}, to={nextGrid.Id}");
outPushed = false;
return false;
}
if (!target.IsValidOnMap(map) || !target.IsAlive() || !map.GetGridDataByUnitId(target.Id, out var targetGridAfterMove) || targetGridAfterMove.Id != nextGrid.Id)
{
LogSystem.LogError($"YuugiPush aborted after target move. self={self.Id}, target={target.Id}, expectedTargetGrid={nextGrid.Id}");
outPushed = false;
return true;
}
if (!self.IsValidOnMap(map) || !self.IsAlive())
{
LogSystem.LogError($"YuugiPush aborted: self invalid after target move. self={self.Id}, target={target.Id}");
outPushed = false;
return true;
}
if (targetGrid.RealUnit(map, out var targetGridOccupant) && targetGridOccupant.Id != self.Id)
{
LogSystem.LogError($"YuugiPush aborted: target origin grid occupied after push. self={self.Id}, target={target.Id}, grid={targetGrid.Id}, occupant={targetGridOccupant.Id}");
outPushed = false;
return true;
}
if (!Main.UnitLogic.MoveToLogic(map, self, targetGrid, MoveType.AttackMove))
{
LogSystem.LogError($"YuugiPush self move failed. self={self.Id}, target={target.Id}, to={targetGrid.Id}");
outPushed = false;
return true;
}
if (!self.IsValidOnMap(map) || !self.IsAlive() || !map.GetGridDataByUnitId(self.Id, out var selfGridAfterMove) || selfGridAfterMove.Id != targetGrid.Id)
{
LogSystem.LogError($"YuugiPush aborted after self move. self={self.Id}, target={target.Id}, expectedSelfGrid={targetGrid.Id}");
outPushed = false;
return true;
}
if (!target.IsValidOnMap(map) || !target.IsAlive() || !map.GetGridDataByUnitId(target.Id, out targetGridAfterMove) || targetGridAfterMove.Id != nextGrid.Id)
{
LogSystem.LogError($"YuugiPush aborted before damage. self={self.Id}, target={target.Id}, expectedTargetGrid={nextGrid.Id}");
outPushed = false;
return true;
}
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.CanCounterByRules(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<GridData>();
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);
}
}
}
}
}
else if (!target.IsAlive())
{
outFragmentType = FragmentType.NotMoveKill;
}
}
return true;
}
// Push 场景专用的反击判定。
// 问题背景:通用 UnitLogic.CanCounter 内部会重算 dmg1 = CalcDamage(self, target)
// push 发生后 self/target 的位置都变了,基于位置的 skill 加成可能让 dmg1 漂移;
// 如果漂移后 dmg1 >= target.HealthCanCounter 会判定"一击必杀 → 不反击"
// 但实际 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;
}
}
}