TH1/Unity/Assets/Scripts/TH1_Logic/Skill/AllSkill/UtsuhoReadyMoveSkill.cs
2026-05-10 17:57:28 +08:00

202 lines
9.4 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月09日
* @Modify:
*/
using System.Collections.Generic;
using Logic.Pool;
using RuntimeData;
using TH1_Anim;
using TH1_Anim.Fragments;
using TH1_Core.Managers;
using TH1_Logic.Core;
using TH1_Presentation.Sequencer.Task;
using TH1Renderer;
using UnityEngine;
namespace Logic.Skill
{
public partial class UtsuhoReadyMoveSkill : SkillBase
{
public UtsuhoReadyMoveSkill()
{
IsPermanent = true;
TurnsLimit = 0;
Score = 4;
}
public override SkillType GetSkillType()
{
return SkillType.UtsuhoReadyMove;
}
// ========== ReadyMove 冲锋完整逻辑 ==========
//
// Step 1 - 确定终点:
// 沿路径从path[1]开始扫描,找到第一个挡路目标:
// - 盟友/背盟单位 或 盟友/背盟城市中心格 → 终点 = 该格前一格 (按盟友挡路处理)
// - 敌方单位 → 终点 = 该敌方所在格 (path[i]),标记 hasEnemyAtEnd
// - 没有遇到任何挡路目标 → 终点 = 路径末尾
// 截断路径为 path[0..endIndex]
//
// Step 2 - 溅射伤害:
// 从path[1]到终点对每个路径格子周围1格范围内的敌方单位造成一次溅射伤害
// 每个敌方单位最多被溅射一次 (HashSet去重)
//
// Step 3 - DelayAttack:
// 如果终点有活着的敌方单位可能已被溅射杀死对其造成一次常规DelayAttack伤害
// - 击杀 → 终点不变,留在目标格子
// - 未击杀 → 终点回退到前一格 (path[^2]),在路径末尾追加该格
//
// Step 4 - 处理终点占位:
// 如果最终移动终点上有任何单位骨堆等将其PassiveMoveAway
//
// Step 5 - 执行移动:
// 播放移动动画 → MoveToLogic → 更新视野 → 刷新迷雾
//
public override void OnTurnStart(IdentifierBase self, MapData mapData)
{
var selfUnit = self as UnitData;
if (selfUnit == null) return;
var playerData = selfUnit.Player(mapData);
if (playerData == null) return;
if (!selfUnit.GetSkill(SkillType.UtsuhoDelayAct, out var skill)) return;
var delayActSkill = skill as UtsuhoDelayActSkill;
if (delayActSkill == null) return;
// 拷贝path避免修改原始序列化数据导致主客机不同步
var path = new List<Vector2Int>(delayActSkill.Path);
if (path == null || path.Count == 0) return;
// 如果单位当前位置和路径起点不一致例如被PassiveMove推走过取消本次冲锋
var currentGrid = selfUnit.Grid(mapData);
if (currentGrid == null || currentGrid.Pos.V2() != path[0])
{
return;
}
// === Step 1: 确定终点 ===
int endIndex = path.Count - 1;
bool hasEnemyAtEnd = false;
bool hasAllyAtEnd = false;
for (int i = 1; i < path.Count; i++)
{
if (!mapData.GridMap.GetGridDataByV2(path[i], out var grid)) continue;
// 盟友/刚背盟的城心即使无驻军也算挡路,否则终点落在城心会触发自动宣战
bool isLeagueCity = mapData.IsLeagueOrJustBreakCityCenter(selfUnit.Id, grid);
bool hasOtherUnit = grid.RealUnit(mapData, out var unit) && unit.Id != selfUnit.Id;
if (!hasOtherUnit && !isLeagueCity) continue;
bool isAlly = isLeagueCity || (hasOtherUnit && mapData.IsLeagueOrJustBreakByUnit(selfUnit.Id, unit.Id));
endIndex = i;
if (isAlly) hasAllyAtEnd = true;
else hasEnemyAtEnd = true;
break;
}
// 截断路径到 [0..endIndex]
if (endIndex + 1 < path.Count)
path.RemoveRange(endIndex + 1, path.Count - (endIndex + 1));
//如果友方在终点
if (hasAllyAtEnd)
{
path.Add(path[endIndex - 1]);
endIndex--;
}
// === Step 2: 溅射伤害 ===
bool hasRadiation = selfUnit.GetSkill(SkillType.UtsuhoRadiation, out _);
if (hasRadiation)
{
using var pooledSplashedUnits = THCollectionPool.GetHashSetHandle<uint>(out var splashedUnits);
for (int i = 1; i < path.Count; i++)
{
if (!mapData.GridMap.GetGridDataByV2(path[i], out var pathGrid)) continue;
var aroundBuf = RentAroundBuf();
mapData.GridMap.GetAroundGridData(1, 1, pathGrid, aroundBuf);
foreach (var aroundGrid in aroundBuf)
{
if (!aroundGrid.RealUnit(mapData, out var splashTarget)) continue;
if (splashTarget.Id == selfUnit.Id) continue;
if (mapData.IsLeagueOrJustBreakByUnit(selfUnit.Id, splashTarget.Id)) continue;
if (!splashedUnits.Add(splashTarget.Id)) continue;
var splashDmg = Table.Instance.CalcDamage(mapData, selfUnit, splashTarget, damagePara: 0.5f);
var splashGrid = splashTarget.Grid(mapData);
Main.UnitLogic.DamageSettlement(mapData, selfUnit, splashTarget, splashDmg, DamageType.Splash);
if (splashGrid != null && splashGrid.InMainSight())
{
splashGrid.Renderer(mapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Damage, splashDmg));
splashGrid.Renderer(mapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Hurt));
splashTarget.Renderer(mapData)?.InstantUpdateUnit(false);
if (!splashTarget.IsAlive())
splashGrid.Renderer(mapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Die));
splashTarget.Renderer(mapData)?.InstantUpdateTryDie();
splashGrid.Renderer(mapData)?.InstantUpdateGrid();
}
}
ReturnAroundBuf();
}
}
// === Step 3: DelayAttack ===
// 终点敌人可能已被溅射杀死,需要再次检查是否仍然活着
if (hasEnemyAtEnd &&
mapData.GridMap.GetGridDataByV2(path[^1], out var endGrid) &&
endGrid.RealUnit(mapData, out var endEnemy) &&
!mapData.IsLeagueOrJustBreakByUnit(selfUnit.Id, endEnemy.Id) &&
endEnemy.IsAlive())
{
var dmg = Table.Instance.CalcDamage(mapData, selfUnit, endEnemy);
var targetGrid = endEnemy.Grid(mapData);
Main.UnitLogic.DamageSettlement(mapData, selfUnit, endEnemy, dmg, DamageType.DelayAttack);
if (targetGrid != null && targetGrid.InMainSight())
{
targetGrid.Renderer(mapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Damage, dmg));
targetGrid.Renderer(mapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Hurt));
endEnemy.Renderer(mapData)?.InstantUpdateUnit(false);
if (!endEnemy.IsAlive())
targetGrid.Renderer(mapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Die));
endEnemy.Renderer(mapData)?.InstantUpdateTryDie();
targetGrid.Renderer(mapData)?.InstantUpdateGrid();
}
// 未击杀:终点回退到前一格
if (endEnemy.IsAlive())
path.Add(path[^2]);
}
// === Step 4: 处理终点占位 ===
if (!mapData.GridMap.GetGridDataByV2(path[^1], out var finalGrid)) return;
if (finalGrid.RealUnit(mapData, out var finalOccupant) && finalOccupant.Id != selfUnit.Id)
Main.UnitLogic.PassiveMoveAway(mapData, finalOccupant);
// === Step 5: 执行移动 ===
var originGrid = selfUnit.Grid(mapData);
if (mapData == Main.MapData && originGrid != null)
{
bool inSight = Main.MapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(originGrid.Id)
|| Main.MapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(finalGrid.Id);
if (inSight && MapRenderer.Instance.ROUnitMap.TryGetValue(selfUnit.Id, out var unitRenderer))
{
var data = FragmentDataFactory.Create(FragmentType.Move, unitRenderer, originGrid, finalGrid, path);
var fragment = FragmentFactory.Create(FragmentType.Move, data);
PresentationManager.EnqueueTask(new FragmentSequencerTask(fragment));
}
}
Main.UnitLogic.MoveToLogic(mapData, selfUnit, finalGrid, MoveType.ActiveMove, path);
// 沿路径更新视野FogDisappear fragments会排在Move动画之后自动处理
foreach (var pathNode in path)
playerData.Sight.UpdateSightByPath(selfUnit, playerData, pathNode, mapData);
}
}
}