/* * @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 Unity.VisualScripting; using UnityEngine; namespace RuntimeData { // 地图上所有的小兵数据 [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() { _unitDict ??= new Dictionary(); _unitDict.Clear(); foreach (var unit in UnitList) _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); } } public void OnTurnStart(MapData map, PlayerData player) { var units = new HashSet(); map.GetUnitDataListByPlayerId(player.Id, units); foreach (var unit in units) { Main.UnitLogic.StartNextTurn(map, unit); unit.OnTurnStart(map); } } public void OnTurnEnd(MapData map, PlayerData player) { var units = new HashSet(); map.GetUnitDataListByPlayerId(player.Id, units); foreach (var unit in units) { 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) { if (_unitDict.TryGetValue(unitId, out var unitData)) { UnitList.Remove(unitData); _unitDict.Remove(unitId); } } // 通过 uid 找小兵数据 unitData public bool GetUnitDataByUnitId(uint unitId, out UnitData unitData) { return _unitDict.TryGetValue(unitId, out unitData); } } [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); } } //小兵数据 [MemoryPackable] public partial class UnitData : IdentifierBase { // 是否存活 public bool Alive; //TODO 暂时使用,之后要干掉 数据层不存render信息,要在unitRenderer里存储 //public bool RenderMark; //单位类型 public UnitFullType UnitFullType => new UnitFullType(UnitType,GiantType,UnitLevel); // 单位类型(legacy) public UnitType UnitType ; public GiantType GiantType; public uint UnitLevel; //棋子种类 public ChessType ChessType; // 血量 public int Health; // move points,行动值 public int MP; // attack points,攻击值 public int AP; // capture city points, 城市行动值,用来占领或者建设城市 public int CP; // 攻击范围 //public int AttackRange; // 移动范围 //public int MoveRange; // 经验值 public int Exp; //英雄经验值 public int GiantExp; //是否老兵 public bool Veteran; //存储carry的UnitType public UnitType CarryUnitType; public GiantType CarryGiantType; public uint CarryUnitLevel; //存储carry的exp状态 public int CarryExp; //存储carry的Veteran状态 public bool CarryVeteran; // 防御削减系数 public float DefenseReduction; // 军团编号 public uint LegionId; 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 UnitRenderer Renderer(MapData map) { if (!map.IsCurrentShowMap()) return null; return MapRenderer.Instance.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() { if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType, GiantType,UnitLevel, out var unitTypeInfo)) return 0; if (unitTypeInfo.NoMaxHealth) { if (CarryUnitType != UnitType.None) { if(Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(CarryUnitType, CarryGiantType,CarryUnitLevel,out var carryUnitTypeInfo)) return carryUnitTypeInfo.MaxHealth + (CarryVeteran?5:0); return 0; } return 0; } else return unitTypeInfo.MaxHealth + (Veteran?5:0); } 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) { if (map != Main.MapData) return Vector3.zero; var t = MapRenderer.Instance.ROUnitMap[Id]; if(t == null)return Vector3.zero; return t.GetPosition(); } // 获取血量百分比 public float GetHealthRatio() { return (float)Health / GetMaxHealth(); } // 获取ProjectileType public ProjectileType GetProjectileType(int distance) { 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(UnitType, GiantType,UnitLevel, out var info)) return 0; //如果是英雄,要单独结算,按照英雄的不同等级定价结算 if (UnitType == UnitType.Giant) return UnitLevel switch { 0 => 10, 1 => 10, 2 => 15, 3 => 25, _ => 0 }; //如果生命值低于50%,得分打8折 if (GetHealthRatio() <= 0.5f) return info.Cost * 0.8f; return info.Cost; } // 获取造价 public float GetCost() { if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType, GiantType,UnitLevel, out var info)) return 0; return info.Cost; } [MemoryPackConstructor] public UnitData() { } //注意,这里没有处理unit Copy之后,skill要触发skillAdd生命周期的情况 public UnitData(UnitData copyData) { Id = copyData.Id; Alive = copyData.Alive; UnitType = copyData.UnitType; GiantType = copyData.GiantType; UnitLevel = copyData.UnitLevel; Health = copyData.Health; MP = copyData.MP; AP = copyData.AP; CP = copyData.CP; //AttackRange = copyData.AttackRange; //MoveRange = copyData.MoveRange; Exp = copyData.Exp; GiantExp = copyData.GiantExp; Veteran = copyData.Veteran; CarryUnitType = copyData.CarryUnitType; CarryGiantType = copyData.CarryGiantType; CarryUnitLevel = copyData.CarryUnitLevel; CarryVeteran = copyData.CarryVeteran; CarryExp = copyData.CarryExp; LegionId = copyData.LegionId; CarryVeteran = copyData.CarryVeteran; DefenseReduction = copyData.DefenseReduction; foreach (var skill in copyData.Skills) Skills.Add(skill.GetCopySkill()); } //注意,这里没有处理unit Copy之后,skill要触发skillAdd生命周期的情况 public void DeepCopy(UnitData copyData) { Id = copyData.Id; Alive = copyData.Alive; UnitType = copyData.UnitType; GiantType = copyData.GiantType; UnitLevel = copyData.UnitLevel; ChessType = copyData.ChessType; Health = copyData.Health; MP = copyData.MP; AP = copyData.AP; CP = copyData.CP; //AttackRange = copyData.AttackRange; //MoveRange = copyData.MoveRange; Exp = copyData.Exp; GiantExp = copyData.GiantExp; Veteran = copyData.Veteran; CarryUnitType = copyData.CarryUnitType; CarryGiantType = copyData.CarryGiantType; CarryExp = copyData.CarryExp; CarryUnitLevel = copyData.CarryUnitLevel; CarryVeteran = copyData.CarryVeteran; LegionId = copyData.LegionId; CarryVeteran = copyData.CarryVeteran; DefenseReduction = copyData.DefenseReduction; foreach (var skill in copyData.Skills) { AddSkill(skill.GetSkillType()); if (!GetSkill(skill.GetSkillType(), out var selfSkill)) continue; selfSkill.DeepCopy(skill); } for (int i = Skills.Count - 1; i >= 0; i--) { if (copyData.GetSkill(Skills[i].GetSkillType(), out _)) continue; Skills.RemoveAt(i); } } // 无参数初始化 public UnitData(UnitFullType unitFullType, MapIdGenerator idGenerator) { Id = idGenerator.GeneratorId(); //UnitFullType = unitFullType; UnitType = unitFullType.UnitType; GiantType = unitFullType.GiantType; UnitLevel = unitFullType.UnitLevel; InitData(); } public void InitData() { if (Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType, GiantType,UnitLevel, out var unitTypeInfo) && unitTypeInfo.NoMaxHealth) { //TODO 目前 nomaxhealth和carry不是一个概念 CarryUnitType = UnitType.Warrior; } Alive = true; Health = GetMaxHealth(); ChessType = unitTypeInfo.ChessType; MP = 0; AP = 0; CP = 0; //AttackRange = Table.Instance.QueryUnitAttackRange(UnitType); //MoveRange = Table.Instance.QueryUnitMoveRange(UnitType); Exp = 0; GiantExp = 0; Veteran = false; LegionId = 0; } // 全局通知调用 public void OnTurnStart(MapData map) { RemoveInActiveSkill(map); for (int i = Skills.Count - 1; i >= 0; i--) { var skill = Skills[i]; if (skill.IsFinished()) { skill.OnFinished(this, map); Skills.RemoveAt(i); continue; } skill.OnTurnStart(this, map); } } public void OnTurnEnd(MapData map) { RemoveInActiveSkill(map); foreach (var skill in Skills)skill.OnTurnEnd(this, map); AP = 0; CP = 0; MP = 0; } //返回该unit的视野半径(考虑unit站在map的实机位置下的视野半径) public int GetSightRange(MapData map) { int v = 1; foreach (var skill in Skills) 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; foreach (var skill in Skills) v += skill.GetExtraSight(this,map); if (grid.Feature == TerrainFeature.Mountain) v = Mathf.Max(v, 2); return v; } public int GetAttackRange() { var attackRange = 0; foreach (var skill in Skills) { attackRange += skill.GetExtraAttackRange(this); } attackRange += Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType, GiantType, UnitLevel, out var info) ? info.AttackRange : 0; return attackRange; } public int GetMoveRange() { if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType, GiantType, UnitLevel, out var info)) return 0; var moveRange = info.MoveRange; foreach (var skill in Skills) { moveRange += skill.GetExtraMoveRange(this); } return moveRange; } //返回该unit是否是静止经验(无法升级) public bool CheckIsStaticExp(MapData map) { var isStaticExp = false; foreach (var skill in Skills) { 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(UnitType,GiantType,UnitLevel,out var Info)?Info.LandType:LandType.None; } // 获取技能加成倍数 (本人拥有的其他技能对特定技能的加成倍数) public float GetSpecialSkillBonus(MapData mapData, SkillType skillType) { var value = 1f; foreach (var skill in Skills) 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.ToString(); if (mul != 1f) ret += " (× " + mul.ToString() + ")"; return ret; } public float GetRawAttackValue(MapData map, UnitData target = null) { if(!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType, GiantType,UnitLevel, out var info)) return 0; return info.Attack; } // 基础攻击力 public float GetBaseAttackValue(MapData map, UnitData target = null) { if(!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType, GiantType,UnitLevel, 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; foreach (var skill in Skills) value += skill.GetAttackAdditionParam(map, this, target); return value; } // 攻击力乘算系数 public float GetAttackMultiplicationParam(MapData map, UnitData target = null) { var value = 1f; foreach (var skill in Skills) 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.ToString(); if (mul != 1f) ret += " (× " + mul.ToString() + ")"; return ret; } public float GetRawDefenseValue(MapData map, UnitData target = null) { if(!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType, GiantType,UnitLevel, out var info)) return 0; return info.Defense; } // 基础防御力 public float GetBaseDefenseValue(MapData map, UnitData target = null) { if(!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType, GiantType,UnitLevel, 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; foreach (var skill in Skills) value += skill.GetDefenseAdditionParam(map, this, target); return value; } // 防御力乘算系数 public float GetDefenseMultiplicationParam(MapData map, UnitData target = null) { var value = 1f; var isZero = 1f; foreach (var skill in Skills) { var t = skill.GetDefenseMultiplicationParam(map, this, target); value = Mathf.Max(t,value); isZero *= t; } var p = GetTerrainDefenseMultiplicationParam(map); value = Mathf.Max(p,value); isZero *= p; if (isZero == 0f) return 0f; return value; } // 地形防御乘算系数 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) extraDefense *= 1.5f; return extraDefense; } // 获取暴击率 public float GetCriticalHitRate(MapData map) { var criticalHitRate = 0f; foreach (var skill in Skills) criticalHitRate += skill.GetCriticalHitRate(this, map); return criticalHitRate; } // 是否能移动到伟人身周 public bool IsCanMoveGiantNearbyGrid(MapData map) { foreach (var skill in Skills) { if (skill.IsCanMoveGiantNearbyGrid(this, map)) return true; } return false; } // 是否movekill public bool IsCanMoveKill(MapData map) { if (GetAttackRange() == 1) return true; return false; } // 是否拥有特别伤害技能,例如ROYALFLAME public bool IsHasAttackSkill(MapData map) { return false; } // 是否能移动无人城市 public bool IsCanMoveNoUnitSelfCity(MapData map) { foreach (var skill in Skills) { if (skill.IsCanMoveToNoUnitSelfCity(this, map)) return true; } return false; } // 是否可以死亡 public bool CanBeKilled(MapData map) { foreach (var skill in Skills) { if (!skill.IsCanBeKill(this, map)) return false; } return true; } // 是否可以被伤害结算 public bool CanBeDamaged(MapData map, int dmg) { foreach (var skill in Skills) { if (skill.IsCanBeDamaged(this, map, dmg)) return true; } return false; } // 是否经验锁定 public bool IsExpLock(MapData map) { foreach (var skill in Skills) { 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; foreach (var skill in Skills) { 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; foreach (var skill in Skills) { if (skill.IsCanMoveOnFeature(this, map,featureType)) return true; } return false; } //返回该unit是否能攻击所有人 public bool CanAttackAll(MapData map) { foreach (var skill in Skills) { if (skill.CanAttackAll(this, map)) return true; } return false; } //返回该unit是否能攻击 public bool IsLimitSelfAttack(MapData map) { foreach (var skill in Skills) { if (skill.IsLimitSelfAttack(this, map)) return true; } return false; } //返回该unit是否能反击 public bool IsLimitSelfCounterAttack(MapData map) { foreach (var skill in Skills) { if (skill.IsLimitSelfCounterAttack(this, map)) return true; } return false; } //返回目标 unit 是否能反击 public bool IsLimitTargetCounterAttack(MapData map) { foreach (var skill in Skills) { if (skill.IsLimitTargetCounterAttack(this, map)) return true; } return false; } // 是否能暴击 (移除了) private bool IsCriticalHitRate(MapData map) { return map.Net.GetRandom().NextDouble() <= GetCriticalHitRate(map); } // 移动后的变化 public void OnMove(MapData map, GridData target, MoveType moveType) { for (int i = Skills.Count - 1; i >= 0; i--) { Skills[i].OnMove(this, target, map,moveType); } //赋予格子特殊效果 if(target.HasSpType(GridSpType.RemiliaGrid)) AddSkill(SkillType.ScarletMistRealTimeVampire); else if(GetSkill(SkillType.ScarletMistRealTimeVampire,out var _)) RemoveSkill(SkillType.ScarletMistRealTimeVampire,map); RemoveInActiveSkill(map); } // 伤害结算前 public void BeforeDamaged(MapData map, SettlementInfo info) { for (int i = Skills.Count - 1; i >= 0; i--) Skills[i].BeforeDamaged(map, info); RemoveInActiveSkill(map); } // 对他人伤害结算前 public void BeforeDamageOther(MapData map, SettlementInfo info) { for (int i = Skills.Count - 1; i >= 0; i--) Skills[i].BeforeDamageOther(map, info); RemoveInActiveSkill(map); } // 伤害结算时 public void OnDamaged(MapData mapData, SettlementInfo info) { for (int i = Skills.Count - 1; i >= 0; i--) Skills[i].OnDamaged(mapData, info); RemoveInActiveSkill(mapData); HeroTask(mapData)?.OnDamaged(mapData, info); } // 对他人伤害结算时 public void OnDamageOther(MapData mapData, SettlementInfo info) { for (int i = Skills.Count - 1; i >= 0; i--) Skills[i].OnDamageOther(mapData, info); RemoveInActiveSkill(mapData); HeroTask(mapData)?.OnDamageOther(mapData, info); } //获取自身占据的population数额 public int GetPopulation() { if (GetSkill(SkillType.NOPOPULATION, out var _)) return 0; return 1; } //判断是否被某人视作英雄 public bool TreatedAsHero(MapData map,UnitData unit) { if (UnitFullType.UnitType == UnitType.RemiliaEgyptianKoakuma) { if (unit.Player(map) != Player(map)) return false; return true; } if (UnitFullType.UnitType == UnitType.Giant) return true; if (CarryUnitType == UnitType.Giant) return true; return false; } //------------------------------------------------- 对unitData的基础操作 ---------------------------------------------- //清空 AP Cp MP public void ClearAPMPCP() { AP = 0; MP = 0; CP = 0; } public void SetFullAPCPMP() { AP = 1; MP = 1; CP = 1; } public int AddHealth(int hp) { var offset = Math.Min(GetMaxHealth() - Health, hp); Health += offset; return offset; } } }