2025-09-08 11:15:37 +08:00

658 lines
21 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.CrashSight;
using Logic.Skill;
using MemoryPack;
using TH1_Logic.Core;
using TH1_Logic.HeroTask;
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(UnitType unitType,GiantType giantType,uint unitLevel, MapIdGenerator idGenerator)
{
var unit = new UnitData(unitType, giantType,unitLevel ,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);
}
}
//小兵数据
[MemoryPackable]
public partial class UnitData : IdentifierBase
{
private static System.Random _random;
// 是否存活
public bool Alive;
// 单位类型
public UnitType UnitType;
public GiantType GiantType;
public uint UnitLevel;
// 任务
public HeroTaskContentBase TaskContent;
//棋子种类
public ChessType ChessType;
// 实际的血量值
[MemoryPackInclude]
protected int UnitHealth;
// 血量
public int Health
{
get => UnitHealth;
set
{
var offset = value - UnitHealth;
if (offset > 0) TaskContent.OnHealthReturn(Main.MapData, offset);
UnitHealth = value;
}
}
// 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 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 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()
{
}
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());
}
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(UnitType unitType,GiantType giantType,uint unitLevel, MapIdGenerator idGenerator)
{
Id = idGenerator.GeneratorId();
UnitType = unitType;
GiantType = giantType;
UnitLevel = 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)
{
for (int i = Skills.Count - 1; i >= 0; i--)
{
var skill = Skills[i];
if (skill.IsFinished())
{
Skills.RemoveAt(i);
continue;
}
skill.OnTurnStart(this, map);
}
}
public void OnTurnEnd(MapData map)
{
foreach (var skill in Skills)skill.OnTurnEnd(this, map);
AP = 0;
CP = 0;
MP = 0;
}
//返回该unit的视野半径
public int GetSightRange()
{
if(GetSkill(SkillType.SCOUT,out var _))
return 2;
return 1;
}
public int GetAttackRange()
{
return Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType,GiantType,UnitLevel,out var info)?info.AttackRange:0;
}
public int GetMoveRange()
{
return Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType,GiantType,UnitLevel,out var info)?info.MoveRange:0;
}
//返回该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)
{
if(!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType, GiantType,UnitLevel, out var info)) return 0;
if (IsCriticalHitRate(map)) return info.Attack * GetExtraAttack(map) * 2;
return info.Attack * GetExtraAttack(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)
{
if(!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType, GiantType,UnitLevel, out var info)) return 0;
return info.Defense * GetExtraDefense(map);
}
// 获取防御倍数
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 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;
}
// 是否拥有特别伤害技能例如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)
{
if (_random == null) _random = new System.Random();
return _random.NextDouble() <= GetCriticalHitRate(map);
}
// 移动后的变化
public void OnMove(MapData map, GridData target, MoveType moveType)
{
foreach (var skill in Skills)
skill.OnMove(this, target, map,moveType);
}
//------------------------------------------------- 对unitData的基础操作 ----------------------------------------------
//清空 AP Cp MP
public void ClearAPMPCP()
{
AP = 0;
MP = 0;
CP = 0;
}
public void AddHealth(int hp)
{
Health += Math.Min(GetMaxHealth() - Health, hp);
}
}
}