/* * @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
(); 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; } } }