/* * @Author: 白哉 * @Description: * @Date: 2025年04月03日 星期四 11:04:31 * @Modify: */ using System; using System.Collections.Generic; using Logic; using Logic.CrashSight; using Logic.Skill; using MemoryPack; using TH1_Logic.Core; using TH1_Logic.HeroTask; using TH1_Renderer; using TH1Renderer; using UnityEngine; namespace RuntimeData { // 行动点枚举 public enum ActionPointType { Common, Move, Attack, Capture, } // 地图上所有的小兵数据 [MemoryPackable] public partial class UnitMapData { public List UnitList; [MemoryPackIgnore] //public bool UnitMapRenderMark; private Dictionary _unitDict; [MemoryPackConstructor] public UnitMapData() { UnitList = new List(); _unitDict = new Dictionary(); //UnitMapRenderMark = true; } public UnitMapData(UnitMapData copyData) { UnitList = new List(); _unitDict = new Dictionary(); foreach (var unitData in copyData.UnitList) { var newUnitData = new UnitData(unitData); UnitList.Add(newUnitData); _unitDict[newUnitData.Id] = newUnitData; } } // MemoryPack 反序列化之后的后处理 [MemoryPackOnDeserialized] public void OnAfterMemoryPackDeserialize() { UnitList ??= new List(); _unitDict ??= new Dictionary(); _unitDict.Clear(); foreach (var unit in UnitList) { if (unit == null) continue; _unitDict[unit.Id] = unit; } } public void DeepCopy(UnitMapData copyData) { _unitDict.Clear(); for (int i = 0; i < copyData.UnitList.Count; i++) { var copyUnit = copyData.UnitList[i]; if (i >= UnitList.Count) { var unit = new UnitData(copyUnit); UnitList.Add(unit); _unitDict[unit.Id] = unit; } else { UnitList[i].DeepCopy(copyUnit); _unitDict[UnitList[i].Id] = UnitList[i]; } } for (int i = UnitList.Count - 1; i >= copyData.UnitList.Count; i--) { _unitDict.Remove(UnitList[i].Id); UnitList.RemoveAt(i); } } [MemoryPackIgnore] private List _turnUnitBuffer; public void OnTurnStart(MapData map, PlayerData player) { _turnUnitBuffer ??= new List(); _turnUnitBuffer.Clear(); map.GetUnitDataListByPlayerId(player.Id, _turnUnitBuffer); foreach (var unit in _turnUnitBuffer) { Main.UnitLogic.StartNextTurn(map, unit); unit.OnTurnStart(map); } } public void OnAfterTurnStart(MapData map, PlayerData player) { _turnUnitBuffer ??= new List(); _turnUnitBuffer.Clear(); map.GetUnitDataListByPlayerId(player.Id, _turnUnitBuffer); foreach (var unit in _turnUnitBuffer) { unit.OnAfterTurnStart(map); } } public void OnTurnEnd(MapData map, PlayerData player) { _turnUnitBuffer ??= new List(); _turnUnitBuffer.Clear(); map.GetUnitDataListByPlayerId(player.Id, _turnUnitBuffer); foreach (var unit in _turnUnitBuffer) { Main.UnitLogic.UnitEndTurn(map, unit); unit.OnTurnEnd(map); } } // 新建小兵 public UnitData AddUnitData(UnitFullType unitFullType, MapIdGenerator idGenerator) { var unit = new UnitData(unitFullType ,idGenerator); UnitList.Add(unit); _unitDict[unit.Id] = unit; return unit; } // 删除小兵 public void RemoveUnitData(uint unitId) { UnitList ??= new List(); _unitDict ??= new Dictionary(); var removed = false; for (var i = UnitList.Count - 1; i >= 0; i--) { var unitData = UnitList[i]; if (unitData == null || unitData.Id != unitId) continue; UnitList.RemoveAt(i); removed = true; } _unitDict.Remove(unitId); if (!removed) LogSystem.LogWarning($"UnitMapData.RemoveUnitData: UnitList missing unitId={unitId}, rebuilt dict may have been stale."); } // 通过 uid 找小兵数据 unitData public bool GetUnitDataByUnitId(uint unitId, out UnitData unitData) { _unitDict ??= new Dictionary(); if (_unitDict.TryGetValue(unitId, out unitData)) return true; if (UnitList != null) { foreach (var unit in UnitList) { if (unit == null || unit.Id != unitId) continue; unitData = unit; _unitDict[unitId] = unit; LogSystem.LogWarning($"UnitMapData.GetUnitDataByUnitId: recovered stale dict entry unitId={unitId}."); return true; } } unitData = null; return false; } } [Serializable] public struct UnitFullType : IEquatable { public UnitType UnitType; public GiantType GiantType; public uint UnitLevel; public UnitFullType(UnitType unitType, GiantType giantType, uint level) { UnitType = unitType; GiantType = giantType; UnitLevel = level; } public bool Equals(UnitFullType other) { return UnitType == other.UnitType && GiantType == other.GiantType && UnitLevel == other.UnitLevel; } public override bool Equals(object obj) { return obj is UnitFullType other && Equals(other); } public override int GetHashCode() { return HashCode.Combine((int)UnitType, (int)GiantType, UnitLevel); } public static bool operator ==(UnitFullType left, UnitFullType right) { return left.Equals(right); } public static bool operator !=(UnitFullType left, UnitFullType right) { return !(left == right); } } //小兵数据 [MemoryPackable] public partial class UnitData : IdentifierBase { // 判断是否已经setDie并从mapDataRelation中除名,如果是判断unit是否存活,应该用IsAlive函数 [MemoryPackInclude] private bool _notSetDie; //单位类型 public UnitFullType UnitFullType; // 单位类型(legacy) public UnitType UnitType => UnitFullType.UnitType; public GiantType GiantType => UnitFullType.GiantType; public uint UnitLevel => UnitFullType.UnitLevel; //棋子种类 public ChessType ChessType; // 血量 public int Health; private const float PositiveIntEpsilon = 0.0001f; private const double PositiveIntDoubleEpsilon = 0.000001d; public static int CeilPositiveToInt(float value) { if (value <= 0f) return 0; var rounded = Mathf.RoundToInt(value); if (rounded > 0 && Mathf.Abs(value - rounded) <= PositiveIntEpsilon) return rounded; return Mathf.CeilToInt(value); } public static int CeilPositiveToInt(double value) { if (value <= 0d) return 0; var rounded = (int)Math.Round(value); if (rounded > 0 && Math.Abs(value - rounded) <= PositiveIntDoubleEpsilon) return rounded; return (int)Math.Ceiling(value); } // 攻击范围 //英雄经验值 public int GiantExp; public UnitFullType OriginUnitFullType; //是否老兵 //public bool Veteran; //存储carry的UnitType public UnitFullType CarryUnitFullType; public UnitType CarryUnitType => CarryUnitFullType.UnitType; public GiantType CarryGiantType => CarryUnitFullType.GiantType; public uint CarryUnitLevel => CarryUnitFullType.UnitLevel; //存储carry的exp状态 //public int CarryExp; //存储carry的Veteran状态 //public bool CarryVeteran; // 防御削减系数 public float DefenseReduction; // 军团编号 [MemoryPackIgnore] public uint LegionId; public UnitFullType FullTypeCache; public List SkillCache; public Dictionary ActionPoint; // 行动点缓存,UI用不参与逻辑 [MemoryPackIgnore] private Dictionary _actionCache; public Dictionary ActionCache => _actionCache; public void SetAlive() { _notSetDie = true; } public void SetDie() { _notSetDie = false; } public bool IsAlive() { return _notSetDie && Health > 0; } // 单位已被扣到 0 血但仍未 SetDie(即未走过 UnitDie/UnitUnnaturalDie)。 // Why: 用于在伤害结算末尾兜底僵尸单位(IsCanBeKill 拒绝 + OnDamaged 被 Frozen 跳过等场景)。 public bool IsZombie() { return _notSetDie && Health <= 0; } public PlayerData Player(MapData map) { map.GetPlayerDataByUnitId(this.Id, out var player); return player; } public bool Player(MapData map,out PlayerData player) { return map.GetPlayerDataByUnitId(this.Id, out player); } public CityData City(MapData map) { map.GetCityDataByUnitId(this.Id, out var city); return city; } public GridData Grid(MapData map) { map.GetGridDataByUnitId(Id, out var grid); return grid; } public bool Grid(MapData map,out GridData grid) { return map.GetGridDataByUnitId(Id, out grid); } public bool IsValidOnMap(MapData map, GridData expectedGrid = null) { if (map?.UnitMap == null) return false; if (!map.UnitMap.GetUnitDataByUnitId(Id, out var currentUnit)) return false; if (!ReferenceEquals(currentUnit, this)) return false; if (!map.GetPlayerDataByUnitId(Id, out _)) return false; if (expectedGrid == null) return true; return map.GetGridDataByUnitId(Id, out var currentGrid) && currentGrid != null && currentGrid.Id == expectedGrid.Id; } public UnitRenderer Renderer(MapData map) { if (!map.IsCurrentShowMap()) return null; var mapRenderer = MapRenderer.Instance; if (mapRenderer?.ROUnitMap == null) return null; return mapRenderer.ROUnitMap.GetValueOrDefault(this.Id); } public HeroTaskContentBase HeroTask(MapData map) { HeroTaskContentBase heroTask = null; Player(map)?.PlayerHeroData.HeroTaskDict.TryGetValue(UnitFullType,out heroTask); return heroTask; } //获取最大血量 public int GetMaxHealth() { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsSetMaxHealth(this, out var maxHealth)) { return maxHealth; } } if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitFullType, out var unitTypeInfo)) return 0; if (unitTypeInfo.NoMaxHealth) { if (CarryUnitFullType.UnitType != UnitType.None) { if(Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(CarryUnitFullType,out var carryUnitTypeInfo)) return carryUnitTypeInfo.MaxHealth + GetOfficerHealth(); return 0; } return 0; } else return unitTypeInfo.MaxHealth + GetOfficerHealth(); } public bool IsMainMap() { if (!Main.MapData.UnitMap.GetUnitDataByUnitId(Id, out var unitData) || unitData != this) return false; return true; } public bool InMainSight() { if (!IsMainMap()) return false; var grid = Grid(Main.MapData); if (grid == null) return false; return grid.InMainSight(); } //获取画面的位置 public Vector3 GetPosition(MapData map) { return TryGetPosition(map, out var position) ? position : Vector3.zero; } public bool TryGetPosition(MapData map, out Vector3 position) { position = Vector3.zero; if (map == null || map != Main.MapData) return false; var mapRenderer = MapRenderer.Instance; if (mapRenderer?.ROUnitMap != null && mapRenderer.ROUnitMap.TryGetValue(Id, out var renderer) && renderer != null) { position = renderer.GetPosition(); return true; } if (map.GetGridDataByUnitId(Id, out var gridData) && gridData != null && Table.Instance != null) { position = Table.Instance.GridToWorld(gridData, "isUnit"); LogSystem.LogWarning($"UnitData.GetPosition: unitId={Id} missing in ROUnitMap, fallback to grid position."); return true; } LogSystem.LogWarning($"UnitData.GetPosition: unitId={Id} missing in ROUnitMap and grid relation."); return false; } // 获取血量百分比 public float GetHealthRatio() { return (float)Health / GetMaxHealth(); } // 获取ProjectileType public ProjectileType GetProjectileType(MapData map,GridData targetGrid)//, MoveAttackType moveAttackType) { if (!Grid(map, out var originGrid)) return ProjectileType.None; int distance = map.GridMap.CalcDistance(originGrid, targetGrid); //TODO 将来复杂了就走skill生命周期,现在就全先写在这儿了 //如果拥有kanakositting,全部变为远程 if (GetSkill(SkillType.KANAKOSITTING, out var _)) { return ProjectileType.KanakoAttack; } if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitFullType, out var info)) return ProjectileType.None; if (distance == 1 && info.ForceMelee) return ProjectileType.Melee; return info.ProjectileType; } // 获取军事分 public float GetMilitary() { if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitFullType, out var info)) return 0; //如果是英雄,要单独结算,按照英雄的不同等级定价结算 if (UnitFullType.UnitType == UnitType.Giant) return UnitFullType.UnitLevel switch { 1 => 10, 2 => 10, 3 => 15, 4 => 25, _ => 0 }; //如果生命值低于50%,得分打8折 if (GetHealthRatio() <= 0.5f) return info.Cost * 0.8f; return info.Cost; } // 获取造价 public float GetCost() { if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitFullType, out var info)) return 0; return info.Cost; } [MemoryPackConstructor] public UnitData() { } //注意,这里没有处理unit Copy之后,skill要触发skillAdd生命周期的情况 public UnitData(UnitData copyData) { Id = copyData.Id; _notSetDie = copyData._notSetDie; UnitFullType = copyData.UnitFullType; Health = copyData.Health; GiantExp = copyData.GiantExp; CarryUnitFullType = copyData.CarryUnitFullType; OriginUnitFullType = copyData.OriginUnitFullType; LegionId = copyData.LegionId; DefenseReduction = copyData.DefenseReduction; foreach (var skill in copyData.Skills) Skills.Add(skill.GetCopySkill()); SkillCache = new List(); foreach (var skill in copyData.SkillCache) SkillCache.Add(skill.GetCopySkill()); ActionPoint = new Dictionary(); foreach (var kv in copyData.ActionPoint) { ActionPoint[kv.Key] = kv.Value; } _actionCache = new Dictionary(); } //注意,这里没有处理unit Copy之后,skill要触发skillAdd生命周期的情况 public void DeepCopy(UnitData copyData) { Id = copyData.Id; _notSetDie = copyData._notSetDie; UnitFullType = copyData.UnitFullType; ChessType = copyData.ChessType; Health = copyData.Health; GiantExp = copyData.GiantExp; CarryUnitFullType = copyData.CarryUnitFullType; OriginUnitFullType = copyData.OriginUnitFullType; LegionId = copyData.LegionId; DefenseReduction = copyData.DefenseReduction; Skills.Clear(); foreach (var skill in copyData.Skills) Skills.Add(skill.GetCopySkill()); SkillCache ??= new List(); SkillCache.Clear(); foreach (var skill in copyData.SkillCache) SkillCache.Add(skill.GetCopySkill()); ActionPoint ??= new Dictionary(); ActionPoint.Clear(); foreach (var kv in copyData.ActionPoint) { ActionPoint[kv.Key] = kv.Value; } _actionCache ??= new Dictionary(); } // 无参数初始化 public UnitData(UnitFullType unitFullType, MapIdGenerator idGenerator) { Id = idGenerator.GeneratorId(); UnitFullType = unitFullType; SkillCache = new List(); ActionPoint = new Dictionary(); _actionCache = new Dictionary(); InitData(); } // MemoryPack 反序列化之后的后处理 [MemoryPackOnDeserialized] public void OnAfterMemoryPackDeserialize() { _actionCache ??= new Dictionary(); } public void InitData() { if (Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitFullType, out var unitTypeInfo) && unitTypeInfo.NoMaxHealth) { //TODO 目前 nomaxhealth和carry不是一个概念 CarryUnitFullType.UnitType = UnitType.Warrior; } _notSetDie = true; Health = GetMaxHealth(); ChessType = unitTypeInfo.ChessType; //AttackRange = Table.Instance.QueryUnitAttackRange(UnitType); //MoveRange = Table.Instance.QueryUnitMoveRange(UnitType); //Exp = 0; GiantExp = 0; //Veteran = false; LegionId = 0; } //----------------------------------------- 判断属性 ---------------------------------------------- /// /// 静态判断:该UnitType是否属于BigGuy类(不含等级判断) /// public static bool IsBigGuyType(UnitType unitType) { return unitType is UnitType.BigGuy or UnitType.KaguyaFrenchWolf or UnitType.KomeijiIndianBigGuy or UnitType.KomeijiIndianJuggernaut or UnitType.WolfJuggernaut or UnitType.Juggernaut or UnitType.RemiliaEgyptianKoakuma or UnitType.RemiliaEgyptianKoakumaLion or UnitType.MoriyaHebi; } public bool IsBigGuy(bool useToCheckDie = false) { if(UnitType == UnitType.BigGuy) return true; if(UnitType == UnitType.KaguyaFrenchWolf) return true; if(UnitType == UnitType.KomeijiIndianBigGuy) return true; if(UnitType == UnitType.KomeijiIndianJuggernaut) return true; if(UnitType == UnitType.WolfJuggernaut) return true; if(UnitType == UnitType.Juggernaut) return true; if(UnitType == UnitType.RemiliaEgyptianKoakuma && !useToCheckDie) return true; if(UnitType == UnitType.RemiliaEgyptianKoakumaLion) return true; if(UnitType == UnitType.MoriyaHebi && UnitLevel >= 3) return true; return false; } public bool IsHero(bool useToCheckDie = false) { if(GiantType == GiantType.None && CarryGiantType == GiantType.None) return false; if(UnitType == UnitType.KaguyaFrenchMokouEgg) return true; if(GiantType == GiantType.FrenchMokou && useToCheckDie) return false; return true; } // 全局通知调用 public void OnTurnStart(MapData map) { OnSkillsTurnStart(map); _actionCache.Clear(); } public void OnAfterTurnStart(MapData map) { OnSkillsAfterTurnStart(map); } public void OnTurnEnd(MapData map) { OnSkillsTurnEnd(map); } protected override void OnSkillsTurnStart(MapData map) { var isFrozen = IsFrozen(); var copy = new List(Skills); foreach (var skill in copy) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; skill.BeforeTurnStart(); if (skill.IsFinished()) { skill.OnFinished(this, map); Skills.Remove(skill); continue; } skill.OnTurnStart(this, map); } } protected override void OnSkillsAfterTurnStart(MapData map) { var isFrozen = IsFrozen(); var copy = new List(Skills); foreach (var skill in copy) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; skill.OnAfterTurnStart(this, map); } } protected override void OnSkillsTurnEnd(MapData map) { var isFrozen = IsFrozen(); var copy = new List(Skills); foreach (var skill in copy) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; skill.OnTurnEnd(this, map); } } //返回该unit的视野半径(考虑unit站在map的实机位置下的视野半径) public int GetSightRange(MapData map) { int v = 1; var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; v += skill.GetExtraSight(this,map); } if (map.GetGridDataByUnitId(this.Id, out var grid) && grid.Feature == TerrainFeature.Mountain) v = Mathf.Max(v, 2); return v; } //返回该unit如果站在具体的pos下的视野半径 public int GetSightRange(MapData map,GridData grid) { int v = 1; var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; v += skill.GetExtraSight(this,map); } if (grid.Feature == TerrainFeature.Mountain) v = Mathf.Max(v, 2); return v; } public int GetAttackRange(MapData mapData) { var attackRange = 0; var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; attackRange += skill.GetExtraAttackRange(mapData,this); } attackRange += Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitFullType, out var info) ? info.AttackRange : 0; return attackRange; } //最终移动能力。 目前仅用于Kanako Sitting的情况(拥有移动力,但是无法移动) public int FinalMoveRange(MapData mapData) { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (!skill.GetFinalMoveRange(this, out var finalMoveRange)) continue; return finalMoveRange; } return GetMoveRange(mapData); } public int GetMoveRange(MapData mapData) { if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitFullType, out var info)) return 0; var moveRange = info.MoveRange; var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; moveRange += skill.GetExtraMoveRange(mapData,this); } return moveRange; } //返回该unit是否是静止经验(无法升级) public bool CheckIsStaticExp(MapData map) { var isStaticExp = false; var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsLimitSelfExp(this, map)) isStaticExp = true; } return isStaticExp; } //返回该unit处于城市的时候是否获得防御加成 public bool CheckIsCityDefenseBuff() { return GetSkill(SkillType.FORTIFY,out var _); } //LandAndPort, WaterAndAshore, LandAndWater, WaterOnly, LandOnly, Fly public LandType GetLandType() { return Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitFullType,out var Info)?Info.LandType:LandType.None; } //----------------------------------------- 数值计算相关函数 --------------------------------------------- // 获取技能加成倍数 (本人拥有的其他技能对特定技能的加成倍数) public float GetSpecialSkillBonus(MapData mapData, SkillType skillType) { var value = 1f; var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; value *= skill.GetSpecialSkillBonus(mapData, this, skillType); } return value; } // 获得总攻击力的string显示 public string GetAttackShowString(MapData map) { var raw = GetRawAttackValue(map, null); var add = GetAttackAdditionParam(map, null); var mul = GetAttackMultiplicationParam(map); string ret = raw.ToString(); if (add != 0) ret += (add > 0 ?" + ":" - ") + Mathf.Abs(add); if (mul != 1f) ret += " (× " + mul.ToString() + ")"; return ret; } public float GetRawAttackValue(MapData map, UnitData target = null) { if(!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitFullType, out var info)) return 0; return info.Attack; } // 基础攻击力 public float GetBaseAttackValue(MapData map, UnitData target = null) { if(!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitFullType, out var info)) return 0; return info.Attack + GetAttackAdditionParam(map, target); } // 总攻击力 public float GetAllAttackValue(MapData map, UnitData target = null) { return GetBaseAttackValue(map, target) * GetAttackMultiplicationParam(map, target); } // 攻击力加算系数 public float GetAttackAdditionParam(MapData map, UnitData target = null) { var value = 0f; var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; value += skill.GetAttackAdditionParam(map, this, target); } return value; } // 攻击力乘算系数 public float GetAttackMultiplicationParam(MapData map, UnitData target = null) { var value = 1f; var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; value *= skill.GetAttackMultiplicationParam(map, this, target); } return value; } // 获得总防御力的string显示 public string GetDefenseShowString(MapData map) { var raw = GetRawDefenseValue(map, null); var add = GetDefenseAdditionParam(map, null); var mul = GetDefenseMultiplicationParam(map); string ret = raw.ToString(); if (add != 0) ret += (add > 0 ?" + ":" - ") + Mathf.Abs(add); if (mul != 1f) ret += " (× " + mul.ToString() + ")"; return ret; } // 获得总射程的string显示 public string GetMoveRangeShowString(MapData mapData) { var raw = Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitFullType, out var info) ? info.MoveRange : 0; var add = 0; var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; add += skill.GetExtraMoveRange(mapData,this); } //var mul = GetDefenseMultiplicationParam(map); string ret = raw.ToString(); if (add != 0) { //特判椛椛的铁十字追击 if(GetSkill(SkillType.MOMIJIHUNTER,out var _)) ret += (add > 0 ?" + ":" - ") + Mathf.Abs(add) + "?"; else ret += (add > 0 ?" + ":" - ") + Mathf.Abs(add); } //if (mul != 1f) ret += " (× " + mul.ToString() + ")"; return ret; } // 获得总射程的string显示 public string GetAttackRangeShowString(MapData mapData) { var raw = Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitFullType, out var info) ? info.AttackRange : 0; var add = 0; var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; add += skill.GetExtraAttackRange(mapData,this); } //var mul = GetDefenseMultiplicationParam(map); string ret = raw.ToString(); if (add != 0) ret += (add > 0 ?" + ":" - ") + Mathf.Abs(add); //if (mul != 1f) ret += " (× " + mul.ToString() + ")"; return ret; } public float GetRawDefenseValue(MapData map, UnitData target = null) { if(!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitFullType, out var info)) return 0; return info.Defense; } // 基础防御力 public float GetBaseDefenseValue(MapData map, UnitData target = null) { if(!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitFullType, out var info)) return 0; return info.Defense + GetDefenseAdditionParam(map, target); } // 总防御力 public float GetAllDefenseValue(MapData map, UnitData target = null) { return GetBaseDefenseValue(map, target) * GetDefenseMultiplicationParam(map, target); } // 防御力加算系数 public float GetDefenseAdditionParam(MapData map, UnitData target = null) { var value = 0f; var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; value += skill.GetDefenseAdditionParam(map, this, target); } return value; } //只用于显示防御外壳判断用的系数计算(会去除<1的乘算因子) public float GetDefenseMultiplicationParamOnlyForDefenseShow(MapData map, UnitData target = null) { var buffValue = 1f; // 增益效果(>1)取最大值 var debuffMult = 1f; // 减益效果(<1)累乘 var isZero = 1f; var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; var t = skill.GetDefenseMultiplicationParam(map, this, target); isZero *= t; // 分离增益和减益 if (t > 1f) buffValue = Mathf.Max(t, buffValue); // 增益取最大 else if (t < 1f) debuffMult *= t; // 减益累乘 } var p = GetTerrainDefenseMultiplicationParam(map); if (p > 1f) buffValue = Mathf.Max(p, buffValue); else if (p < 1f) debuffMult *= p; isZero *= p; if (isZero == 0f) return 0f; // 最终防御系数 = 最大增益 * 减益累乘 return buffValue; } // 防御力乘算系数 public float GetDefenseMultiplicationParam(MapData map, UnitData target = null) { var buffValue = 1f; // 增益效果(>1)取最大值 var debuffMult = 1f; // 减益效果(<1)累乘 var isZero = 1f; var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; var t = skill.GetDefenseMultiplicationParam(map, this, target); isZero *= t; // 分离增益和减益 if (t > 1f) buffValue = Mathf.Max(t, buffValue); // 增益取最大 else if (t < 1f) debuffMult *= t; // 减益累乘 } var p = GetTerrainDefenseMultiplicationParam(map); if (p > 1f) buffValue = Mathf.Max(p, buffValue); else if (p < 1f) debuffMult *= p; isZero *= p; if (isZero == 0f) return 0f; // 最终防御系数 = 最大增益 * 减益累乘 return buffValue * debuffMult; } #region [------------------------------------------------------------------ AttackAlly 生命周期入口 ------------------------------------------------------------------] /// /// 是否具备 AttackAlly 能力 /// public bool AttackAllyEnable(MapData mapData, UnitData target) { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.AttackAllyEnable(mapData, this, target)) return true; } return false; } /// /// 获取 AttackAlly 基础恢复数值 /// public int AttackAllyBaseHeal(MapData mapData, UnitData target) { int ret = 0; var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; ret += skill.AttackAllyBaseHeal(mapData, this, target); } return ret; } /// /// 获取 AttackAlly 恢复数值加成(加法) /// public int AttackAllyHealAddition(MapData mapData, UnitData target) { int ret = 0; var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; ret += skill.AttackAllyHealAddition(mapData, this, target); } return ret; } /// /// 获取 AttackAlly 对自身造成的伤害(如Koakuma的治疗代价) /// public int AttackAllySelfDamage(MapData mapData, UnitData target) { int ret = 0; var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; ret += skill.AttackAllySelfDamage(mapData, this, target); } return ret; } #endregion // 地形防御乘算系数 public float GetTerrainDefenseMultiplicationParam(MapData map) { var extraDefense = 1f; if (!map.GetGridDataByUnitId(Id, out var grid) || !map.GetPlayerDataByUnitId(Id, out var unitPlayer)) { LogSystem.LogError("UnitData GetExtraDefense Cant find grid or player by uid"); return extraDefense; } //如果是中立土地 if (!map.GetPlayerDataByTerritoryGridId(grid.Id, out var gridPlayer)) return extraDefense; //如果不是盟友土地 if(!map.SameUnion(unitPlayer.Id,gridPlayer.Id)) return extraDefense; if (grid.Resource == ResourceType.NavalBase || grid.Resource == ResourceType.Military || grid.Resource == ResourceType.RemiliaMilitary) extraDefense *= 1.5f; return extraDefense; } // 获取暴击率 public float GetCriticalHitRate(MapData map) { var criticalHitRate = 0f; var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; criticalHitRate += skill.GetCriticalHitRate(this, map); } return criticalHitRate; } #region [------------------------------ Skill 相关 -----------------------------------] //清楚隐身状态 /*public void RemoveHideState(MapData map) { foreach(skill) //RemoveSkill(SkillType.HideState, map); }*/ //获取officer的额外血量 public int GetOfficerHealth() { int ret = 0; var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; ret += skill.GetOfficerHealth(); } return ret; } //判断当前是不是officer public bool IsOfficer() { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsOfficer()) return true; } return false; } public bool IsPrepareOfficer() { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsPrepareOfficer()) return true; } return false; } public bool IsCanBeOfficer() { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsCanBeOfficer()) return true; } return false; } // 是否能移动到伟人身周 public bool IsCanMoveGiantNearbyGrid(MapData map) { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsCanMoveGiantNearbyGrid(this, map)) return true; } return false; } // 是否movekill public bool IsCanMoveKill(MapData mapData) { if (GetAttackRange(mapData) == 1) return true; return false; } // 是否拥有特别伤害技能,例如ROYALFLAME public bool IsHasAttackSkill(MapData map) { return false; } // 是否能移动无人城市 public bool IsCanMoveNoUnitSelfCity(MapData map) { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsCanMoveToNoUnitSelfCity(this, map)) return true; } return false; } // 是否可以死亡 public bool CanBeKilled(MapData map) { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (!skill.IsCanBeKill(this, map)) return false; } return true; } // 是否可以被伤害结算 public bool IsLimitDamaged(MapData map, int dmg) { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsLimitDamaged(this, map, dmg)) return true; } return false; } // 是否经验锁定 public bool IsExpLock(MapData map) { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsLimitSelfExp(this, map)) return true; } return false; } //返回该unit是否能在terrain上移动 public bool IsCanMoveOnTerrain(MapData map, TerrainType terrainType) { var landType = GetLandType(); if (terrainType == TerrainType.Land && (landType == LandType.LandAndPort || landType == LandType.LandOnly || landType == LandType.LandAndWater || landType == LandType.Fly)) return true; if (terrainType == TerrainType.ShallowSea && (landType == LandType.WaterAndAshore || landType == LandType.WaterOnly || landType == LandType.LandAndWater || landType == LandType.Fly)) return true; var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsCanMoveOnTerrain(this, map, terrainType)) return true; } return false; } //返回该unit是否能在feature上移动 public bool IsCanMoveOnFeature(MapData map, TerrainFeature featureType) { if (featureType != TerrainFeature.Mountain) return true; var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsCanMoveOnFeature(this, map,featureType)) return true; } return false; } //返回该unit是否能攻击所有人 public bool CanAttackAll(MapData map) { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.CanAttackAll(this, map)) return true; } return false; } //返回该unit是否隐身 public bool IsHideState(MapData map) { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsHideState()) return true; } return false; } //返回该unit是否不可见(不考虑视野),与上方IsHideState的区别在于, 我方/盟友的隐身单位是可见的,敌方的隐身单位才不可见 public bool IsHideAndCantSee(MapData map,PlayerData player) { if (!IsHideState(map)) return false; if (player == null) return false; if (Player(map, out var unitPlayer) && map.SameUnion(unitPlayer.Id, player.Id)) return false; return true; } //返回是否限制unit攻击 public bool IsLimitSelfAttack(MapData map) { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsLimitSelfAttack(this, map)) return true; } return false; } //返回该unit是否能移动 public bool IsLimitSelfMove(MapData map) { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsLimitSelfMove(this, map)) return true; } return false; } //返回该unit是否能攻击ground public bool IsCanAttackTargetGrid(MapData map, GridData target) { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsCanAttackTargetGrid(map,this,target)) return true; } return false; } //返回该unit是否能攻击Ally public bool IsCanAttackAlly() { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsCanAttackAlly()) return true; } return false; } //返回该unit是否处在载具上 public bool IsOnCarry() { foreach (var skill in Skills) { if (skill.IsOnCarry()) return true; } return false; } //返回该unit是否可以向BonePile献祭(古明地非Giant单位、拥有BoneSacrifice技能、或拥有CorpseBuff技能的单位) public bool CanFeedBonePile(MapData map) { if (GetSkill(SkillType.BoneSacrifice, out _)) return true; if (GetSkill(SkillType.CorpseBuff, out var corpsBuff) && corpsBuff.Level > 0) return true; if (UnitType == UnitType.Giant) return false; if (!Player(map, out var player)) return false; if (player.PlayerCivId != 3) return false; return true; } //返回该unit是否能攻击ground public bool IsCanAttackTargetAlly(MapData map, UnitData target) { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (!skill.IsCanAttackTargetAlly(map,this,target)) return false; } return true; } public bool IsCanAttackTargetUnit(MapData map, UnitData target) { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (!skill.IsCanAttackTargetUnit(map, this, target)) return false; } return true; } //返回该unit是否能反击 public bool IsLimitSelfCounterAttack(MapData map) { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsLimitSelfCounterAttack(this, map)) return true; } return false; } //返回目标 unit 是否能反击 public bool IsLimitTargetCounterAttack(MapData map) { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsLimitTargetCounterAttack(this, map)) return true; } return false; } public bool IsCanTransport() { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsCanTransport()) return true; } return false; } // 移动是否会失败(并不限制移动) public bool IsActiveMoveFailed(MapData map, GridData target, MoveType moveType, List path = null) { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.ActiveMoveFailed(this, target, map, moveType, path)) return true; } return false; } // 移动后的变化 public void BeforeMove(MapData map, GridData target, MoveType moveType, List path = null) { var isFrozen = IsFrozen(); var copy = new List(Skills); foreach (var skill in copy) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; skill.BeforeMove(this, target, map, moveType, path); } } // 移动后 public void OnMove(MapData map, GridData target, MoveType moveType, List path = null) { if (map == null || target == null || !IsValidOnMap(map, target)) return; var isFrozen = IsFrozen(); var copy = new List(Skills); foreach (var skill in copy) { if (!IsValidOnMap(map, target)) return; if (isFrozen && IsSkillFrozenFilter(skill)) continue; skill.OnMove(this, target, map,moveType, path); } if (!IsValidOnMap(map, target)) return; //赋予格子特殊效果,scarletEmpire的单位走到格子上时会得到被赋予一个技能realTimeVampire // 注意:多人允许相同 Empire 后,多名 PlayerCivId==0 玩家会同时享有此特权,符合预期保留逻辑。 if(target.HasSpType(GridSpType.RemiliaGrid) && target.RealUnit(map,out var unit) && unit.Player(map,out var player) && player.PlayerCivId == 0) AddSkill_Legacy(SkillType.ScarletMistRealTimeVampire, map,true,-1,false,-1,false,SpecialAddSkillType.Force,0); else if(GetSkill(SkillType.ScarletMistRealTimeVampire,out var _)) RemoveSkill(SkillType.ScarletMistRealTimeVampire,map); } // 成为移动目标时 public void OnBeInteractTarget(MapData map, UnitData origin, GridData target) { var isFrozen = IsFrozen(); var copy = new List(Skills); foreach (var skill in copy) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; skill.OnBeInteractTarget(origin, this, target, map); } } // 伤害结算前 public void BeforeDamagedSupportStage(MapData map, SettlementInfo info) { var isFrozen = IsFrozen(); var copy = new List(Skills); foreach (var skill in copy) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; skill.BeforeDamagedSupportStage(map, info); } } // 伤害结算前 public void BeforeDamagedTransformStage(MapData map, SettlementInfo info) { var isFrozen = IsFrozen(); var copy = new List(Skills); foreach (var skill in copy) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; skill.BeforeDamagedTransformStage(map, info); } } // 对他人攻击前 public void BeforeActiveAttackOther(MapData mapData, UnitData origin, UnitData target,out int addDmg) { addDmg = 0; if (mapData == null || origin == null || target == null) return; if (!origin.IsValidOnMap(mapData) || !target.IsValidOnMap(mapData)) return; var isFrozen = IsFrozen(); var copy = new List(Skills); foreach (var skill in copy) { if (!origin.IsValidOnMap(mapData) || !target.IsValidOnMap(mapData)) return; if (isFrozen && IsSkillFrozenFilter(skill)) continue; skill.BeforeActiveAttackOther(mapData, origin, target, out var tmpAddDmg); addDmg += tmpAddDmg; } } // 对他人攻击后 public void AfterActiveAttackOther(MapData mapData, AttackInfo attackInfo) { var isFrozen = IsFrozen(); var copy = new List(Skills); foreach (var skill in copy) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; skill.AfterActiveAttackOther(mapData, attackInfo); } } // 受他人攻击后 public void AfterActiveAttacked(MapData mapData, AttackInfo attackInfo) { var isFrozen = IsFrozen(); var copy = new List(Skills); foreach (var skill in copy) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; skill.AfterActiveAttacked(mapData, attackInfo); } } // 对他人伤害结算前 public void BeforeDamageOther(MapData map, SettlementInfo info) { var isFrozen = IsFrozen(); var copy = new List(Skills); foreach (var skill in copy) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; skill.BeforeDamageOther(map, info); } } // 伤害结算时 public void OnDamaged(MapData mapData, SettlementInfo info) { var isFrozen = IsFrozen(); var copy = new List(Skills); foreach (var skill in copy) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; skill.OnDamaged(mapData, info); } } // 消失前 public void BeforeDisappear(MapData mapData) { var isFrozen = IsFrozen(); var copy = new List(Skills); foreach (var skill in copy) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; skill.BeforeDisappear(mapData, this); } } // 对他人伤害结算时 public void OnDamageOther(MapData mapData, SettlementInfo info) { var isFrozen = IsFrozen(); var copy = new List(Skills); foreach (var skill in copy) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; skill.OnDamageOther(mapData, info); } } // 对他人伤害结算后 public void AfterDamageOther(MapData mapData, SettlementInfo info) { var isFrozen = IsFrozen(); var copy = new List(Skills); foreach (var skill in copy) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; skill.AfterDamageOther(mapData, info); } } // 对他人治疗结算时 public void OnHealOther(MapData mapData, UnitData target,HealType healType) { var isFrozen = IsFrozen(); var copy = new List(Skills); foreach (var skill in copy) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; skill.OnHealOther(mapData,this,target, healType); } } // 治疗前修改回复量(被治疗者视角) public int BeforeHealSelf(MapData mapData, UnitData origin, HealType healType, int recover) { var isFrozen = IsFrozen(); var copy = new List(Skills); foreach (var skill in copy) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; recover = skill.BeforeHealSelf(mapData, this, origin, healType, recover); } return recover; } // AttackAlly 准备治疗前(Healer 视角) public void OnAttackAllyJustBeforeHeal(MapData mapData, UnitData target, HealType healType) { var isFrozen = IsFrozen(); var copy = new List(Skills); foreach (var skill in copy) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; skill.OnAttackAllyJustBeforeHeal(mapData, this, target, healType); } } // 对他人治疗结算后(Healer 视角) public void OnAttackAllyAfterHeal(MapData mapData, UnitData target, HealType healType, int realHealed, int overflow) { var isFrozen = IsFrozen(); var copy = new List(Skills); foreach (var skill in copy) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; skill.OnAttackAllyAfterHeal(mapData, this, target, healType, realHealed, overflow); } } //获取抵达某格所需要的movecostinfo下限,目前仅用于momijiHUnter,也就是说moveinfo>该值才 public float GetGridMoveFloor(MapData mapData, UnitData originUnit,GridData targetGrid) { var isFrozen = IsFrozen(); var copy = new List(Skills); float ret = 0f; foreach (var skill in copy) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; ret = Mathf.Max(ret,skill.GetGridMoveFloor(mapData,originUnit,targetGrid)); } return ret; } //获取自身占据的population数额 public int GetPopulation() { if (GetSkill(SkillType.NOPOPULATION, out var _)) return 0; return 1; } public int GetKillCount() { int ret = 0; var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; ret += skill.GetKillCount(); } return ret; } //判断是否被某人视作英雄 public bool TreatedAsHero(MapData map,UnitData unit) { return TreatedAsHero(map, unit, out _); } public bool TreatedAsHero(MapData map, UnitData unit, out GiantType giantType) { giantType = GiantType.None; if (UnitFullType.UnitType == UnitType.Giant) { giantType = UnitFullType.GiantType; return true; } if (CarryUnitFullType.UnitType == UnitType.Giant) { giantType = CarryUnitFullType.GiantType; return true; } var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsTreatAsHero(map, this, unit, out giantType)) return true; } return false; } // 判断是否只能在我方领土内移动 public bool IsLimitMoveToSelfTerrain(MapData map) { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsLimitMoveToSelfTerrain(this, map)) return true; } return false; } // 判断是否限制行动点 public bool IsLimitActionPoint() { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsLimitActionPoint(this)) return true; } return false; } // 是否无视ZOC public bool IsIgnoreZOC() { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsIgnoreZOC()) return true; } return false; } // 是否无视森林(Trees 植被)带来的移动减损 —— BAMBOOMOVE / CREEP 等技能聚合 public bool IsIgnoreForestMoveDebuff(MapData mapData) { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (skill.IsIgnoreForestMoveDebuff(this, mapData)) return true; } return false; } // 是否具备ZOC控制能力 public bool HasZOC() { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; if (!skill.HasZOC()) return false; } return true; } #endregion //------------------------------------------------- 对unitData的基础操作 ---------------------------------------------- //清空 GetActionPoint(ActionPointType.Attack) Cp GetActionPoint(ActionPointType.Move) public void ClearActionPoint() { ActionPoint.Clear(); var map = Main.MapData; if (map == null) return; // 用副本遍历:技能内部可能 RemoveSkill 改动 Skills 集合 var snapshot = new List(Skills); foreach (var skill in snapshot) skill?.OnClearActionPoint(map, this); } public void SetFullActionPoint() { ActionPoint.Clear(); ActionPoint[ActionPointType.Common] = 1; } public void SetFullActionPoint_AllSkillRefresh() { SetFullActionPoint(); var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; skill.OnRefresh(); } } public int AddHealth(int hp) { var offset = Math.Min(GetMaxHealth() - Health, hp); Health += offset; return offset; } public int AddHealth(float hp) { return AddHealth(CeilPositiveToInt(hp)); } public void SetOfficer() { var isFrozen = IsFrozen(); foreach (var skill in Skills) { if (isFrozen && IsSkillFrozenFilter(skill)) continue; skill.SetOfficer(); } } public bool IsFrozen() { foreach (var skill in Skills) { if (skill.IsAllSkillBan()) return true; } return false; } //用于筛选哪些skill能被ban 哪些不能被ban public bool IsSkillFrozenFilter(SkillBase skill) { return skill.SkillPriority == SkillPriority.Normal; return false; } public bool IsSkillFrozenForSkillBase(SkillBase skill) { return IsFrozen() && IsSkillFrozenFilter(skill); } /// /// 判断单位是否拥有且正在生效某个技能。 /// 与 GetSkill 的区别是:GetSkill 只判断“是否持有”,本方法会额外排除被全技能封禁(Force)的技能。 /// /// 目标技能类型 /// 返回技能实例 /// true=拥有且生效;false=未拥有或已被封禁 public bool HasEffectiveSkill(SkillType skillType, out SkillBase skill) { skill = null; if (!GetSkill(skillType, out var foundSkill)) return false; if (IsSkillFrozenForSkillBase(foundSkill)) return false; skill = foundSkill; return true; } // 增加 public void AddActionPoint(ActionPointType actionPointType) { if (IsLimitActionPoint()) return; ActionPoint.TryAdd(actionPointType, 0); ActionPoint[actionPointType]++; } // 获取 public int GetActionPoint(ActionPointType actionPointType) { if (IsLimitActionPoint()) return 0; if (ActionPoint.ContainsKey(actionPointType) && ActionPoint[actionPointType] > 0) return ActionPoint[actionPointType]; return ActionPoint.GetValueOrDefault(ActionPointType.Common, 0); } // 减少 public void ReduceActionPoint(ActionPointType actionPointType) { if (IsLimitActionPoint()) return; _actionCache.TryAdd(actionPointType, 0); _actionCache.TryAdd(ActionPointType.Common, 0); if (ActionPoint.ContainsKey(actionPointType) && ActionPoint[actionPointType] > 0) { ActionPoint[actionPointType]--; _actionCache[actionPointType]++; return; } ActionPoint.TryAdd(ActionPointType.Common, 0); ActionPoint[ActionPointType.Common]--; _actionCache[ActionPointType.Common]++; } //判断是否拥有某种行动点 public bool HasActionPoint(ActionPointType actionPointType) { if (IsLimitActionPoint()) return false; if (ActionPoint.ContainsKey(actionPointType) && ActionPoint[actionPointType] > 0) return true; return ActionPoint[ActionPointType.Common] > 0; } } }