820 lines
26 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月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 TH1Renderer;
using Unity.VisualScripting;
using UnityEngine;
namespace RuntimeData
{
// 地图上所有的小兵数据
[MemoryPackable]
public partial class UnitMapData
{
public List<UnitData> UnitList;
[MemoryPackIgnore]
public bool UnitMapRenderMark;
private Dictionary<uint, UnitData> _unitDict;
[MemoryPackConstructor]
public UnitMapData()
{
UnitList = new List<UnitData>();
_unitDict = new Dictionary<uint, UnitData>();
UnitMapRenderMark = true;
}
public UnitMapData(UnitMapData copyData)
{
UnitList = new List<UnitData>();
_unitDict = new Dictionary<uint, UnitData>();
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<uint, UnitData>();
_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<UnitData>();
map.GetUnitDataListByPlayerId(player.Id, units);
foreach (var unit in units) unit.OnTurnStart(map);
}
public void OnTurnEnd(MapData map, PlayerData player)
{
var units = new HashSet<UnitData>();
map.GetUnitDataListByPlayerId(player.Id, units);
foreach (var unit in units) 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<UnitFullType>
{
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;
//单位类型
public UnitFullType UnitFullType;
// 单位类型(legacy)
public UnitType UnitType ;
public GiantType GiantType;
public uint UnitLevel;
//棋子种类
public ChessType ChessType;
//TODO 将来要彻底干掉legacy单位类型
public UnitFullType GetUnitFullType => new UnitFullType {UnitType = UnitType,GiantType = GiantType,UnitLevel = UnitLevel };
// 血量
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;
//Attack信息的RenderMakr
[MemoryPackIgnore]
public bool AttackRenderMark;
[MemoryPackIgnore]
public Vector3 AttackRenderMarkTargetPos;
[MemoryPackIgnore]
public bool AttackRenderMarkNeedBack;
[MemoryPackIgnore]
public AttackAnimType AttackRenderMarkAttackAnimType;
[MemoryPackIgnore]
public bool BounceRenderMark;
[MemoryPackIgnore]
public float BounceRenderMakrWaitTime;
public PlayerData Player(MapData map)
{
map.GetPlayerDataByUnitId(this.Id, out var player);
return 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 HeroTaskContentBase HeroTask(MapData map)
{
HeroTaskContentBase heroTask = null;
Player(map)?.PlayerHeroData.HeroTaskDict.TryGetValue(GetUnitFullType,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 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();
}
// 获取军事分
public float GetMilitary()
{
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType, GiantType,UnitLevel, out var info))
return 0;
//如果是英雄,要单独结算,按照英雄的不同等级定价结算
if (UnitType == UnitType.Giant)
return UnitLevel switch
{
0 => 8,
1 => 11,
2 => 13,
3 => 15,
4 => 20,
5 => 30,
_ => 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();
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();
foreach (var skill in Skills)skill.OnTurnEnd(this, map);
AP = 0;
CP = 0;
MP = 0;
}
//返回该unit的视野半径
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;
}
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;
}
// 获得总攻击力的string显示
public string GetAttackShowString(MapData map)
{
if(!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType, GiantType,UnitLevel, out var info)) return "0";
float extra = GetExtraAttack(map);
if (extra == 1f)
return info.Attack.ToString();
return info.Attack + "(×" + extra + ")";
}
// 获得总攻击力
public float GetAttackValue(MapData map, UnitData target=null)
{
if(!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType, GiantType,UnitLevel, out var info)) return 0;
if (IsCriticalHitRate(map)) return info.Attack * GetExtraAttack(map) * 2;
var value = info.Attack * GetExtraAttack(map);
if (target != null)
{
var attackOffsetValue = GetAttackerValueOffset(map,target);
if (GetSkill(SkillType.EIRINFRENCHBUFF, out var _) && target.GetUnitFullType.UnitType == UnitType.Giant)
attackOffsetValue *= 2;
value += attackOffsetValue;
}
return value * GetAllExtraAttack(map);
}
// 获取总防御力的string显示
public string GetDefenseShowString(MapData map)
{
if(!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType, GiantType,UnitLevel, out var info)) return "0";
float extra = GetExtraDefense(map);
if (extra == 1f)
return info.Defense.ToString();
return info.Defense + "(×" + extra + ")";
}
// 获取总防御力
public float GetDefenseValue(MapData map, UnitData origin=null)
{
if(!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType, GiantType,UnitLevel, out var info)) return 0;
var value = info.Defense * GetExtraDefense(map);
if (origin != null) value += origin.GetDefenderValueOffset(map,origin);
return value;
}
public float GetRawDefenseValue()
{
if(!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType, GiantType,UnitLevel, out var info)) return 0;
var value = info.Defense;// * GetExtraDefense(map);
//if (origin != null) value += origin.GetDefenderValueOffset(map,origin);
return value;
}
// 获取防御倍数
public float GetExtraDefense(MapData map)
{
var extraDefense = 1f;
foreach (var skill in Skills)
{
extraDefense *= skill.GetExtraDefense(this, map);
}
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 GetExtraAttack(MapData map)
{
var extraAttack = 1f;
foreach (var skill in Skills) extraAttack *= skill.GetExtraAttack(this, map);
return extraAttack;
}
// 获取总攻击倍数
public float GetAllExtraAttack(MapData map)
{
var extraAttack = 1f;
foreach (var skill in Skills) extraAttack *= skill.GetAllExtraAttack(this, map);
return extraAttack;
}
// 获取暴击率
public float GetCriticalHitRate(MapData map)
{
var criticalHitRate = 0f;
foreach (var skill in Skills) criticalHitRate += skill.GetCriticalHitRate(this, map);
return criticalHitRate;
}
// 受攻击时给对方的攻击力偏移
public float GetAttackerValueOffset(MapData mapData,UnitData target)
{
var value = 0f;
foreach (var skill in Skills) value += skill.GetAttackerValueOffset(mapData,target);
return value;
}
// 攻击时给对方的防御力偏移
public float GetDefenderValueOffset(MapData mapData,UnitData origin)
{
var value = 0f;
foreach (var skill in Skills) value += skill.GetDefenderValueOffset(mapData,origin);
return value;
}
// 是否能移动到伟人身周
public bool IsCanMoveGiantNearbyGrid(MapData map)
{
foreach (var skill in Skills)
{
if (skill.IsCanMoveGiantNearbyGrid(this, map)) 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 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);
}
RemoveInActiveSkill();
}
// 伤害结算时
public void OnDamaged(MapData mapData, SettlementInfo info)
{
for (int i = Skills.Count - 1; i >= 0; i--) Skills[i].OnDamageOther(mapData, info);
RemoveInActiveSkill();
HeroTask(mapData)?.OnDamaged(mapData, info);
}
// 对他人伤害结算时
public void OnDamageOther(MapData mapData, SettlementInfo info)
{
for (int i = Skills.Count - 1; i >= 0; i--) Skills[i].OnDamaged(mapData, info);
RemoveInActiveSkill();
HeroTask(mapData)?.OnDamageOther(mapData, info);
}
//获取自身占据的population数额
public int GetPopulation()
{
if (GetSkill(SkillType.NOPOPULATION, out var _))
return 0;
return 1;
}
//------------------------------------------------- 对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;
}
}
}