TH1/Unity/Assets/Scripts/TH1_Logic/Action/AttackAllyActionLogic.cs
2026-06-22 20:03:09 +08:00

406 lines
19 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: 2025年04月10日 星期四 11:04:44
* @Modify:
*/
using System;
using Logic.AI;
using Logic.Audio;
using Logic.CrashSight;
using Logic.Skill;
using RuntimeData;
using TH1_Anim;
using TH1_Anim.Fragments;
using TH1_Core.Events;
using TH1_Core.Managers;
using TH1_Logic.Core;
using TH1_Presentation.Sequencer.Task;
using TH1_Renderer;
using TH1Renderer;
using UnityEngine;
//这里是所有BuildAction派生子类的实现模块
namespace Logic.Action
{
public class UnitAttackAllyAction : ActionLogicBase
{
public UnitAttackAllyAction(CommonActionId id) : base(id)
{
}
protected override bool Execute(CommonActionParams actionParams)
{
//Step #0 为anim做数据准备
GridData originGrid = null;
GridData targetGrid = null;
UnitRenderer originUnitRenderer = null;
UnitRenderer targetUnitRenderer = null;
//如果不是AI预测相关的行为
if (actionParams.MapData == Main.MapData)
{
originGrid = actionParams.UnitData.Grid(Main.MapData);
targetGrid = actionParams.TargetUnitData.Grid(Main.MapData);
MapRenderer.Instance.ROUnitMap.TryGetValue(actionParams.UnitData.Id, out originUnitRenderer);
MapRenderer.Instance.ROUnitMap.TryGetValue(actionParams.TargetUnitData.Id, out targetUnitRenderer);
}
//Step #1 处理所有攻击友军的逻辑
UnitData unit1 = actionParams.UnitData;
UnitData unit2 = actionParams.TargetUnitData;
CityData city1 = unit1.City(actionParams.MapData);
MapData mapData = actionParams.MapData;
var originAliveBeforeAttackAlly = unit1.IsAlive();
var targetAliveBeforeAttackAlly = unit2.IsAlive();
SkillType animSkillData = SkillType.NONE;
bool NotProjectile = false;
bool unit1Die = false;
//========== 【新增】AttackAlly 生命周期版本 ==========
bool handledByLifecycle = false;
// 检查是否有技能支持 AttackAlly通过新生命周期
if (unit1.AttackAllyEnable(mapData, unit2))
{
// 计算治疗量
int baseHeal = unit1.AttackAllyBaseHeal(mapData, unit2);
int healAddition = unit1.AttackAllyHealAddition(mapData, unit2);
int finalHeal = baseHeal + healAddition;
if (finalHeal > 0)
{
// 获取动画技能类型第一个支持AttackAlly的技能
var isFrozen = unit1.IsFrozen();
foreach (var skill in unit1.Skills)
{
if (isFrozen && unit1.IsSkillFrozenFilter(skill)) continue;
if (skill.AttackAllyEnable(mapData, unit1, unit2))
{
animSkillData = skill.GetSkillType();
break;
}
}
// 新流程同样需要消耗所有行动点
unit1.ClearActionPoint();
// 执行治疗走新版RecoverHealth内部会触发生命周期OnAttackAllyJustBeforeHeal -> AddHealth -> OnAttackAllyAfterHeal
Main.UnitLogic.RecoverHealth(mapData, unit1, unit2, finalHeal, HealType.AttackAllyHeal);
handledByLifecycle = true;
}
else
{
var lifecycleNotProjectile = unit1.AttackAllyIsNotProjectile(mapData, unit2);
if (unit1.AttackAllyExecute(mapData, unit2, out var executeSkillData))
{
animSkillData = executeSkillData;
NotProjectile = lifecycleNotProjectile;
if (unit1.GetActionPoint(ActionPointType.Attack) > 0)
unit1.ClearActionPoint();
handledByLifecycle = true;
}
}
if (handledByLifecycle)
{
// 处理自身伤害如Koakuma的治疗代价
int selfDamage = unit1.AttackAllySelfDamage(mapData, unit2);
if (selfDamage > 0)
{
var damageInfo = Main.UnitLogic.DamageSettlement(mapData, unit1, unit1, selfDamage, DamageType.KillSelf);
if (mapData == Main.MapData)
{
unit1.Grid(mapData)?.Renderer(mapData)?.InstantUpdateGrid();
if (damageInfo.IsKill)
unit1.Renderer(mapData)?.Die();
}
}
}
}
// 如果生命周期未处理,走原有的特判逻辑(向后兼容)
if (!handledByLifecycle)
{
//======================================================
//处理Eirin的友军攻击
/*if (unit1.GetSkill(SkillType.EIRINFRENCHATTACK, out var _))
{
animSkillData = SkillType.EIRINFRENCHATTACK;
unit1.ClearActionPoint();
int recover = 5;
if (unit1.GetSkill(SkillType.EIRINFRENCHBUFF, out var _) && unit2.TreatedAsHero(actionParams.MapData,unit1))
recover = 15;
var before = unit2.Health;
Main.UnitLogic.RecoverHealth_Legacy(mapData, unit1, unit2, recover);
unit2.AddSkill_Legacy(SkillType.KAGUYAFRENCHSYNERGY, mapData,false,1,false,-1,false, SpecialAddSkillType.AddTurnLimit,0);
//如果溢出
if (unit2.Health - before < recover)
{
unit2.AddSkill_Legacy(SkillType.MOVERANGEUP,mapData,false,1,false,-1,false, SpecialAddSkillType.AddTurnLimit,0);
}
//Step #2 处理所有skill的 OnHealOther生命周期
unit1.OnHealOther(actionParams.MapData,unit2,HealType.AttackAllyHeal);
}
else*/
//处理Kaguya的友军攻击
if (unit1.GetSkill(SkillType.KAGUYAFRENCHATTACK, out var _) || unit1.GetSkill(SkillType.KAGUYAFRENCHATTACKPRO, out var _) )
{
animSkillData = SkillType.KAGUYAFRENCHATTACK;
var lv = unit1.GetSkill(SkillType.KAGUYAFRENCHATTACKPRO, out var _) ? 9 : 1;
unit1.ClearActionPoint();
unit2.AddSkill_Legacy(SkillType.KAGUYAFRENCHFOREVERBUFF, mapData,false,1,true,lv,false, SpecialAddSkillType.AddLevel,unit1.Id);
//if (unit2.GetSkill(SkillType.KAGUYAFRENCHFOREVERBUFF, out var skill))
//skill.AddLevel(mapData, unit1, unit2, lv);
//Step #2 处理所有skill的 OnHealOther生命周期
unit1.OnHealOther(actionParams.MapData,unit2,HealType.AttackAllyHeal);
}
else
//处理Patchouli的友军施法
if (unit1.GetSkill(SkillType.PATCHOULIEARTH, out var tskill))
{
animSkillData = SkillType.PATCHOULIEARTH;
var lv = tskill.Level;
int recover = lv * 5;
unit1.ClearActionPoint();
Main.UnitLogic.RecoverHealth_Legacy(mapData, unit1, unit2, recover);
tskill.ReduceLevel(mapData, unit1, lv);
unit1.HeroTask(mapData)?.OnReduceSkillLevels(mapData,SkillType.PATCHOULIEARTH,(uint)lv);
//如果同事存在金石,也要消耗掉金石头
if (unit1.GetSkill(SkillType.PATCHOULIMETAL, out var tskill2))
{
var lv2 = tskill2.Level;
tskill2.ReduceLevel(mapData, unit1, lv2);
unit1.HeroTask(mapData)?.OnReduceSkillLevels(mapData,SkillType.PATCHOULIMETAL,(uint)lv2);
}
//Step #2 处理所有skill的 OnHealOther生命周期
unit1.OnHealOther(actionParams.MapData,unit2,HealType.AttackAllyHeal);
}
// 【Koakuma友军施法 - 已迁移到新生命周期,见上方 handledByLifecycle 分支】
//else
//if (unit1.GetSkill(SkillType.KOAKUMADEVOTION, out var skill))
//{
// var t = skill as KoakumaDevotionSkill;
// animSkillData = SkillType.KOAKUMADEVOTION;
// int recover = 5;
// unit1.ClearActionPoint();
// Main.UnitLogic.RecoverHealth_Legacy(mapData, unit1, unit2, recover);
// if (t?.FirstDevotion ?? false)
// {
// t.FirstDevotion = false;
// }
// unit1.OnHealOther(actionParams.MapData,unit2,HealType.AttackAllyHeal);
//
// var grid = unit1.Grid(mapData);
// var info = Main.UnitLogic.DamageSettlement(mapData,unit1,unit1,5,DamageType.KillSelf);
// grid?.Renderer(mapData)?.InstantUpdateGrid();
// if (info.IsKill)
// unit1.Renderer(mapData)?.Die();
//}
else
//处理SuwakoHebi的合并
if (unit1.GetSkill(SkillType.SUWAKOHEBI, out var _))
{
if (!SuwakoHebiSkill.CanCombineToNextLevel(unit2)) return false;
NotProjectile = true;
//unit2.UnitFullType.UnitLevel++;
var unitFullType = unit2.UnitFullType;
var health = unit2.Health + unit1.Health;
unitFullType.UnitLevel++;
Main.UnitLogic.UnitTypeTransform(mapData,unit2,unitFullType);
unit2.Health = Mathf.Min(unit2.GetMaxHealth(),health);
unit1.Grid(actionParams.MapData, out var grid);
Main.UnitLogic.UnitDie(actionParams.MapData,unit1,0);
if(grid?.InMainSight()??false)
grid.Renderer(actionParams.MapData)?.InstantUpdateGrid();
}
else
//处理KomeijiRider献祭给KomeijiKnight原单位消失目标恢复5HP恢复1攻击行动点
if (unit1.GetSkill(SkillType.KomeijiRiderAdd, out _) && unit2.GetSkill(SkillType.KomeijiKnightAdd, out _))
{
NotProjectile = true;
unit1.Grid(mapData, out var grid);
Main.UnitLogic.DamageSettlement(mapData, unit1, unit1, unit1.Health, DamageType.KillSelf);
unit1.Renderer(mapData)?.Die();
Main.UnitLogic.UnitUnnaturalDie(mapData, unit1);
Main.UnitLogic.RecoverHealth_Legacy(mapData, unit1, unit2, 5);
unit2.AddActionPoint(ActionPointType.Attack);
unit2.Renderer(mapData)?.InstantUpdateUnit(false);
grid?.Renderer(mapData)?.InstantUpdateGrid();
unit1Die = true;
}
else
//处理拥有CorpseBuff的单位向BonePile喂食消耗层数每层回复5点
if (unit1.GetSkill(SkillType.CorpseBuff, out var corpseBuffSkill) && unit2.UnitType == UnitType.BonePile)
{
NotProjectile = true;
int layers = corpseBuffSkill.Level;
int feedHealth = layers * 5;
corpseBuffSkill.ReduceLevel(mapData, unit1, layers);
unit1.ClearActionPoint();
Main.UnitLogic.RecoverHealth_Legacy(mapData, unit1, unit2, feedHealth);
unit1.Grid(mapData, out var grid);
grid?.Renderer(mapData)?.InstantUpdateGrid();
}
else
//处理古明地非Giant单位向BonePile献祭
if (unit1.CanFeedBonePile(mapData) && unit2.UnitType == UnitType.BonePile)
{
NotProjectile = true;
int feedHealth = unit1.Health;
unit1.Grid(mapData, out var grid);
Main.UnitLogic.DamageSettlement(mapData, unit1, unit1, unit1.Health, DamageType.KillSelf);
unit1.Renderer(mapData)?.Die();
Main.UnitLogic.UnitUnnaturalDie(mapData, unit1);
Main.UnitLogic.RecoverHealth_Legacy(mapData, unit1, unit2, feedHealth);
grid?.Renderer(mapData)?.InstantUpdateGrid();
unit1Die = true;
}
//处理sanae的友军攻击
if (unit1.GetSkill(SkillType.SANAEWIND, out var _))
{
animSkillData = SkillType.SANAEWIND;
int recover = 4;
unit1.ClearActionPoint();
Main.UnitLogic.RecoverHealth_Legacy(mapData, unit1, unit2, recover);
unit1.OnHealOther(actionParams.MapData,unit2,HealType.AttackAllyHeal);
}
//========== 【新增】向后兼容分支闭合 ==========
}
//================================================
//Step #3 处理动画
var originKilledByAttackAlly = originAliveBeforeAttackAlly && (!unit1.IsAlive() || !unit1.IsValidOnMap(mapData));
var targetKilledByAttackAlly = targetAliveBeforeAttackAlly && (!unit2.IsAlive() || !unit2.IsValidOnMap(mapData));
//如果是AI预测行为return
if (actionParams.MapData != Main.MapData) return true;
//如果数据出错return
if (originGrid == null || targetGrid == null || originUnitRenderer == null || targetUnitRenderer == null)
{
if (NotProjectile)
{
originGrid?.Renderer(mapData)?.InstantUpdateGrid();
targetGrid?.Renderer(mapData)?.InstantUpdateGrid();
return true;
}
if (originKilledByAttackAlly || targetKilledByAttackAlly)
{
ReportUnitAttackAllyRendererMissingAfterKill(
actionParams,
_actionId,
originAliveBeforeAttackAlly,
targetAliveBeforeAttackAlly,
originKilledByAttackAlly,
targetKilledByAttackAlly,
handledByLifecycle,
NotProjectile,
unit1Die,
originGrid,
targetGrid,
originUnitRenderer,
targetUnitRenderer);
try
{
if (targetKilledByAttackAlly && targetUnitRenderer != null)
targetUnitRenderer.Die();
if (originKilledByAttackAlly && originUnitRenderer != null)
originUnitRenderer.Die();
}
catch (System.Exception e)
{
LogSystem.LogWarning($"UnitAttackAllyRendererMissingAfterKill cleanup failed: {e}");
}
}
return false;
}
//如果不在视野return
if (!Main.MapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(originGrid.Id) &&
!Main.MapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(targetGrid.Id)) return true;
//投射物类的动画
if (!NotProjectile)
{
var data = FragmentDataFactory.CreateAttackAllyData(FragmentType.AttackAlly, originUnitRenderer,
targetUnitRenderer, originGrid, targetGrid, animSkillData,unit1Die,city1);
var fragment = FragmentFactory.Create(FragmentType.AttackAlly,data);
PresentationManager.EnqueueTask(new FragmentSequencerTask(fragment));
_duration = fragment.Duration;
}
//非投射物动画(目前仅suwako hebi 的合并
else
{
if (!targetUnitRenderer.InstantUpdateTryDie())
targetUnitRenderer.InstantUpdateUnit(showoff: true);
if (!originUnitRenderer.InstantUpdateTryDie())
originUnitRenderer.InstantUpdateUnit(showoff: true);
targetGrid.Renderer(Main.MapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Fog));
originGrid.Renderer(Main.MapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Fog));
//因为没有走包装好的fragmenData 所以由手动重置周围单位的高亮状态
MapRenderer.Instance.UpdateAroundHighlight(Main.MapData,originGrid);
}
return true;
}
public override bool CheckCan(CommonActionParams actionParams)
{
if (actionParams.UnitData.Id == actionParams.TargetUnitData.Id) return false;
if (!actionParams.UnitData.IsAlive() || !actionParams.TargetUnitData.IsAlive()) return false;
if (!actionParams.MapData.IsLeagueUnitByUnit(actionParams.UnitData.Id, actionParams.TargetUnitData.Id)) return false;
if (!actionParams.MapData.GetPlayerDataByUnitId(actionParams.UnitData.Id, out _)) return false;
if (!actionParams.MapData.GetPlayerDataByUnitId(actionParams.TargetUnitData.Id, out _)) return false;
if (!actionParams.MapData.GetCityDataByUnitId(actionParams.UnitData.Id, out _)) return false;
if (!actionParams.MapData.GetCityDataByUnitId(actionParams.TargetUnitData.Id, out _)) return false;
if (!actionParams.MapData.GetGridDataByUnitId(actionParams.UnitData.Id, out var unitGrid)) return false;
if (!actionParams.MapData.GetGridDataByUnitId(actionParams.TargetUnitData.Id, out var targetUnitGrid)) return false;
if (actionParams.MapData.GridMap.CalcDistance(unitGrid, targetUnitGrid) >
actionParams.UnitData.GetAttackAllyRange(actionParams.MapData)) return false;
// 古明地非Giant单位可以向BonePile献祭
if (actionParams.UnitData.CanFeedBonePile(actionParams.MapData)
&& actionParams.TargetUnitData.UnitType == UnitType.BonePile)
return true;
if (!actionParams.UnitData.IsCanAttackAlly()) return false;
if (!actionParams.UnitData.IsCanAttackTargetAlly(actionParams.MapData, actionParams.TargetUnitData))
return false;
return true;
}
public override bool CheckShow(CommonActionParams actionParams,out ShowType showType)
{
showType = ShowType.None;
return false;
}
public override ActionShowState CheckShowState(CommonActionParams actionParams) { return ActionShowState.None; }
public override bool CameraControl(CommonActionParams actionParams)
{
var main = GameObject.Find("Main").GetComponent<Main>();
if(actionParams.GridData != null && main != null && MapRenderer.Instance.CameraController != null
&& actionParams.MapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(actionParams.GridData.Id))
MapRenderer.Instance.CameraController?.CameraFocusOnGrid(actionParams.GridData);
return true;
}
}
}