2026-06-25 00:18:42 +08:00

1508 lines
48 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月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,
HakureiEinherjarPoint = 311,
HakureiCityEinherjarOffering = 312,
NoCounterAttack = 313,
HakureiDragonshipRam = 314,
CityRaided = 315,
Berserk = 316,
Disperse = 317,
Item = 318,
AlwaysHide = 319,
ValkyrieMoveHeal = 320,
ValkyrieProtectionCaster = 321,
ValkyrieProtection = 322,
SumirekoNorwayOrbBuff = 323,
SumirekoDenmarkOrbBuff = 324,
SumirekoEnglandOrbBuff = 325,
SumirekoOrbSwapMaxValueBuff = 326,
SuikaThrowReady = 327,
SuikaThrownUnit = 328,
SuikaThrowUnitBuff = 329,
//补充之前的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<SkillBase> 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<Vector2Int> path = null);
// 事件触发生命周期 (事件已发生)
public void OnMove(UnitData self, GridData grid, MapData mapData, MoveType moveType, List<Vector2Int> path = null);
public void AfterTransformFromBoat(UnitData self, GridData grid, MapData mapData, MoveType moveType,
List<Vector2Int> 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<Vector2Int> 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<List<GridData>> _aroundBufPool = new();
private static int _poolTop = -1;
/// <summary>
/// 从池中租用 List<GridData>,自动清空
/// </summary>
protected static List<GridData> RentAroundBuf()
{
_poolTop++;
if (_poolTop >= _aroundBufPool.Count)
{
_aroundBufPool.Add(new List<GridData>(8));
}
var buf = _aroundBufPool[_poolTop];
buf.Clear();
return buf;
}
/// <summary>
/// 归还 List<GridData> 到池中
/// </summary>
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<SkillBase> 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 已查不到 selfself == 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<Vector2Int> path = null)
{
}
public virtual void OnMove(UnitData self, GridData grid, MapData mapData,MoveType moveType, List<Vector2Int> path = null)
{
}
public virtual void AfterTransformFromBoat(UnitData self, GridData grid, MapData mapData, MoveType moveType,
List<Vector2Int> 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<Vector2Int> 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)
{
}
/// <summary>
/// 治疗前生命周期(被治疗者视角),用于目标侧技能修改最终回复量。
/// </summary>
/// <param name="mapData">地图数据</param>
/// <param name="self">拥有此技能的单位(被治疗者)</param>
/// <param name="origin">治疗来源,可能为空</param>
/// <param name="healType">治疗类型</param>
/// <param name="recover">当前回复量</param>
/// <returns>修改后的回复量</returns>
public virtual int BeforeHealSelf(MapData mapData, UnitData self, UnitData origin, HealType healType, int recover)
{
return recover;
}
/// <summary>
/// AttackAlly 准备治疗前生命周期Healer 视角),用于消耗性技能在此触发
/// </summary>
/// <param name="mapData">地图数据</param>
/// <param name="self">拥有此技能的单位(治疗者)</param>
/// <param name="target">治疗目标</param>
/// <param name="healType">治疗类型</param>
public virtual void OnAttackAllyJustBeforeHeal(MapData mapData, UnitData self, UnitData target, HealType healType)
{
}
/// <summary>
/// 治疗后生命周期Healer 视角)
/// </summary>
/// <param name="mapData">地图数据</param>
/// <param name="origin">治疗来源</param>
/// <param name="target">治疗目标</param>
/// <param name="healType">治疗类型</param>
/// <param name="realHealed">实际恢复的生命值</param>
/// <param name="overflow">溢出点数(治疗量超过最大生命值的部分)</param>
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 ------------------------------------------------------------------]
/// <summary>
/// 是否具备 AttackAlly 能力
/// </summary>
public virtual bool AttackAllyEnable(MapData mapData, UnitData self, UnitData target)
{
return false;
}
/// <summary>
/// 获取 AttackAlly 基础恢复数值
/// </summary>
public virtual int AttackAllyBaseHeal(MapData mapData, UnitData self, UnitData target)
{
return 0;
}
/// <summary>
/// 获取 AttackAlly 恢复数值加成(加法)
/// </summary>
public virtual int AttackAllyHealAddition(MapData mapData, UnitData self, UnitData target)
{
return 0;
}
/// <summary>
/// AttackAlly 对自身造成的伤害如Koakuma的治疗代价
/// </summary>
public virtual int AttackAllySelfDamage(MapData mapData, UnitData self, UnitData target)
{
return 0;
}
/// <summary>
/// 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.
/// </summary>
public virtual bool AttackAllyConsumeActionPointBeforeExecute(MapData mapData, UnitData self, UnitData target)
{
return false;
}
public virtual bool AttackAllyIsNotProjectile(MapData mapData, UnitData self, UnitData target)
{
return false;
}
/// <summary>
/// Execute non-heal AttackAlly effects, such as ally buffs.
/// </summary>
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
}
}