1171 lines
39 KiB
C#
1171 lines
39 KiB
C#
/*
|
||
* @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
|
||
{
|
||
// 地图上所有的小兵数据
|
||
[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)
|
||
{
|
||
Main.UnitLogic.StartNextTurn(map, unit);
|
||
unit.OnTurnStart(map);
|
||
}
|
||
}
|
||
|
||
public void OnAfterTurnStart(MapData map, PlayerData player)
|
||
{
|
||
var units = new HashSet<UnitData>();
|
||
map.GetUnitDataListByPlayerId(player.Id, units);
|
||
foreach (var unit in units)
|
||
{
|
||
unit.OnAfterTurnStart(map);
|
||
}
|
||
}
|
||
|
||
public void OnTurnEnd(MapData map, PlayerData player)
|
||
{
|
||
var units = new HashSet<UnitData>();
|
||
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<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);
|
||
}
|
||
|
||
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;
|
||
|
||
// move points,行动值
|
||
public int MP;
|
||
// attack points,攻击值
|
||
public int AP;
|
||
// capture city points, 城市行动值,用来占领或者建设城市
|
||
public int CP;
|
||
// 攻击范围
|
||
//英雄经验值
|
||
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 void SetAlive() { _notSetDie = true; }
|
||
|
||
public void SetDie() { _notSetDie = false; }
|
||
public bool IsAlive()
|
||
{
|
||
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 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(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)
|
||
{
|
||
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(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
|
||
{
|
||
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(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;
|
||
MP = copyData.MP;
|
||
AP = copyData.AP;
|
||
CP = copyData.CP;
|
||
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());
|
||
}
|
||
|
||
//注意,这里没有处理unit Copy之后,skill要触发skillAdd生命周期的情况
|
||
public void DeepCopy(UnitData copyData)
|
||
{
|
||
Id = copyData.Id;
|
||
_notSetDie = copyData._notSetDie;
|
||
UnitFullType = copyData.UnitFullType;
|
||
ChessType = copyData.ChessType;
|
||
Health = copyData.Health;
|
||
MP = copyData.MP;
|
||
AP = copyData.AP;
|
||
CP = copyData.CP;
|
||
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());
|
||
}
|
||
|
||
// 无参数初始化
|
||
public UnitData(UnitFullType unitFullType, MapIdGenerator idGenerator)
|
||
{
|
||
Id = idGenerator.GeneratorId();
|
||
UnitFullType = unitFullType;
|
||
InitData();
|
||
}
|
||
|
||
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;
|
||
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 bool IsBigGuy(bool useToCheckDie = false)
|
||
{
|
||
if(UnitType == UnitType.BigGuy) return true;
|
||
if(UnitType == UnitType.KaguyaFrenchWolf) 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);
|
||
}
|
||
|
||
public void OnAfterTurnStart(MapData map)
|
||
{
|
||
OnSkillsAfterTurnStart(map);
|
||
}
|
||
|
||
public void OnTurnEnd(MapData map)
|
||
{
|
||
OnSkillsTurnEnd(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(MapData mapData)
|
||
{
|
||
var attackRange = 0;
|
||
foreach (var skill in Skills)
|
||
{
|
||
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)
|
||
{
|
||
foreach (var skill in Skills)
|
||
{
|
||
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;
|
||
foreach (var skill in Skills)
|
||
{
|
||
moveRange += skill.GetExtraMoveRange(mapData,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(UnitFullType,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 > 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;
|
||
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 > 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;
|
||
foreach (var skill in Skills)
|
||
{
|
||
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;
|
||
foreach (var skill in Skills)
|
||
{
|
||
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;
|
||
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 || grid.Resource == ResourceType.RemiliaMilitary) 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;
|
||
}
|
||
|
||
|
||
#region [------------------------------ Skill 相关 -----------------------------------]
|
||
|
||
//获取officer的额外血量
|
||
public int GetOfficerHealth()
|
||
{
|
||
int ret = 0;
|
||
foreach (var skill in Skills)
|
||
ret += skill.GetOfficerHealth();
|
||
return ret;
|
||
}
|
||
|
||
//判断当前是不是officer
|
||
public bool IsOfficer()
|
||
{
|
||
foreach (var skill in Skills)
|
||
if (skill.IsOfficer()) return true;
|
||
return false;
|
||
}
|
||
|
||
public bool IsPrepareOfficer()
|
||
{
|
||
foreach (var skill in Skills)
|
||
if (skill.IsPrepareOfficer()) return true;
|
||
return false;
|
||
|
||
}
|
||
|
||
public bool IsCanBeOfficer()
|
||
{
|
||
foreach (var skill in Skills)
|
||
if (skill.IsCanBeOfficer()) return true;
|
||
return false;
|
||
}
|
||
|
||
// 是否能移动到伟人身周
|
||
public bool IsCanMoveGiantNearbyGrid(MapData map)
|
||
{
|
||
foreach (var skill in Skills)
|
||
{
|
||
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)
|
||
{
|
||
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是否能攻击ground
|
||
public bool IsCanAttackTargetGrid(MapData map, GridData target)
|
||
{
|
||
foreach (var skill in Skills)
|
||
{
|
||
if (skill.IsCanAttackTargetGrid(map,this,target)) return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
//返回该unit是否能攻击Ally
|
||
public bool IsCanAttackAlly()
|
||
{
|
||
foreach (var skill in Skills)
|
||
{
|
||
if (skill.IsCanAttackAlly()) return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
//返回该unit是否能攻击ground
|
||
public bool IsCanAttackTargetAlly(MapData map, UnitData target)
|
||
{
|
||
foreach (var skill in Skills)
|
||
{
|
||
if (!skill.IsCanAttackTargetAlly(map,this,target)) return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
//返回该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;
|
||
}
|
||
|
||
public bool IsCanTransport()
|
||
{
|
||
foreach (var skill in Skills)
|
||
{
|
||
if (skill.IsCanTransport()) return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// 移动后的变化
|
||
public void OnMove(MapData map, GridData target, MoveType moveType, List<Vector2Int> path = null)
|
||
{
|
||
var copy = new List<SkillBase>(Skills);
|
||
foreach (var skill in copy) skill.OnMove(this, target, map,moveType, path);
|
||
//赋予格子特殊效果
|
||
if(target.HasSpType(GridSpType.RemiliaGrid) && target.Unit(map,out var unit) && unit.Player(map,out var player) && player.PlayerCivId == 0)
|
||
AddSkill(SkillType.ScarletMistRealTimeVampire, map,true,-1,false,-1,false,AddSkillType.Force,0);
|
||
else
|
||
if(GetSkill(SkillType.ScarletMistRealTimeVampire,out var _))
|
||
RemoveSkill(SkillType.ScarletMistRealTimeVampire,map);
|
||
}
|
||
|
||
// 伤害结算前
|
||
public void BeforeDamagedSupportStage(MapData map, SettlementInfo info)
|
||
{
|
||
var copy = new List<SkillBase>(Skills);
|
||
foreach (var skill in copy) skill.BeforeDamagedSupportStage(map, info);
|
||
}
|
||
|
||
// 伤害结算前
|
||
public void BeforeDamagedTransformStage(MapData map, SettlementInfo info)
|
||
{
|
||
var copy = new List<SkillBase>(Skills);
|
||
foreach (var skill in copy) skill.BeforeDamagedTransformStage(map, info);
|
||
}
|
||
|
||
// 对他人攻击前
|
||
public void BeforeActiveAttackOther(MapData mapData, UnitData origin, UnitData target,out int addDmg)
|
||
{
|
||
addDmg = 0;
|
||
var copy = new List<SkillBase>(Skills);
|
||
foreach (var skill in copy)
|
||
{
|
||
skill.BeforeActiveAttackOther(mapData, origin, target, out var tmpAddDmg);
|
||
addDmg += tmpAddDmg;
|
||
}
|
||
}
|
||
|
||
// 对他人攻击后
|
||
public void AfterActiveAttackOther(MapData mapData, UnitData origin, UnitData target)
|
||
{
|
||
var copy = new List<SkillBase>(Skills);
|
||
foreach (var skill in copy) skill.AfterActiveAttackOther(mapData, origin, target);
|
||
}
|
||
|
||
// 受他人攻击后
|
||
public void AfterActiveAttacked(MapData mapData, UnitData origin, UnitData target)
|
||
{
|
||
var copy = new List<SkillBase>(Skills);
|
||
foreach (var skill in copy) skill.AfterActiveAttacked(mapData, origin, target);
|
||
}
|
||
|
||
// 对他人伤害结算前
|
||
public void BeforeDamageOther(MapData map, SettlementInfo info)
|
||
{
|
||
var copy = new List<SkillBase>(Skills);
|
||
foreach (var skill in copy) skill.BeforeDamageOther(map, info);
|
||
}
|
||
|
||
// 伤害结算时
|
||
public void OnDamaged(MapData mapData, SettlementInfo info)
|
||
{
|
||
var copy = new List<SkillBase>(Skills);
|
||
foreach (var skill in copy) skill.OnDamaged(mapData, info);
|
||
}
|
||
|
||
// 对他人伤害结算时
|
||
public void OnDamageOther(MapData mapData, SettlementInfo info)
|
||
{
|
||
var copy = new List<SkillBase>(Skills);
|
||
foreach (var skill in copy) skill.OnDamageOther(mapData, info);
|
||
}
|
||
|
||
// 对他人伤害结算后
|
||
public void AfterDamageOther(MapData mapData, SettlementInfo info)
|
||
{
|
||
var copy = new List<SkillBase>(Skills);
|
||
foreach (var skill in copy) skill.AfterDamageOther(mapData, info);
|
||
}
|
||
|
||
// 对他人治疗结算时
|
||
public void OnHealOther(MapData mapData, UnitData target,HealType healType)
|
||
{
|
||
var copy = new List<SkillBase>(Skills);
|
||
foreach (var skill in copy) skill.OnHealOther(mapData,this,target, healType);
|
||
//HeroTask(mapData)?.OnDamageOther(mapData, info);
|
||
//for (int i = Skills.Count - 1; i >= 0; i--) Skills[i].OnHealOther(mapData, info);
|
||
//HeroTask(mapData)?.OnDamageOther(mapData, info);
|
||
}
|
||
|
||
//获取抵达某格所需要的movecostinfo下限,目前仅用于momijiHUnter,也就是说moveinfo>该值才
|
||
public float GetGridMoveFloor(MapData mapData, UnitData originUnit,GridData targetGrid)
|
||
{
|
||
|
||
var copy = new List<SkillBase>(Skills);
|
||
float ret = 0f;
|
||
foreach (var skill in copy)
|
||
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;
|
||
foreach(var skill in Skills)
|
||
ret += skill.GetKillCount();
|
||
return ret;
|
||
}
|
||
|
||
//判断是否被某人视作英雄
|
||
public bool TreatedAsHero(MapData map,UnitData unit)
|
||
{
|
||
/*if (UnitFullType.UnitType == UnitType.RemiliaEgyptianKoakuma || UnitFullType.UnitType == UnitType.RemiliaEgyptianKoakumaLion)
|
||
{
|
||
if (unit.Player(map) != Player(map)) return false;
|
||
return true;
|
||
}*/
|
||
|
||
if (UnitFullType.UnitType == UnitType.Giant) return true;
|
||
if (CarryUnitFullType.UnitType == UnitType.Giant) return true;
|
||
|
||
foreach (var skill in Skills)
|
||
{
|
||
if (skill.IsTreatAsHero(map, this,unit)) return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// 判断是否只能在我方领土内移动
|
||
public bool IsLimitMoveToSelfTerrain(MapData map)
|
||
{
|
||
foreach (var skill in Skills)
|
||
{
|
||
if (skill.IsLimitMoveToSelfTerrain(this, map)) return true;
|
||
}
|
||
return false;
|
||
}
|
||
#endregion
|
||
|
||
//------------------------------------------------- 对unitData的基础操作 ----------------------------------------------
|
||
//清空 AP Cp MP
|
||
public void ClearAPMPCP()
|
||
{
|
||
AP = 0;
|
||
MP = 0;
|
||
CP = 0;
|
||
}
|
||
|
||
public void SetFullAPCPMP()
|
||
{
|
||
AP = 1;
|
||
MP = 1;
|
||
CP = 1;
|
||
}
|
||
|
||
public void SetFullAPCPMP_AllSkillRefresh()
|
||
{
|
||
SetFullAPCPMP();
|
||
foreach (var skill in Skills)
|
||
skill.OnRefresh();
|
||
}
|
||
|
||
public int AddHealth(int hp)
|
||
{
|
||
var offset = Math.Min(GetMaxHealth() - Health, hp);
|
||
Health += offset;
|
||
return offset;
|
||
}
|
||
|
||
public void SetOfficer()
|
||
{
|
||
foreach (var skill in Skills)
|
||
skill.SetOfficer();
|
||
}
|
||
}
|
||
|
||
} |