1955 lines
68 KiB
C#
1955 lines
68 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
|
||
{
|
||
// 行动点枚举
|
||
public enum ActionPointType
|
||
{
|
||
Common,
|
||
Move,
|
||
Attack,
|
||
Capture,
|
||
}
|
||
|
||
// 地图上所有的小兵数据
|
||
[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()
|
||
{
|
||
UnitList ??= new List<UnitData>();
|
||
_unitDict ??= new Dictionary<uint, UnitData>();
|
||
_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<UnitData> _turnUnitBuffer;
|
||
|
||
public void OnTurnStart(MapData map, PlayerData player)
|
||
{
|
||
_turnUnitBuffer ??= new List<UnitData>();
|
||
_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<UnitData>();
|
||
_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<UnitData>();
|
||
_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<UnitData>();
|
||
_unitDict ??= new Dictionary<uint, UnitData>();
|
||
|
||
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<uint, UnitData>();
|
||
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<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;
|
||
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<SkillBase> SkillCache;
|
||
public Dictionary<ActionPointType, int> ActionPoint;
|
||
|
||
// 行动点缓存,UI用不参与逻辑
|
||
[MemoryPackIgnore]
|
||
private Dictionary<ActionPointType, int> _actionCache;
|
||
public Dictionary<ActionPointType, int> 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<SkillBase>();
|
||
foreach (var skill in copyData.SkillCache) SkillCache.Add(skill.GetCopySkill());
|
||
ActionPoint = new Dictionary<ActionPointType, int>();
|
||
foreach (var kv in copyData.ActionPoint)
|
||
{
|
||
ActionPoint[kv.Key] = kv.Value;
|
||
}
|
||
_actionCache = new Dictionary<ActionPointType, int>();
|
||
}
|
||
|
||
//注意,这里没有处理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<SkillBase>();
|
||
SkillCache.Clear();
|
||
foreach (var skill in copyData.SkillCache) SkillCache.Add(skill.GetCopySkill());
|
||
ActionPoint ??= new Dictionary<ActionPointType, int>();
|
||
ActionPoint.Clear();
|
||
foreach (var kv in copyData.ActionPoint)
|
||
{
|
||
ActionPoint[kv.Key] = kv.Value;
|
||
}
|
||
_actionCache ??= new Dictionary<ActionPointType, int>();
|
||
}
|
||
|
||
// 无参数初始化
|
||
public UnitData(UnitFullType unitFullType, MapIdGenerator idGenerator)
|
||
{
|
||
Id = idGenerator.GeneratorId();
|
||
UnitFullType = unitFullType;
|
||
SkillCache = new List<SkillBase>();
|
||
ActionPoint = new Dictionary<ActionPointType, int>();
|
||
_actionCache = new Dictionary<ActionPointType, int>();
|
||
InitData();
|
||
}
|
||
|
||
// MemoryPack 反序列化之后的后处理
|
||
[MemoryPackOnDeserialized]
|
||
public void OnAfterMemoryPackDeserialize()
|
||
{
|
||
_actionCache ??= new Dictionary<ActionPointType, int>();
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
|
||
|
||
//----------------------------------------- 判断属性 ----------------------------------------------
|
||
/// <summary>
|
||
/// 静态判断:该UnitType是否属于BigGuy类(不含等级判断)
|
||
/// </summary>
|
||
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<SkillBase>(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<SkillBase>(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<SkillBase>(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 生命周期入口 ------------------------------------------------------------------]
|
||
|
||
/// <summary>
|
||
/// 是否具备 AttackAlly 能力
|
||
/// </summary>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取 AttackAlly 基础恢复数值
|
||
/// </summary>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取 AttackAlly 恢复数值加成(加法)
|
||
/// </summary>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取 AttackAlly 对自身造成的伤害(如Koakuma的治疗代价)
|
||
/// </summary>
|
||
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<Vector2Int> 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<Vector2Int> path = null)
|
||
{
|
||
var isFrozen = IsFrozen();
|
||
var copy = new List<SkillBase>(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<Vector2Int> path = null)
|
||
{
|
||
if (map == null || target == null || !IsValidOnMap(map, target)) return;
|
||
|
||
var isFrozen = IsFrozen();
|
||
var copy = new List<SkillBase>(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<SkillBase>(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<SkillBase>(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<SkillBase>(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<SkillBase>(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<SkillBase>(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<SkillBase>(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<SkillBase>(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<SkillBase>(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<SkillBase>(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<SkillBase>(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<SkillBase>(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<SkillBase>(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<SkillBase>(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<SkillBase>(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<SkillBase>(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<SkillBase>(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<SkillBase>(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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断单位是否拥有且正在生效某个技能。
|
||
/// 与 GetSkill 的区别是:GetSkill 只判断“是否持有”,本方法会额外排除被全技能封禁(Force)的技能。
|
||
/// </summary>
|
||
/// <param name="skillType">目标技能类型</param>
|
||
/// <param name="skill">返回技能实例</param>
|
||
/// <returns>true=拥有且生效;false=未拥有或已被封禁</returns>
|
||
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;
|
||
|
||
}
|
||
|
||
}
|
||
}
|