/* * @Author: 白哉 * @Description: 技能类 * @Date: 2025年04月22日 星期二 17:04:44 * @Modify: */ using System.Collections.Generic; using Logic.Action; using MemoryPack; using RuntimeData; using UnityEngine; //SkillType 只能新增, 不能删除! public enum SkillType { NONE = 0, PEACE = 1, DASH = 2, ESCAPE = 3, PERSIST = 4, SPLASH = 5, SNEAK = 6, STIFF = 7, FORTIFY = 8, HEAL = 9, CONVERT = 10, CARRY = 11, SCOUT = 12, STATIC = 13, // deprecated compatibility id; do not add to unit rows HIDE = 14, // no use CREEP = 15, INFILTRATE = 16, BOOST = 17, // no use EXPLODE = 18, // no use STOMP = 19, ATTACKUP = 20, SPEEDUP = 21, FORESTDEFENSE = 22, WATERDEFENSE = 23, OCEANDEFENSE = 24, MOUNTAINDEFENSE = 25, MOUNTAINMOVE = 26, WATERMOVE = 27, OCEANMOVE = 28, SURPRISE = 29, POISON = 30, // no use POISONED = 31, UNIQUE = 32, SWAP = 33, AUTOHEAL = 34, CITYTRANSPORT = 35, ALLYCOUNTER = 36, POWERUP = 37, ALLYTRANSPORT = 38, QUARTET = 39, TAICHI = 40, POORHEALTH = 41, ROYALFLAMES = 42, VAMPIRE = 43, SUPERDASH = 44, PHOENIX = 45, PHOENIXEGG = 46, ILLUSION = 47, LUCK = 48, // Sanae Divine Pending CRITICAL = 49, MOONPRINCESS = 50, // no use ETERNITY = 51, // no use GALAXYARROW = 52, WINDGOD = 53, WINDPRIESTESS = 54, // no use MOUNTAINGOD = 55, CURSEGOD = 56, // no use NUCLEAR = 57, // no use NUCLEARFUSION = 58, // no use CATCART = 59, // no use RECYCLE = 60, // no use THIRDEYE = 61, // no use SUPERHIDE = 62, // no use // 文明技能 = , CommonPlayerSkillIndustry = 63, RemiliaForcesSkill1 = 64, // no use EgyptCivSkill1 = 65, // no use // 2025.8.13新增 = , RENGESYOU = 66, // no use RENGESYOUCONTRO = 67, // no use LAEVATAIN = 68, // no use LAEVATAINPREY = 69, // no use SATSUJINKI = 70, PHILOSTONE = 71, ROYALFLAMESPRO = 72, VAMPIREPRO = 73, DUO = 74, TRIO = 75, //2025.9.5 永远亭新增 = , KaguyaFrenchNapoleonicCode = 76, KaguyaFrenchWarriorSynergy = 77, KaguyaFrenchBambooMove = 78, KaguyaFrenchCatapultSynergy = 79, BAMBOOMOVE = 80, KAGUYAFRENCHSYNERGY = 81, KAGUYAFRENCHSYNERGYDEBUFF = 82, NOPOPULATION = 83, // 2025.9.12新增Demo V1.4 = , TEWIFRENCHSIGHT = 84, TEWIFRENCHDIE = 85, TEWIFRENCHATTACK = 86, TEWIFRENCHKILL = 87, TEWIFRENCHBUFF = 88, EIRINFRENCHKILL = 89, EIRINFRENCHATTACK = 90, EIRINFRENCHBUFF = 91, MOKOUFRENCHEGG = 92, MOKOUFRENCHREVIVE = 93, MOVERANGEUP = 94, ATTACKRANGEUP = 95, ATTACKAFTERKILL = 96, MOKOUFRENCHBOOM = 97, KAGUYAFRENCHFOREVERBUFF = 98, KAGUYAFRENCHATTACK = 99, KAGUYAFRENCHAROUND = 100, KAGUYAFRENCHATTACKPRO = 101, REISENILLUSION = 102, REISENFRENCHKILL = 103, REISENFRENCHATTAK = 104, REISENILLUSIONPRO = 105, REISENFRENCHKILLPRO = 106, // no use FLY = 107, // no use FORESTSTARTDASH = 108, LANDONLY = 109, LANDANDWATER = 110, // 10.12新增 = , SAKUYAFLY = 111, SAKUYAGUARD = 112, SAKUYAKILL = 113, SAKUYAFLYPRO = 114, PATCHOULIMOVE = 115, PATCHOULIREST = 116, PATCHOULISTONE = 117, PATCHOULISTONEPRO = 118, PATCHOULIEARTH = 119, PATCHOULIWATER = 120, PATCHOULIMETAL = 121, PATCHOULIMOVEPRO = 122, PATCHOULIWOOD = 123, PATCHOULIFIRE = 124, MEILINGREST = 125, MEILINGCOUNTER = 126, MEILINGDUEL = 127, CANTMOVE = 128, REMILIAHELP = 129, REMILIAHUNTER = 130, // no use REMILIAATTACK = 131, // no use REMILIABUFF = 132, FLANDREATTACK = 133, FLANDREKILL = 134, FLANDREBUFF = 135, RemiliaEgyptianEmpireKill = 136, //红雾领地下所有单位的实时吸血效果 = , ScarletMistRealTimeVampire = 137, REDMISTDEFENSE = 138, REMILIAHELPPRO = 139, KOAKUMADEVOTION = 140, KOAKUMAHERO = 141, ScarletMistRealTimeVampireDebuff = 142, // no use ScarletKoakuma = 143, REMILIABUFF2 = 144, REMILIABUFF3 = 145, REMILIAABSORB = 146, // 11.13新增 = , MORIYABUFF = 147, KANAKOTECH = 148, KANAKOBATTLEFIELD = 149, KANAKOMOUNTAIN = 150, KANAKOMOUNTAINATTACK = 151, KANAKOMOUNTAINBUFF = 152, KANAKOSITTING = 153, KANAKOWIND = 154, SUWAKOMOVE = 155, SUWAKOATTACK = 156, SUWAKOATTACKPRO = 157, SUWAKOATTACKALLY = 158, SUWAKOHEBIATTACK = 159, SANAEMOVE = 160, SANAEWIND = 161, SANAEWINDX = 162, SANAEDIVINE = 163, SANAENINE = 164, TENGUBUFF = 165, PATHSTOMP = 166, AYAMOVEAGAIN = 167, AYAMOVEAGAINBUFF = 168, MOMIJISIGHT = 169, MOMIJIHUNT = 170, MOMIJIPREY = 171, MOMIJIHUNTER = 172, MOMIJIBUFF = 173, MOMIJIHUNTERATTACK = 174, SANAENINECONTINUE = 175, // no use GRIDSANAENINECONTINUEDAMAGE = 176, GRIDMOMIJIPREY = 177, GRIDMOUNTAIN = 178, JUNKEROFFICER = 179, MORIYAKNIGHTMOVE = 180, MORIYAROAD = 181, OFFICER = 182, KANAKOWINDPRO = 183, KANAKOWAR = 184, KANAKOWARPRO = 185, SUWAKOCOMBINE = 186, SUWAKOSPLIT = 187, SUWAKOFULLMAP = 188, SUWAKOHEBICOMBINE = 189, SUWAKOHEBISPLIT = 190, SUWAKOHEBI = 191, KANAKOBATTLEFIELDPRO = 192, AYABUFF = 193, DIVINE_F2_DEFENSE = 194, DIVINE_F2_RESIST= 195, DIVINE_F3_MOVE= 196, DIVINE_F3_RESIST= 197, DIVINE_F4_KILL= 198, DIVINE_F4_ATK= 199, DIVINE_E2_ATK = 200, DIVINE_E2_MOVE = 201, DIVINE_E3_HP = 202, DIVINE_E3_COUNTER = 203, DIVINE_E4_KILL = 204, DIVINE_E4_DEFENSE = 205, TREATASHERO = 206, //-----v0.6.6----- ESCAPEPRO = 207, SCOUTPRO = 208, MOMIJIKILL = 209, // 3.3 新增 KomeijiFear = 210, SatoriSee = 211, SatoriBan = 212, SkillBan = 213, SkillBanBoom = 214, Undead = 215, BonePile = 216, CorpseBuff = 217, HideState = 218, CanHide = 219, KoishiRespawn = 220, KoishiAuto = 221, KoishiAutoMove = 222, BoneSacrifice = 223, FearMaker = 224, KoishiUndead = 225, KoishiDeathFear = 226, RinCorpseCollet = 227, RinFire = 228, UtsuhoDelayAct = 229, UtsuhoReadyMove = 230, UtsuhoRadiation = 231, GridRadiation = 232, UtsuhoBoneMaker = 233, UtsuhoReadyMoveSuper = 234, YuugiPush = 235, YuugiDash = 236, YuugiDashPro = 237, BoneAttackUp = 238, YuugiMove = 239, YuugiMovePlus = 240, SatoriAttack = 241, SatoriAttackBoom = 242, KomeijiFearImmune = 243, MEILINGATTACKUP = 244, KomeijiRiderKill = 245, KomeijiRiderTrans = 246, KomeijiKnightKill = 247, DieBonePile = 248, KomeijiRiderAdd = 249, KomeijiFearSplash = 250, UtsuhoBase = 251, MahaCorpseBuff = 252, KomeijiKnightAdd = 253, // 妹红占位技能:凤翼天翔 MokouFrenchEnhance = 254, KaguyaFrenchCrescentMoon = 255, KaguyaFrenchNewMoon = 256, // 蓬莱山法国狼技能 HouraisanFrenchWolfStart = 257, HouraisanFrenchWolfMoon = 258, EirinFrenchSuperAttack = 259, EirinFrenchOverHeal = 260, HouraisanFrenchFakeMoon = 261, AttackGetAttackPoint = 262, AYADAMAGEDEBUFF = 263, SAKUYATIRED = 264, CITYSTOLEN = 265, // 深蓝:纯视觉技能,让单位贴图按时间循环变色,没有任何数值/逻辑效果 Shenlan = 266, AssaultFortress = 267, QueenTerritoryMove = 268, KingCoin = 269, SakuyaEnhanced = 270, FearMakerPro = 271, CityBuildingUsageMarker = 272, MokouFrenchMoveKill = 273, ReimuHakureiProtectionCaster = 274, HakureiProtection = 275, ReimuExterminationAttack = 276, ReimuExtermination = 277, ReimuExterminationPulse = 278, ReimuRandomOfuda = 279, ReimuOfudaAlmsOnDamaged = 280, ReimuOfudaAlmsOnAttack = 281, ReimuOfudaRaidDouble = 282, ReimuOfudaProtectionOffering = 283, ReimuOfudaEnemyAttackTax = 284, ReimuOfudaEnemyDamagedTax = 285, ReimuOfudaKillBounty = 286, ReimuOfudaExterminationOffering = 287, SumirekoOccultOrbOwner = 288, SumirekoNorwayOrbSwapMoveAttack = 289, SumirekoDenmarkOrbSwapAttackDefense = 290, SumirekoEnglandOrbDamageProxy = 291, SumirekoOrbSwapMaxValue = 292, KasenBeastGuideOwner = 293, KasenBeastGuideAura = 294, KasenOniForm = 295, AunnPetrifiedState = 296, AunnTwinBody = 297, AunnSharedHealth = 298, AunnTwinOperable = 299, SuikaMiniSpawnAfterMove = 300, SuikaMiniStack = 301, SuikaDamageHalveDropMini = 302, SuikaBigForm = 303, SuikaGiantForm = 304, SuikaFallingSplash = 305, SuikaMiniUnitMarker = 306, ReimuFantasyNature = 307, HakureiRoundShieldWall = 308, HakureiKarviEmbark = 309, ValhallaOath = 310, //补充之前的bug问题 Max = 999, } namespace Logic.Skill { public enum SpecialAddSkillType { Normal, // 普通添加,如果已经有了这个技能了就不添加了 Force, // 强制添加,如果已经有了这个技能了就覆盖掉原来的 AddTurnLimit, // 如果已经有了这个技能了,就增加回合限制,否则正常添加 AddLevel,// 如果已经有了这个技能了,就增加叠层数 } public struct SkillOverrideInfo { public bool IsPermanent; public int TurnLimit; public bool IsLevel; public int Level; public bool AutoDisappear; public uint OriginId; } public enum SkillDamageEvent { ActiveAttackBeforeStarted, ActiveAttackFinished, } public interface ISkill { public SkillType GetSkillType(); public bool IsAllSkillBan(); public bool ReservedOnTransform(UnitData self, UnitFullType fullType); public bool ReservedOnTransformBoat(UnitData self, UnitFullType fullType); public bool ReservedOnTransformUpgrade(UnitData self, UnitFullType fullType); public bool ReservedOnTransformFromBoat(UnitData self, UnitFullType fullType); public void NewSkillOnTransform(List oldSkills); public void OnSkillOverride(MapData mapData, uint originId, uint selfId); public void OnSkillSpecialOverride(MapData mapData, uint originId, SkillOverrideInfo overrideInfo); #region [ ---------------------------------------------------------------- 生命周期--------------------------------------------------------] //刷新所有点数 public void OnRefresh(); // 事件触发生命周期 (事件未发生) public void BeforeMove(UnitData self, GridData grid, MapData mapData, MoveType moveType, List path = null); // 事件触发生命周期 (事件已发生) public void OnMove(UnitData self, GridData grid, MapData mapData, MoveType moveType, List path = null); public void OnBeInteractTarget(UnitData origin, UnitData self, GridData grid, MapData mapData); public bool ActiveMoveFailed(UnitData self, GridData grid, MapData mapData, MoveType moveType, List path = null); // 伤害结算前自身承受阶段 public void BeforeDamagedSupportStage(MapData mapData, SettlementInfo info); // 伤害结算前自身转移阶段 public void BeforeDamagedTransformStage(MapData mapData, SettlementInfo info); // 对他人伤害结算前 public void BeforeDamageOther(MapData mapData, SettlementInfo info); // 伤害结算时 public void OnDamaged(MapData mapData, SettlementInfo info); // 消失前 public void BeforeDisappear(MapData mapData, UnitData self); // 对他人伤害结算时 public void OnDamageOther(MapData mapData, SettlementInfo info); public void AfterDamageOther(MapData mapData, SettlementInfo info); public void OnHealOther(MapData mapData, UnitData origin,UnitData target, HealType healType); public int BeforeHealSelf(MapData mapData, UnitData self, UnitData origin, HealType healType, int recover); // 主动攻击之前 public void BeforeActiveAttackOther(MapData mapData, UnitData origin, UnitData target,out int addDmg); // 主动攻击之后 public void AfterActiveAttackOther(MapData mapData, AttackInfo attackInfo); public void AfterActiveAction(MapData mapData, UnitData origin, UnitData target); public void AfterActiveAttacked(MapData mapData, AttackInfo attackInfo); // 事件触发生命周期 (时刻) public void OnTurnStart(IdentifierBase self, MapData mapData); //需要一个afterturnstart周期,不然有些技能在turnstart会影响其他技能的存在(比如新增一个持续0回合技能,那么马上就被刷掉了) public void OnAfterTurnStart(IdentifierBase self, MapData mapData); public void OnTurnEnd(IdentifierBase self, MapData mapData); public void OnFinished(IdentifierBase self, MapData mapData); // 全局技能 // 当有单位受伤结算前 public void BeforeUnitDamaged(UnitData self, MapData mapData, SettlementInfo info); // 当有单位受伤结算时 public void OnUnitDamaged(UnitData self, MapData mapData, SettlementInfo info); // 当有单位移动时 public void OnAnyUnitMove(MapData map, IdentifierBase identifier, UnitData moveUnit, GridData target, MoveType moveType); // 当有单位死亡时 public void OnAnyUnitDie(MapData map, UnitData self, UnitData dieUnit); // 当有单位创建时 public void OnSelfCreated(MapData map, UnitData self); public void OnAnyUnitCreate(MapData map, IdentifierBase identifier, UnitData newUnit); // 当有行为执行后 public void OnActionExecuted(ActionLogicBase logic, CommonActionParams param, UnitData self); #endregion #region [ ---------------------------------------------------------------- 判断属性--------------------------------------------------------] // 是否为隐身状态 public bool IsHideState(); // 判断是不是Officer public bool IsOfficer(); // 获取Officer的额外血量 public int GetOfficerHealth(); // 属性变化 // 是否能攻击所有人 public bool CanAttackAll(UnitData self, MapData mapData); // 是否限制自身移动 public bool IsLimitSelfMove(UnitData self, MapData mapData); // 是否限制自身攻击 public bool IsLimitSelfAttack(UnitData self, MapData mapData); // 是否限制敌方反击 public bool IsLimitTargetCounterAttack(UnitData self, MapData mapData); // 是否限制自身反击 public bool IsLimitSelfCounterAttack(UnitData self, MapData mapData); // 是否无视敌人控制区 public bool IsIgnoreControlArea(UnitData self, MapData mapData); // 是否限制自身经验增长 public bool IsLimitSelfExp(UnitData self, MapData mapData); // 是否无视树林和山脉 public bool IsIgnoreWoodAndMountains(UnitData self, MapData mapData); // 是否能在指定海陆层移动 public bool IsCanMoveOnTerrain(UnitData self, MapData mapData, TerrainType terrainType); public bool TryGetLandToBoatTarget(UnitData self, MapData mapData, GridData targetGrid, out UnitFullType targetFullType); // 是否能在指定地形层移动 public bool IsCanMoveOnFeature(UnitData self, MapData mapData, TerrainFeature featureType); // 是否无视地形层移动减损 public bool IsIgnoreMoveLoss(UnitData self, MapData mapData, TerrainFeature terrainType); // 是否无视植被层移动减损 public bool IsIgnoreMoveLoss(UnitData self, MapData mapData, Vegetation terrainType); // 是否无视森林(Trees 植被)带来的移动减损 —— 用于 BAMBOOMOVE / CREEP 等"林中行走如平地"的技能 public bool IsIgnoreForestMoveDebuff(UnitData self, MapData mapData); // 是否具备同类数量限制 public bool IsLimitCount(UnitData self, MapData mapData, out int count); // 是否能移动到我方的无人城市 public bool IsCanMoveToNoUnitSelfCity(UnitData self, MapData mapData); // 是否能移动到我方的英雄附近 public bool IsCanMoveGiantNearbyGrid(UnitData self, MapData mapData); // 是否能移动到 // 能否被杀死 public bool IsCanBeKill(UnitData self, MapData mapData); // 修改生命值上限 public bool IsSetMaxHealth(UnitData self, out int maxHealth); // 能否被伤害 public bool IsLimitDamaged(UnitData self, MapData mapData, int dmg); // 能否制造矿山 public bool IsCanBuildMine(UnitData self, MapData mapData); // 是否隐身 public bool IsInvisible(UnitData self, MapData mapData); // 是否限制只能在我方领土移动 public bool IsLimitMoveToSelfTerrain(UnitData self, MapData mapData); // 是否限制行动点 public bool IsLimitActionPoint(UnitData self); // 判断能否立刻成为Officer public bool IsCanBeOfficer(); // 判断是否是Officer的苗子 public bool IsPrepareOfficer(); // 判断是否能攻击Ground public bool IsCanAttackTargetGrid(MapData map,UnitData self, GridData target); //判断是否能攻击友军 public bool IsCanAttackAlly(); //判断是否能攻击指定单位(SuwakoHebi 专用 public bool IsCanAttackTargetAlly(MapData map, UnitData self, UnitData target); //判断是否能攻击指定敌方单位(用于细粒度过滤,如 INFILTRATE 只能打城心上的敌人) public bool IsCanAttackTargetUnit(MapData map, UnitData self, UnitData target); //判断是否处在载具上 public bool IsOnCarry(); //判断是否具有传送属性 public bool IsCanTransport(); //判断是否视为英雄单位 public bool IsTreatAsHero(MapData map, UnitData self,UnitData target, out GiantType giantType); #endregion #region [------------------------------------------------------------------------------获取状态值------------------------------------------------------------------------------] //当前技能是否被ban public bool IsFrozen(UnitData self); // 值变化 // 获取额外视野(视野增加) public int GetExtraSight(UnitData self, MapData mapData); // 获取暴击率 public float GetCriticalHitRate(UnitData self, MapData mapData); public float GetAttackAdditionParam(MapData mapData, UnitData self, UnitData target = null); public float GetAttackMultiplicationParam(MapData mapData, UnitData self, UnitData target = null); public float GetDefenseAdditionParam(MapData mapData, UnitData self, UnitData target = null); public float GetDefenseMultiplicationParam(MapData mapData, UnitData self, UnitData target = null); // 获得额外移动力 public int GetExtraMoveRange(MapData mapData,UnitData self); // 获得对应格子的额外移动范围 //public int GetExtraMoveRange(UnitData self, GridData grid); // 获得固定移动范围 public bool GetFinalMoveRange(UnitData self, out int fixedRange); // 获得额外攻击范围 public int GetExtraAttackRange(MapData map,UnitData self); // 获取特定技能加成 public int GetSpecialSkillBonus(MapData mapData, UnitData self, SkillType skillType); public int GetKillCount(); //获取抵达某格所需要的movecostinfo下限,目前仅用于momijiHUnter,也就是说moveinfo>该值才 public float GetGridMoveFloor(MapData mapData, UnitData origin,GridData target); //获取当前skill应该有的背景颜色 //public Color GetBGColor(); // 是否无视ZOC public bool IsIgnoreZOC(); // 是否具备ZOC控制能力 public bool HasZOC(); #endregion #region [ -------------------------------------------- 操作修改属性 -----------------------------------------] public void SetOfficer(); #endregion } [MemoryPackable] public abstract partial class SkillBase : ISkill { // 所有技能共享的临时集合池(技能逻辑顺序执行,不并发,支持嵌套调用) private static List> _aroundBufPool = new(); private static int _poolTop = -1; /// /// 从池中租用 List,自动清空 /// protected static List RentAroundBuf() { _poolTop++; if (_poolTop >= _aroundBufPool.Count) { _aroundBufPool.Add(new List(8)); } var buf = _aroundBufPool[_poolTop]; buf.Clear(); return buf; } /// /// 归还 List 到池中 /// protected static void ReturnAroundBuf() { _poolTop--; if (_poolTop < -1) _poolTop = -1; } [MemoryPackInclude] protected bool IsPermanent; //是否叠层 [MemoryPackInclude] protected bool IsLevelSkill; [MemoryPackInclude] protected uint OriginId; [MemoryPackInclude] protected uint Turns; [MemoryPackInclude] protected uint TurnsLimit; [MemoryPackInclude] protected float Score; [MemoryPackInclude] protected int _level; [MemoryPackInclude] protected int _levelLimit; //如果是叠层且0层的时候会不会自动消失 [MemoryPackInclude] protected bool _autoDisappear; [MemoryPackInclude] protected SkillPriority _skillPriority; public virtual int Level => _level; public bool HasLevel => IsLevelSkill; public bool HasTimeLimit => !IsPermanent; public int LastTime => (int)TurnsLimit - (int)Turns; public bool AutoDisappear => _autoDisappear; public bool NoShow => _autoDisappear && _level == 0; public bool ShowSkillLevel => IsLevelSkill && !NoShow; public bool ShowSkill => !IsLevelSkill || ShowSkillLevel; public SkillPriority SkillPriority => _skillPriority; [MemoryPackConstructor] public SkillBase() { IsLevelSkill = false; Turns = 0; _level = 0; _autoDisappear = true; } public abstract SkillType GetSkillType(); public virtual void GetSkillCopy(SkillBase skill) { skill.IsPermanent = IsPermanent; skill.IsLevelSkill = IsLevelSkill; skill.OriginId = OriginId; skill.Turns = Turns; skill.TurnsLimit = TurnsLimit; skill._level = _level; skill._levelLimit = _levelLimit; skill._autoDisappear = _autoDisappear; } public virtual void DeepCopy(SkillBase copySkill) { IsPermanent = copySkill.IsPermanent; IsLevelSkill = copySkill.IsLevelSkill; OriginId = copySkill.OriginId; Turns = copySkill.Turns; TurnsLimit = copySkill.TurnsLimit; _level = copySkill._level; _levelLimit = copySkill._levelLimit; _autoDisappear = copySkill._autoDisappear; } #region [----------------------------------------- 生命周期 -----------------------------------------] public void SetSkillPriority(SkillPriority priority) { _skillPriority = priority; } public virtual bool IsAllSkillBan() { return false; } public virtual bool ReservedOnTransform(UnitData self, UnitFullType fullType) { if (self == null || Table.Instance?.UnitTypeDataAssets == null) return false; Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(self.UnitFullType, out var originInfo); Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(fullType, out var targetInfo); if (originInfo?.Skills == null || targetInfo?.Skills == null) return false; bool originIsBoat = originInfo.Skills.Contains(SkillType.CARRY); bool targetIsBoat = targetInfo.Skills.Contains(SkillType.CARRY); if (originIsBoat == targetIsBoat) return ReservedOnTransformUpgrade(self, fullType); if (originIsBoat) return ReservedOnTransformFromBoat(self, fullType); return ReservedOnTransformBoat(self, fullType); } public virtual bool ReservedOnTransformBoat(UnitData self, UnitFullType fullType) { //如果不是永久的skill,继承 if (!IsPermanent) return true; if (Table.Instance?.SkillDataAssets != null && Table.Instance.SkillDataAssets.GetSkillInfo(GetSkillType(), out var skillInfo)) { //如果是buff或者debuff 通常也是继承的 if (skillInfo.SkillViewType is SkillViewType.Positive or SkillViewType.Negative) return true; return skillInfo.ReserveOnCarry; } return false; } public virtual bool ReservedOnTransformUpgrade(UnitData self, UnitFullType fullType) { //如果不是永久的skill,继承 if (!IsPermanent) return true; if (Table.Instance?.SkillDataAssets != null && Table.Instance.SkillDataAssets.GetSkillInfo(GetSkillType(), out var skillInfo)) { //如果是buff或者debuff 通常也是继承的 if (skillInfo.SkillViewType is SkillViewType.Positive or SkillViewType.Negative) return true; bool isGiantUpgrade = fullType.GiantType != GiantType.None; return isGiantUpgrade ? skillInfo.ReserveGiantUpgrade : skillInfo.ReserveCommonTransform; } return true; } public virtual bool ReservedOnTransformFromBoat(UnitData self, UnitFullType fullType) { //如果不是永久的skill,继承 if (!IsPermanent) return true; if (Table.Instance?.SkillDataAssets != null && Table.Instance.SkillDataAssets.GetSkillInfo(GetSkillType(), out var skillInfo)) { //如果是buff或者debuff 通常也是继承的 if (skillInfo.SkillViewType is SkillViewType.Positive or SkillViewType.Negative) return true; return skillInfo.ReserveLeaveCarry; } return false; } public virtual void NewSkillOnTransform(List oldSkills) { } public virtual void OnSkillOverride(MapData mapData, uint originId, uint selfId) { mapData.UnitMap.GetUnitDataByUnitId(originId, out var origin); mapData.UnitMap.GetUnitDataByUnitId(selfId, out var self); if (origin == null) return; // [Fix 2026/05/10] HeroTask计数丢失修复 // 场景:在击杀目标的那一次伤害结算中,先调用 UnitDie -> SetUnitDataDie -> RemoveUnitData, // 将 target 从 UnitMap 移除;之后 OnDamageOther 才触发,进入 AddOrOverrideSkill。 // 如果 target 身上"原本已经"有该技能,会走 OnSkillOverride 路径, // 此时 selfId 已查不到 self(self == null),原实现直接 return, // 导致 origin 的 HeroTask 漏计这一层(典型表现:Satori 击杀目标那一击的 KomeijiFear 不计数)。 // 修复:self 已死亡时,AddLevel 因依赖 self 不能调用,但仍按 +1 通知 origin 的 HeroTask, // 与"目标存活时本就 +1 计数"的语义对齐,避免击杀帧丢计数。 // 影响范围:仅"target 死亡 + 该技能在 target 身上已存在"两条件同时满足时生效; // 其他路径(首次施加、显式 AddLevel(...,N)、目标存活)行为不变。 // 回退方法:把下面的 if (self == null) {...} 块整段删除,把 if (origin == null) return; 改回 // if (origin == null || self == null) return; 即可恢复原逻辑。 if (self == null) { origin.HeroTask(mapData)?.OnAddSkillLevels(mapData, GetSkillType(), 1); return; } if (IsLevelSkill) AddLevel(mapData, origin, self, 1); // AddLevel内部已调用OnAddSkillLevels else origin.HeroTask(mapData)?.OnAddSkillLevels(mapData, GetSkillType(), 1); } public virtual void OnSkillSpecialOverride(MapData mapData, uint originId, SkillOverrideInfo overrideInfo) { } public virtual float GetScore() { return Score; } public virtual void BeforeTurnStart() { Turns++; } // 冻结不影响技能结束 public virtual bool IsFinished() { return (!IsPermanent && TurnsLimit < Turns) || (IsLevelSkill && _autoDisappear && _level == 0); } public virtual void SetPermanent(bool isPermanent) { IsPermanent = isPermanent; } public virtual void SetIsLevel(bool isLevel) { IsLevelSkill = isLevel; } public virtual void SetAutoDisappear(bool isAutoDisappear) { _autoDisappear = isAutoDisappear; } public SkillBase GetCopySkill() { var skill = SkillFactory.GetSkillBySkillType(GetSkillType()); GetSkillCopy(skill); return skill; } public virtual void SetTurnsLimit(uint time) { IsPermanent = false; TurnsLimit = time; Turns = 0; } public virtual void AddTurnLimit(uint time) { TurnsLimit += time; } public virtual void AddLevel(MapData map, UnitData origin, UnitData self, int add) { _level += add; if (_level > _levelLimit) _level = _levelLimit; if (origin == null) return; if (!map.GetPlayerDataByUnitId(origin.Id, out var player)) return; origin.HeroTask(map)?.OnAddSkillLevels(map, GetSkillType(), (uint)add); } public virtual void ReduceLevel(MapData map, UnitData origin, int reduce) { _level -= reduce; } public virtual void SetLevel(int level) { _level = level; } //刷新所有点数 public virtual void OnRefresh() { } public virtual void OnSkillAdd(MapData mapData, uint originId) { OriginId = originId; //如果是叠层技能并且是0层消失的那种,那么填加的时候就至少是1层 if (IsLevelSkill && _autoDisappear && _level < 1) _level = 1; } public virtual void BeforeMove(UnitData self, GridData grid, MapData mapData,MoveType moveType, List path = null) { } public virtual void OnMove(UnitData self, GridData grid, MapData mapData,MoveType moveType, List path = null) { } public virtual void OnBeInteractTarget(UnitData origin, UnitData self, GridData grid, MapData mapData) { } public virtual bool ActiveMoveFailed(UnitData self, GridData grid, MapData mapData, MoveType moveType, List path = null) { return false; } public virtual void BeforeDamagedSupportStage(MapData mapData, SettlementInfo info) { } public virtual void BeforeDamagedTransformStage(MapData mapData, SettlementInfo info) { } public virtual void BeforeDamageOther(MapData mapData, SettlementInfo info) { } public virtual void OnDamaged(MapData mapData, SettlementInfo info) { } public virtual void BeforeDisappear(MapData mapData, UnitData self) { } public virtual void OnDamageOther(MapData mapData, SettlementInfo info) { } public virtual void AfterDamageOther(MapData mapData, SettlementInfo info) { } public virtual void OnHealOther(MapData mapData, UnitData origin,UnitData target,HealType healType) { } /// /// 治疗前生命周期(被治疗者视角),用于目标侧技能修改最终回复量。 /// /// 地图数据 /// 拥有此技能的单位(被治疗者) /// 治疗来源,可能为空 /// 治疗类型 /// 当前回复量 /// 修改后的回复量 public virtual int BeforeHealSelf(MapData mapData, UnitData self, UnitData origin, HealType healType, int recover) { return recover; } /// /// AttackAlly 准备治疗前生命周期(Healer 视角),用于消耗性技能在此触发 /// /// 地图数据 /// 拥有此技能的单位(治疗者) /// 治疗目标 /// 治疗类型 public virtual void OnAttackAllyJustBeforeHeal(MapData mapData, UnitData self, UnitData target, HealType healType) { } /// /// 治疗后生命周期(Healer 视角) /// /// 地图数据 /// 治疗来源 /// 治疗目标 /// 治疗类型 /// 实际恢复的生命值 /// 溢出点数(治疗量超过最大生命值的部分) public virtual void OnAttackAllyAfterHeal(MapData mapData, UnitData origin, UnitData target, HealType healType, int realHealed, int overflow) { } public virtual void BeforeActiveAttackOther(MapData mapData, UnitData origin, UnitData target,out int addDmg) { addDmg = 0; } public virtual void AfterActiveAttackOther(MapData mapData, AttackInfo attackInfo) { } public virtual void AfterActiveAction(MapData mapData, UnitData origin, UnitData target) { } public virtual void AfterActiveAttacked(MapData mapData, AttackInfo attackInfo) { } public virtual void OnTurnStart(IdentifierBase self, MapData mapData) { } public virtual void OnAfterTurnStart(IdentifierBase self, MapData mapData) { } public virtual void OnTurnEnd(IdentifierBase self, MapData mapData) { } public virtual void OnFinished(IdentifierBase self, MapData mapData) { } public virtual bool CanAttackAll(UnitData self, MapData mapData) { return false; } public virtual void BeforeUnitDamaged(UnitData self, MapData mapData, SettlementInfo info) { } public virtual void OnUnitDamaged(UnitData self, MapData mapData, SettlementInfo info) { } public virtual void OnClearActionPoint(MapData mapData, UnitData self) { } public virtual void OnAnyUnitMove(MapData map, IdentifierBase identifier, UnitData moveUnit, GridData target, MoveType moveType) { } public virtual void OnAnyUnitDie(MapData map, UnitData self, UnitData dieUnit) { } public virtual void OnSelfCreated(MapData map, UnitData self) { } public virtual void OnAnyUnitCreate(MapData map, IdentifierBase identifier, UnitData newUnit) { } public virtual void OnActionExecuted(ActionLogicBase logic, CommonActionParams param, UnitData self) { } #endregion #region [ ---------------------------------------- 判断方法 -------------------------------------] public virtual bool IsLimitMoveToSelfTerrain(UnitData self, MapData mapData) { return false; } public virtual bool IsLimitActionPoint(UnitData self) { return false; } public virtual bool IsHideState() { return false; } // 判断是不是Officer public virtual bool IsOfficer() { return false; } public virtual bool IsLimitSelfMove(UnitData self, MapData mapData) { return false; } public virtual bool IsLimitSelfAttack(UnitData self, MapData mapData) { return false; } public virtual bool IsLimitTargetCounterAttack(UnitData self, MapData mapData) { return false; } public virtual bool IsLimitSelfCounterAttack(UnitData self, MapData mapData) { return false; } public virtual bool IsIgnoreControlArea(UnitData self, MapData mapData) { return false; } public virtual bool IsLimitSelfExp(UnitData self, MapData mapData) { return false; } public virtual bool IsIgnoreWoodAndMountains(UnitData self, MapData mapData) { return false; } public virtual bool IsCanMoveOnTerrain(UnitData self, MapData mapData, TerrainType terrainType) { return false; } public virtual bool TryGetLandToBoatTarget(UnitData self, MapData mapData, GridData targetGrid, out UnitFullType targetFullType) { targetFullType = new UnitFullType(); return false; } public virtual bool IsCanMoveOnFeature(UnitData self, MapData mapData, TerrainFeature terrainType) { return false; } public virtual bool IsIgnoreMoveLoss(UnitData self, MapData mapData, TerrainFeature terrainType) { return false; } public virtual bool IsIgnoreMoveLoss(UnitData self, MapData mapData, Vegetation terrainType) { return false; } public virtual bool IsIgnoreForestMoveDebuff(UnitData self, MapData mapData) { return false; } public virtual bool IsLimitCount(UnitData self, MapData mapData, out int count) { count = -1; return false; } public virtual bool IsCanMoveToNoUnitSelfCity(UnitData self, MapData mapData) { return false; } public virtual bool IsCanMoveGiantNearbyGrid(UnitData self, MapData mapData) { return false; } public virtual bool IsCanBeKill(UnitData self, MapData mapData) { return true; } public virtual bool IsSetMaxHealth(UnitData self, out int maxHealth) { maxHealth = 1; return false; } public virtual bool IsLimitDamaged(UnitData self, MapData mapData, int dmg) { return false; } public virtual bool IsCanBuildMine(UnitData self, MapData mapData) { return false; } public virtual bool IsInvisible(UnitData self, MapData mapData) { return false; } public virtual bool IsCanBeOfficer() { return false; } public virtual bool IsPrepareOfficer() { return false; } // yse TODO 和白哉确认 public virtual bool IsCanAttackTargetGrid(MapData map, UnitData self,GridData target) { return false; } public virtual bool AttackGroundExecute(MapData map, UnitData self, GridData target, out SkillType animSkillType) { animSkillType = SkillType.NONE; return false; } public virtual bool IsCanAttackAlly() { return false; } //在已经IsCanAttackAlly的情况下,额外判断能否attack指定target(目前仅用于suwako hebi,默认是true public virtual bool IsCanAttackTargetAlly(MapData map, UnitData self,UnitData target) { return true; } public virtual int GetAttackAllyRange(MapData mapData, UnitData self) { return 0; } public virtual bool IsCanAttackTargetUnit(MapData map, UnitData self, UnitData target) { return true; } public virtual bool IsOnCarry() { return false; } public virtual bool IsCanTransport() { return false; } public virtual bool IsTreatAsHero(MapData map, UnitData self,UnitData target, out GiantType giantType) { giantType = GiantType.None; return false; } #endregion #region [------------------------------------------------------------------------------获取状态值------------------------------------------------------------------------------] //判断当前技能是否被ban,注意这个是unitData的状态,一定要通过unit才知道自己是不是被ban public bool IsFrozen(UnitData self) { return self.IsSkillFrozenForSkillBase(this); } // 获取Officer的额外血量 public virtual int GetOfficerHealth() { return 0; } public virtual int GetExtraSight(UnitData self, MapData mapData) { return 0; } public virtual float GetCriticalHitRate(UnitData self, MapData mapData) { return 0; } public virtual float GetAttackAdditionParam(MapData mapData, UnitData self, UnitData target = null) { return 0; } public virtual float GetDefenseAdditionParam(MapData mapData, UnitData self, UnitData target = null) { return 0; } public virtual float GetAttackMultiplicationParam(MapData mapData, UnitData self, UnitData target = null) { return 1; } public virtual float GetDefenseMultiplicationParam(MapData mapData, UnitData self, UnitData target = null) { return 1; } #region [------------------------------------------------------------------ AttackAlly 生命周期 ------------------------------------------------------------------] /// /// 是否具备 AttackAlly 能力 /// public virtual bool AttackAllyEnable(MapData mapData, UnitData self, UnitData target) { return false; } /// /// 获取 AttackAlly 基础恢复数值 /// public virtual int AttackAllyBaseHeal(MapData mapData, UnitData self, UnitData target) { return 0; } /// /// 获取 AttackAlly 恢复数值加成(加法) /// public virtual int AttackAllyHealAddition(MapData mapData, UnitData self, UnitData target) { return 0; } /// /// AttackAlly 对自身造成的伤害(如Koakuma的治疗代价) /// public virtual int AttackAllySelfDamage(MapData mapData, UnitData self, UnitData target) { return 0; } /// /// Return true when a non-heal AttackAlly effect must spend action points before executing. /// This is for effects that intentionally grant a new action point as part of execution. /// public virtual bool AttackAllyConsumeActionPointBeforeExecute(MapData mapData, UnitData self, UnitData target) { return false; } public virtual bool AttackAllyIsNotProjectile(MapData mapData, UnitData self, UnitData target) { return false; } /// /// Execute non-heal AttackAlly effects, such as ally buffs. /// public virtual bool AttackAllyExecute(MapData mapData, UnitData self, UnitData target) { return false; } #endregion public virtual int GetExtraMoveRange(MapData mapData,UnitData self) { return 0; } //最终移动能力,目前仅用于KanakoSitting public virtual bool GetFinalMoveRange(UnitData self, out int fixedRange) { fixedRange = 0; return false; } public virtual int GetExtraAttackRange(MapData mapData,UnitData self) { return 0; } public virtual int GetSpecialSkillBonus(MapData mapData, UnitData self, SkillType skillType) { return 1; } public virtual int GetKillCount() { return 0; } public virtual float GetGridMoveFloor(MapData mapData, UnitData origin, GridData target) { return 0; } public virtual bool IsIgnoreZOC() { return false; } public virtual bool HasZOC() { return true; } public virtual bool IgnorePositiveTargetDefenseBonus(MapData mapData, UnitData self, UnitData target) { return false; } public virtual int GetHeroUpgradeRecoverAddition(MapData mapData, UnitData self) { return 0; } #endregion #region [--------------------操作修改属性--------------------] public virtual void SetOfficer() { } #endregion } }