3350 lines
150 KiB
C#
3350 lines
150 KiB
C#
/*
|
||
* @Author: 白哉
|
||
* @Description: 行为逻辑类
|
||
* @Date: 2025年04月10日 星期四 11:04:44
|
||
* @Modify:
|
||
*/
|
||
|
||
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Text;
|
||
using Logic.AI;
|
||
using Logic.Audio;
|
||
using Logic.CrashSight;
|
||
using Logic.Skill;
|
||
using MemoryPack;
|
||
using RuntimeData;
|
||
using TH1_Anim;
|
||
using TH1_Anim.Fragments;
|
||
using TH1_Core.Events;
|
||
using TH1_Core.Managers;
|
||
using TH1_Logic.Action;
|
||
using TH1_Logic.Collect;
|
||
using TH1_Logic.Core;
|
||
using TH1_Logic.MatchConfig;
|
||
using TH1_Presentation.Sequencer.Task;
|
||
using TH1_Renderer;
|
||
using TH1_Logic.Net;
|
||
using TH1_Logic.Steam;
|
||
using TH1_Logic.Tools;
|
||
using UnityEngine;
|
||
using TH1Renderer;
|
||
|
||
|
||
//记录ActionCheckCan失败时的原因
|
||
public enum ShowType
|
||
{
|
||
None,
|
||
Locked,
|
||
Cost,
|
||
Done,
|
||
Send,
|
||
Cold,
|
||
}
|
||
|
||
namespace Logic.Action
|
||
{
|
||
// 行为类型枚举
|
||
public enum CommonActionType
|
||
{
|
||
Gain,
|
||
Build,
|
||
StartWonder,
|
||
BuildWonder,
|
||
TrainUnit,
|
||
GridMisc,
|
||
UnitAction,
|
||
CityLevelUpAction,
|
||
UnitSkill,
|
||
LearnTech,
|
||
UnitMove,
|
||
UnitAttack,
|
||
PlayerAction,
|
||
TurnStart,
|
||
TurnEnd,
|
||
UnitPassiveMove,
|
||
AIParamControl,
|
||
UnitAttackAlly,
|
||
UnitAttackGround,
|
||
PlayerSurrender,
|
||
BuyCultureCard,
|
||
CityAction,
|
||
HakureiEinherjarCityDevelopment,
|
||
}
|
||
|
||
|
||
public enum ActionShowState
|
||
{
|
||
None,
|
||
Available,
|
||
Unavailable,
|
||
Expensive,
|
||
Finished
|
||
}
|
||
|
||
|
||
public enum AIParamControlType
|
||
{
|
||
AllClear,
|
||
APClear,
|
||
MPClear,
|
||
CPClear,
|
||
AIMoney,
|
||
Max,
|
||
}
|
||
|
||
|
||
// 通用行为参数类
|
||
[MemoryPackable]
|
||
public partial class CommonActionParams
|
||
{
|
||
[MemoryPackIgnore]
|
||
public MapData MapData;
|
||
[MemoryPackIgnore]
|
||
public PlayerData PlayerData;
|
||
[MemoryPackIgnore]
|
||
public UnitData UnitData;
|
||
[MemoryPackIgnore]
|
||
public CityData CityData;
|
||
[MemoryPackIgnore]
|
||
public GridData GridData;
|
||
[MemoryPackIgnore]
|
||
public UnitData TargetUnitData;
|
||
[MemoryPackIgnore]
|
||
public GridData TargetGridData;
|
||
[MemoryPackIgnore]
|
||
public PlayerData TargetPlayerData;
|
||
|
||
public MainObjectType MainObjectType;
|
||
public uint PlayerId;
|
||
public uint UnitId;
|
||
public uint CityId;
|
||
public uint GridId;
|
||
public uint TargetUnitId;
|
||
public uint TargetGridId;
|
||
public uint TargetPlayerId;
|
||
|
||
|
||
|
||
[MemoryPackConstructor]
|
||
public CommonActionParams()
|
||
{
|
||
|
||
}
|
||
|
||
|
||
//必须走这个函数新建,否则需要调用下方的OnParamChanged刷新属性状态
|
||
public CommonActionParams(MapData mapData=null, PlayerData playerData=null,
|
||
UnitData unitData=null, CityData cityData=null, GridData gridData=null, UnitData targetUnit=null, GridData targetGrid=null, PlayerData targetPlayer = null,MainObjectType mainObjectType=MainObjectType.None)
|
||
{
|
||
MapData = mapData;
|
||
PlayerData = playerData;
|
||
UnitData = unitData;
|
||
CityData = cityData;
|
||
GridData = gridData;
|
||
TargetUnitData = targetUnit;
|
||
TargetGridData = targetGrid;
|
||
TargetPlayerData = targetPlayer;
|
||
MainObjectType = mainObjectType;
|
||
|
||
PlayerId = 0;
|
||
UnitId = 0;
|
||
CityId = 0;
|
||
GridId = 0;
|
||
TargetUnitId = 0;
|
||
TargetGridId = 0;
|
||
TargetPlayerId = 0;
|
||
if (PlayerData != null) PlayerId = PlayerData.Id;
|
||
if (UnitData != null) UnitId = UnitData.Id;
|
||
if (CityData != null) CityId = CityData.Id;
|
||
if (GridData != null) GridId = GridData.Id;
|
||
if (TargetUnitData != null) TargetUnitId = TargetUnitData.Id;
|
||
if (TargetGridData != null) TargetGridId = TargetGridData.Id;
|
||
if (TargetPlayerData != null) TargetPlayerId = TargetPlayerData.Id;
|
||
}
|
||
|
||
public void RefreshParams()
|
||
{
|
||
if (PlayerId != 0 && MapData.PlayerMap.GetPlayerDataByPlayerID(PlayerId, out var player))
|
||
{
|
||
PlayerData = player;
|
||
}
|
||
if (UnitId != 0 && MapData.UnitMap.GetUnitDataByUnitId(UnitId, out var unit))
|
||
{
|
||
UnitData = unit;
|
||
}
|
||
if (CityId != 0 && MapData.CityMap.GetCityById(CityId, out var city))
|
||
{
|
||
CityData = city;
|
||
}
|
||
if (GridId != 0 && MapData.GridMap.GetGridDataByGid(GridId, out var grid))
|
||
{
|
||
GridData = grid;
|
||
}
|
||
if (TargetUnitId != 0 && MapData.UnitMap.GetUnitDataByUnitId(TargetUnitId, out var target))
|
||
{
|
||
TargetUnitData = target;
|
||
}
|
||
if (TargetGridId != 0 && MapData.GridMap.GetGridDataByGid(TargetGridId, out var targetGrid))
|
||
{
|
||
TargetGridData = targetGrid;
|
||
}
|
||
if (TargetPlayerId != 0 && MapData.PlayerMap.GetPlayerDataByPlayerID(TargetPlayerId, out var targetPlayer))
|
||
{
|
||
TargetPlayerData = targetPlayer;
|
||
}
|
||
}
|
||
public void OnParamChanged()
|
||
{
|
||
if (PlayerData != null) PlayerId = PlayerData.Id;
|
||
else PlayerId = 0;
|
||
if (UnitData != null) UnitId = UnitData.Id;
|
||
else UnitId = 0;
|
||
if (CityData != null) CityId = CityData.Id;
|
||
else CityId = 0;
|
||
if (GridData != null) GridId = GridData.Id;
|
||
else GridId = 0;
|
||
if (TargetUnitData != null) TargetUnitId = TargetUnitData.Id;
|
||
else TargetUnitId = 0;
|
||
if (TargetGridData != null) TargetGridId = TargetGridData.Id;
|
||
else TargetGridId = 0;
|
||
if (TargetPlayerData != null) TargetPlayerId = TargetPlayerData.Id;
|
||
else TargetPlayerId = 0;
|
||
}
|
||
|
||
public CommonActionParams GetCopyParam()
|
||
{
|
||
var param = new CommonActionParams();
|
||
param.MapData = MapData;
|
||
param.PlayerData = PlayerData;
|
||
param.UnitData = UnitData;
|
||
param.CityData = CityData;
|
||
param.GridData = GridData;
|
||
param.TargetUnitData = TargetUnitData;
|
||
param.TargetGridData = TargetGridData;
|
||
param.TargetPlayerData = TargetPlayerData;
|
||
param.MainObjectType = MainObjectType;
|
||
|
||
param.PlayerId = PlayerId;
|
||
param.UnitId = UnitId;
|
||
param.CityId = CityId;
|
||
param.GridId = GridId;
|
||
param.TargetUnitId = TargetUnitId;
|
||
param.TargetGridId = TargetGridId;
|
||
param.TargetPlayerId = TargetPlayerId;
|
||
return param;
|
||
}
|
||
|
||
public string GetStringLog()
|
||
{
|
||
var log = $"";
|
||
log += $"MainObjectType : {MainObjectType}\n";
|
||
log += $"PlayerId : {PlayerId}\n";
|
||
log += $"UnitId : {UnitId}\n";
|
||
log += $"CityId : {CityId}\n";
|
||
log += $"GridId : {GridId}\n";
|
||
log += $"TargetUnitId : {TargetUnitId}\n";
|
||
log += $"TargetGridId : {TargetGridId}\n";
|
||
log += $"TargetPlayerId : {TargetPlayerId}\n";
|
||
return log;
|
||
}
|
||
|
||
public string GetNotSameLog(CommonActionParams other)
|
||
{
|
||
var log = $"";
|
||
if (MainObjectType != other.MainObjectType)
|
||
log += $"MainObjectType : {MainObjectType}\n";
|
||
if (PlayerId != other.PlayerId)
|
||
log += $"PlayerId : {PlayerId}\n";
|
||
if (UnitId != other.UnitId)
|
||
log += $"UnitId : {UnitId}\n";
|
||
if (CityId != other.CityId)
|
||
log += $"CityId : {CityId}\n";
|
||
if (GridId != other.GridId)
|
||
log += $"GridId : {GridId}\n";
|
||
if (TargetUnitId != other.TargetUnitId)
|
||
log += $"TargetUnitId : {TargetUnitId}\n";
|
||
if (TargetGridId != other.TargetGridId)
|
||
log += $"TargetGridId : {TargetGridId}\n";
|
||
if (TargetPlayerId != other.TargetPlayerId)
|
||
log += $"TargetPlayerId : {TargetPlayerId}\n";
|
||
return log;
|
||
}
|
||
}
|
||
|
||
|
||
// 通用行为ID类
|
||
[Serializable]
|
||
[MemoryPackable]
|
||
public partial class CommonActionId
|
||
{
|
||
public CommonActionType ActionType;
|
||
public WonderTypeEnum WonderType;
|
||
public ResourceType ResourceType;
|
||
public TerrainFeature FeatureType;
|
||
public TerrainType TerrainType;
|
||
public UnitType UnitType;
|
||
public GiantType GiantType;
|
||
public uint UnitLevel;
|
||
public Vegetation Vegetation;
|
||
public UnitActionType UnitActionType;
|
||
public CityLevelUpActionType CityLevelUpActionType;
|
||
public CityActionType CityActionType;
|
||
public GridMiscActionType GridMiscActionType;
|
||
public SkillType SkillType;
|
||
public TechType TechType;
|
||
public PlayerActionType PlayerActionType;
|
||
public AIParamControlType AIParamType;
|
||
public CultureCardType CultureCardType;
|
||
|
||
|
||
[MemoryPackConstructor]
|
||
public CommonActionId()
|
||
{
|
||
|
||
}
|
||
|
||
//自动生成唯一Id Hash
|
||
public uint Id => ComputeId();
|
||
private uint ComputeId()
|
||
{
|
||
unchecked // 溢出安全
|
||
{
|
||
int hash = 17;
|
||
hash = hash * 31 + ActionType.GetHashCode();
|
||
hash = hash * 31 + WonderType.GetHashCode();
|
||
hash = hash * 31 + ResourceType.GetHashCode();
|
||
hash = hash * 31 + FeatureType.GetHashCode();
|
||
hash = hash * 31 + TerrainType.GetHashCode();
|
||
hash = hash * 31 + UnitType.GetHashCode();
|
||
hash = hash * 31 + GiantType.GetHashCode();
|
||
hash = hash * 31 + UnitLevel.GetHashCode();
|
||
hash = hash * 31 + Vegetation.GetHashCode();
|
||
hash = hash * 31 + UnitActionType.GetHashCode();
|
||
hash = hash * 31 + CityLevelUpActionType.GetHashCode();
|
||
hash = hash * 31 + CityActionType.GetHashCode();
|
||
hash = hash * 31 + GridMiscActionType.GetHashCode();
|
||
hash = hash * 31 + SkillType.GetHashCode();
|
||
hash = hash * 31 + TechType.GetHashCode();
|
||
hash = hash * 31 + PlayerActionType.GetHashCode();
|
||
hash = hash * 31 + AIParamType.GetHashCode();
|
||
hash = hash * 31 + CultureCardType.GetHashCode();
|
||
return (uint)hash;
|
||
}
|
||
}
|
||
// 重载 == 运算符 当属性被修改时需要修改运算符重载方法
|
||
public static bool operator ==(CommonActionId a, CommonActionId b)
|
||
{
|
||
if (a is null) return b is null;
|
||
if (b is null) return false;
|
||
|
||
if (a.ActionType != b.ActionType) return false;
|
||
if (a.WonderType != b.WonderType) return false;
|
||
if (a.ResourceType != b.ResourceType) return false;
|
||
if (a.TerrainType != b.TerrainType) return false;
|
||
if (a.FeatureType != b.FeatureType) return false;
|
||
if (a.UnitType != b.UnitType) return false;
|
||
if (a.GiantType != b.GiantType) return false;
|
||
if (a.UnitLevel != b.UnitLevel) return false;
|
||
if (a.Vegetation != b.Vegetation) return false;
|
||
if (a.UnitActionType != b.UnitActionType) return false;
|
||
if (a.CityLevelUpActionType != b.CityLevelUpActionType) return false;
|
||
if (a.CityActionType != b.CityActionType) return false;
|
||
if (a.GridMiscActionType != b.GridMiscActionType) return false;
|
||
if (a.SkillType != b.SkillType) return false;
|
||
if (a.TechType != b.TechType) return false;
|
||
if (a.PlayerActionType != b.PlayerActionType) return false;
|
||
if (a.AIParamType != b.AIParamType) return false;
|
||
if (a.CultureCardType != b.CultureCardType) return false;
|
||
return true;
|
||
}
|
||
|
||
// 必须同时重载 != 运算符
|
||
public static bool operator !=(CommonActionId a, CommonActionId b)
|
||
{
|
||
return !(a == b);
|
||
}
|
||
|
||
public override int GetHashCode()
|
||
{
|
||
// 直接调用你的哈希计算逻辑,并转换为int
|
||
return (int)ComputeId();
|
||
}
|
||
public override bool Equals(object obj)
|
||
{
|
||
// 调用类型安全的Equals方法
|
||
return Equals(obj as CommonActionId);
|
||
}
|
||
|
||
public bool Equals(CommonActionId other)
|
||
{
|
||
// 如果另一个对象是null,它们不相等
|
||
if (other is null)
|
||
{
|
||
return false;
|
||
}
|
||
// 如果它们是同一个内存中的对象,它们肯定相等
|
||
if (ReferenceEquals(this, other))
|
||
{
|
||
return true;
|
||
}
|
||
// 调用你已经写好的比较逻辑!
|
||
// 我们可以直接使用你重载的 `==` 运算符,这样可以重用代码。
|
||
return this == other;
|
||
}
|
||
|
||
public string GetStringLog()
|
||
{
|
||
var log = $"";
|
||
log += $"Action : {ActionType}\n";
|
||
log += $"Wonder : {WonderType}\n";
|
||
log += $"Resource : {ResourceType}\n";
|
||
log += $"Feature : {FeatureType}\n";
|
||
log += $"Terrain : {TerrainType}\n";
|
||
log += $"Unit : {UnitType}\n";
|
||
log += $"Giant : {GiantType}\n";
|
||
log += $"Vegetation : {Vegetation}\n";
|
||
log += $"UnitAction : {UnitActionType}\n";
|
||
log += $"CityLevelUpAction : {CityLevelUpActionType}\n";
|
||
log += $"CityAction : {CityActionType}\n";
|
||
log += $"GridMiscAction : {GridMiscActionType}\n";
|
||
log += $"Skill : {SkillType}\n";
|
||
log += $"Tech : {TechType}\n";
|
||
log += $"PlayerAction : {PlayerActionType}\n";
|
||
log += $"AIParam : {AIParamType}\n";
|
||
log += $"Tech : {TechType}\n";
|
||
log += $"CultureCardType : {CultureCardType}\n";
|
||
return log;
|
||
}
|
||
}
|
||
|
||
|
||
// 行为逻辑工厂类
|
||
public static class ActionLogicFactory
|
||
{
|
||
private static Dictionary<CommonActionId, ActionLogicBase> ActionLogicDict;
|
||
private static Dictionary<uint, ActionLogicBase> _actionLogicIdDict;
|
||
|
||
|
||
public static Dictionary<CommonActionId, ActionLogicBase> GetActionLogicDict()
|
||
{
|
||
Refresh();
|
||
return ActionLogicDict;
|
||
}
|
||
|
||
public static Dictionary<uint, ActionLogicBase> GetActionLogicIDDict()
|
||
{
|
||
Refresh();
|
||
return _actionLogicIdDict;
|
||
}
|
||
|
||
public static void RefreshPlayerAction()
|
||
{
|
||
CommonActionId commonActionId;
|
||
//填加外交行为
|
||
var diplomacyActionTypes = new[]
|
||
{
|
||
PlayerActionType.OfferAlly,
|
||
PlayerActionType.AcceptAlly,
|
||
PlayerActionType.RefuseAlly,
|
||
PlayerActionType.BreakAlly,
|
||
PlayerActionType.Embassy,
|
||
PlayerActionType.BreakEmbassy,
|
||
PlayerActionType.DanegeldDemand,
|
||
PlayerActionType.DanegeldPay,
|
||
PlayerActionType.DanegeldReject,
|
||
};
|
||
foreach (var playerActionType in diplomacyActionTypes)
|
||
{
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.PlayerAction,
|
||
PlayerActionType = playerActionType
|
||
};
|
||
ActionLogicDict[commonActionId] = new PlayerActionDiplomacy(commonActionId);
|
||
}
|
||
|
||
foreach (var playerActionType in new[]
|
||
{
|
||
PlayerActionType.TreasureGainCoin,
|
||
PlayerActionType.TreasureGainCulture,
|
||
PlayerActionType.TreasureGainCityExp,
|
||
})
|
||
{
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.PlayerAction,
|
||
PlayerActionType = playerActionType,
|
||
};
|
||
ActionLogicDict[commonActionId] = new PlayerActionTreasureReward(commonActionId);
|
||
}
|
||
foreach (var playerActionType in new[]
|
||
{
|
||
PlayerActionType.TreasureGainUnit,
|
||
PlayerActionType.TreasureGainTech,
|
||
})
|
||
{
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.PlayerAction,
|
||
PlayerActionType = playerActionType,
|
||
};
|
||
ActionLogicDict[commonActionId] = new PlayerActionTreasureReward(commonActionId);
|
||
}
|
||
//填加英雄相关行为
|
||
foreach (GiantType giantType in System.Enum.GetValues(typeof(GiantType)))
|
||
{
|
||
if (giantType == GiantType.None) continue;
|
||
//选择英雄
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.PlayerAction,
|
||
PlayerActionType = PlayerActionType.SelectHero,
|
||
GiantType = giantType
|
||
};
|
||
ActionLogicDict[commonActionId] = new PlayerActionSelectHero(commonActionId);
|
||
//强制完成英雄的任务
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.PlayerAction,
|
||
PlayerActionType = PlayerActionType.FinishHeroTask,
|
||
GiantType = giantType
|
||
};
|
||
ActionLogicDict[commonActionId] = new PlayerActionFinishHeroTask(commonActionId);
|
||
}
|
||
|
||
|
||
}
|
||
|
||
public static void Refresh()
|
||
{
|
||
if (ActionLogicDict != null)
|
||
return;
|
||
|
||
ActionLogicDict = new Dictionary<CommonActionId, ActionLogicBase>();
|
||
CommonActionId commonActionId;
|
||
|
||
// 移动和攻击 Action 入库
|
||
commonActionId = new CommonActionId { ActionType = CommonActionType.UnitMove };
|
||
ActionLogicDict[commonActionId] = new UnitMoveAction(commonActionId);
|
||
commonActionId = new CommonActionId { ActionType = CommonActionType.UnitAttack };
|
||
ActionLogicDict[commonActionId] = new UnitAttackAction(commonActionId);
|
||
commonActionId = new CommonActionId { ActionType = CommonActionType.UnitAttackGround };
|
||
ActionLogicDict[commonActionId] = new UnitAttackGroundAction(commonActionId);
|
||
commonActionId = new CommonActionId { ActionType = CommonActionType.UnitPassiveMove };
|
||
ActionLogicDict[commonActionId] = new UnitPassiveMoveAction(commonActionId);
|
||
commonActionId = new CommonActionId { ActionType = CommonActionType.UnitAttackAlly };
|
||
ActionLogicDict[commonActionId] = new UnitAttackAllyAction(commonActionId);
|
||
|
||
|
||
// 回合开始和结束 Action 入库
|
||
commonActionId = new CommonActionId { ActionType = CommonActionType.TurnStart };
|
||
ActionLogicDict[commonActionId] = new PlayerTurnStartAction(commonActionId);
|
||
commonActionId = new CommonActionId { ActionType = CommonActionType.TurnEnd };
|
||
ActionLogicDict[commonActionId] = new PlayerTurnEndAction(commonActionId);
|
||
|
||
commonActionId = new CommonActionId { ActionType = CommonActionType.PlayerSurrender };
|
||
ActionLogicDict[commonActionId] = new SurrenderAction(commonActionId);
|
||
|
||
for (int i = (int)CultureCardType.SecondHero; i < (int)CultureCardType.Max; i++)
|
||
{
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.BuyCultureCard,
|
||
CultureCardType = (CultureCardType)i,
|
||
};
|
||
ActionLogicDict[commonActionId] = new BuyCultureCardAction(commonActionId);
|
||
}
|
||
|
||
for (int i = (int)AIParamControlType.AllClear; i < (int)AIParamControlType.Max; i++)
|
||
{
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.AIParamControl,
|
||
AIParamType = (AIParamControlType)i,
|
||
};
|
||
ActionLogicDict[commonActionId] = new AIParamControlAction(commonActionId);
|
||
}
|
||
|
||
foreach (ResourceType resourceType in System.Enum.GetValues(typeof(ResourceType)))
|
||
{
|
||
if (resourceType == ResourceType.None) continue;
|
||
|
||
// 先登记所有Gain 收获一次性资源的行为逻辑
|
||
if (resourceType == ResourceType.Animal || resourceType == ResourceType.Fish ||
|
||
resourceType == ResourceType.Fruit)
|
||
{
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.Gain,
|
||
ResourceType = resourceType,
|
||
|
||
};
|
||
ActionLogicDict[commonActionId] = new GainResourceAction(commonActionId);
|
||
continue;
|
||
}
|
||
|
||
// 登记Build 建设建筑的行为逻辑
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.Build,
|
||
ResourceType = resourceType,
|
||
};
|
||
switch (resourceType)
|
||
{
|
||
|
||
case ResourceType.ForestTemple:
|
||
case ResourceType.WaterTemple:
|
||
case ResourceType.Temple:
|
||
case ResourceType.MountainTemple:
|
||
case ResourceType.KingTemple:
|
||
ActionLogicDict[commonActionId] = new BuildActionBuildTemple(commonActionId);
|
||
continue;
|
||
case ResourceType.Preserve:
|
||
ActionLogicDict[commonActionId] = new BuildActionBuildPreserve(commonActionId);
|
||
continue;
|
||
case ResourceType.NavalBase:
|
||
ActionLogicDict[commonActionId] = new BuildActionBuildNavalBase(commonActionId);
|
||
continue;
|
||
case ResourceType.KaguyaFrenchYard:
|
||
ActionLogicDict[commonActionId] = new BuildActionBuildKaguyaFrenchYard(commonActionId);
|
||
continue;
|
||
case ResourceType.EgyptianIrrigation:
|
||
ActionLogicDict[commonActionId] = new BuildActionBuildEgyptianIrrigation(commonActionId);
|
||
continue;
|
||
case ResourceType.RemiliaMilitary:
|
||
ActionLogicDict[commonActionId] = new BuildActionBuildRemiliaMilitary(commonActionId);
|
||
continue;
|
||
case ResourceType.MetalStation:
|
||
ActionLogicDict[commonActionId] = new BuildActionBuildMetalStation(commonActionId);
|
||
continue;
|
||
case ResourceType.MoriyaMilitary:
|
||
ActionLogicDict[commonActionId] = new BuildActionBuildMoriyaMilitary(commonActionId);
|
||
continue;
|
||
default:
|
||
ActionLogicDict[commonActionId] = new BuildAction(commonActionId);
|
||
continue;
|
||
}
|
||
}
|
||
|
||
//登记build road这种特殊情况
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.Build,
|
||
FeatureType = TerrainFeature.Road
|
||
};
|
||
ActionLogicDict[commonActionId] = new BuildAction(commonActionId);
|
||
|
||
//GridMisc grid的杂类行为逻辑
|
||
foreach (GridMiscActionType gridMiscActionType in System.Enum.GetValues(typeof(GridMiscActionType)))
|
||
{
|
||
if (gridMiscActionType == GridMiscActionType.None) continue;
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.GridMisc,
|
||
GridMiscActionType = gridMiscActionType
|
||
};
|
||
switch (gridMiscActionType)
|
||
{
|
||
case GridMiscActionType.GrowForestOutside:
|
||
ActionLogicDict[commonActionId] = new GridMiscActionGrowTreeOutside(commonActionId);
|
||
continue;
|
||
case GridMiscActionType.CreateMountain:
|
||
ActionLogicDict[commonActionId] = new GridMiscActionCreateMountain(commonActionId);
|
||
continue;
|
||
case GridMiscActionType.SellMetal:
|
||
ActionLogicDict[commonActionId] = new GridMiscActionSellMetal(commonActionId);
|
||
continue;
|
||
default:
|
||
ActionLogicDict[commonActionId] = new GridMiscAction(commonActionId);
|
||
continue;
|
||
};
|
||
|
||
|
||
}
|
||
|
||
//登记BuildWonder建造奇观的行为逻辑
|
||
foreach (WonderTypeEnum wonderType in System.Enum.GetValues(typeof(WonderTypeEnum)))
|
||
{
|
||
if(wonderType == WonderTypeEnum.None) continue;
|
||
//登记建造奇观的action
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.BuildWonder,
|
||
WonderType = wonderType,
|
||
};
|
||
ActionLogicDict[commonActionId] = new BuildWonderAction(commonActionId);
|
||
|
||
//登记建造开启奇观任务的action
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.StartWonder,
|
||
WonderType = wonderType,
|
||
};
|
||
ActionLogicDict[commonActionId] = new StartWonderAction(commonActionId);
|
||
}
|
||
|
||
// TrainUnit 建造unit的行为逻辑
|
||
foreach (UnitType unitType in Enum.GetValues(typeof(UnitType)))
|
||
{
|
||
if(unitType == UnitType.None) continue;
|
||
|
||
if (unitType == UnitType.Giant)
|
||
{
|
||
foreach (GiantType giantType in Enum.GetValues(typeof(GiantType)))
|
||
{
|
||
if(giantType == GiantType.None) continue;
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.TrainUnit,
|
||
UnitType = unitType,
|
||
GiantType = giantType
|
||
};
|
||
|
||
//Debug.Log(ActionId.GiantType);
|
||
ActionLogicDict[commonActionId] = new TrainUnitActionTrainHero(commonActionId);
|
||
}
|
||
continue;
|
||
}
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.TrainUnit,
|
||
UnitType = unitType,
|
||
};
|
||
if (unitType == UnitType.KaguyaFrenchAnimalWarrior)
|
||
ActionLogicDict[commonActionId] = new TrainUnitActionTrainKaguyaFrenchAnimalWarrior(commonActionId);
|
||
else
|
||
ActionLogicDict[commonActionId] = new TrainUnitAction(commonActionId);
|
||
|
||
// MoriyaHebi Level3 额外注册
|
||
if (unitType == UnitType.MoriyaHebi)
|
||
{
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.TrainUnit,
|
||
UnitType = unitType,
|
||
UnitLevel = 3,
|
||
};
|
||
ActionLogicDict[commonActionId] = new TrainUnitAction(commonActionId);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
//cityLevelUp的各个行为逻辑
|
||
foreach (CityLevelUpActionType cityLevelUpActionType in System.Enum.GetValues(typeof(CityLevelUpActionType)))
|
||
{
|
||
if(cityLevelUpActionType == CityLevelUpActionType.None) continue;
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.CityLevelUpAction,
|
||
CityLevelUpActionType = cityLevelUpActionType
|
||
};
|
||
ActionLogicDict[commonActionId] = new CityLevelUpActionAction(commonActionId);
|
||
}
|
||
|
||
foreach (CityActionType cityActionType in System.Enum.GetValues(typeof(CityActionType)))
|
||
{
|
||
if (cityActionType == CityActionType.None) continue;
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.CityAction,
|
||
CityActionType = cityActionType
|
||
};
|
||
ActionLogicDict[commonActionId] = new CityAction(commonActionId);
|
||
}
|
||
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.HakureiEinherjarCityDevelopment
|
||
};
|
||
ActionLogicDict[commonActionId] = new HakureiEinherjarCityDevelopmentAction(commonActionId);
|
||
|
||
//unitAction自身行为的各个行为逻辑
|
||
foreach (UnitActionType unitActionType in System.Enum.GetValues(typeof(UnitActionType)))
|
||
{
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.UnitAction,
|
||
UnitActionType = unitActionType
|
||
};
|
||
switch (unitActionType)
|
||
{
|
||
case UnitActionType.None:
|
||
continue;
|
||
case UnitActionType.HeroUpgrade:
|
||
ActionLogicDict[commonActionId] = new UnitActionHeroUpgrade(commonActionId);
|
||
continue;
|
||
case UnitActionType.CultureUnitUpgrade:
|
||
ActionLogicDict[commonActionId] = new UnitActionCultureUnitUpgrade(commonActionId);
|
||
continue;
|
||
case UnitActionType.ReservedReimuProtectionAttackAlly:
|
||
continue;
|
||
case UnitActionType.ReimuPayClearExtermination:
|
||
ActionLogicDict[commonActionId] = new UnitActionReimuPayClearExtermination(commonActionId);
|
||
continue;
|
||
case UnitActionType.Raid:
|
||
ActionLogicDict[commonActionId] = new UnitActionRaid(commonActionId);
|
||
continue;
|
||
case UnitActionType.WarCry:
|
||
ActionLogicDict[commonActionId] = new UnitActionWarCry(commonActionId);
|
||
continue;
|
||
case UnitActionType.KasenToggleOniForm:
|
||
ActionLogicDict[commonActionId] = new UnitActionKasenToggleOniForm(commonActionId);
|
||
continue;
|
||
case UnitActionType.SuikaShakeOffMinis:
|
||
ActionLogicDict[commonActionId] = new UnitActionSuikaShakeOffMinis(commonActionId);
|
||
continue;
|
||
case UnitActionType.SuikaCreateMiniByHp:
|
||
ActionLogicDict[commonActionId] = new UnitActionSuikaCreateMiniByHp(commonActionId);
|
||
continue;
|
||
case UnitActionType.SuikaThrowUnit:
|
||
ActionLogicDict[commonActionId] = new UnitActionSuikaThrowUnit(commonActionId);
|
||
continue;
|
||
case UnitActionType.HakureiAbsorbRune:
|
||
ActionLogicDict[commonActionId] = new UnitActionHakureiAbsorbRune(commonActionId);
|
||
continue;
|
||
case UnitActionType.Demolish:
|
||
ActionLogicDict[commonActionId] = new UnitActionAction(commonActionId);
|
||
continue;
|
||
case UnitActionType.ROYALFLAMESPRO:
|
||
ActionLogicDict[commonActionId] = new UnitActionROYALFLAMESPRO(commonActionId);
|
||
continue;
|
||
case UnitActionType.HEAL:
|
||
ActionLogicDict[commonActionId] = new UnitActionHEAL(commonActionId);
|
||
continue;
|
||
case UnitActionType.TEWIFRENCHBUFF:
|
||
ActionLogicDict[commonActionId] = new UnitActionTEWIFRENCHBUFF(commonActionId);
|
||
continue;
|
||
case UnitActionType.MOKOUFRENCHBOOM:
|
||
ActionLogicDict [commonActionId] = new UnitActionMOKOUFRENCHBOOM(commonActionId);
|
||
continue;
|
||
case UnitActionType.KAGUYAFRENCHAROUND:
|
||
ActionLogicDict [commonActionId] = new UnitActionKAGUYAFRENCHAROUND(commonActionId);
|
||
continue;
|
||
case UnitActionType.ForceDisband:
|
||
ActionLogicDict [commonActionId] = new UnitActionForceDisband(commonActionId);
|
||
continue;
|
||
case UnitActionType.RemoveRedMist:
|
||
ActionLogicDict [commonActionId] = new UnitActionRemoveRedMist(commonActionId);
|
||
continue;
|
||
case UnitActionType.REMILIABUFF:
|
||
ActionLogicDict [commonActionId] = new UnitActionREMILIABUFF(commonActionId);
|
||
continue;
|
||
case UnitActionType.AbsorbRedMist:
|
||
ActionLogicDict [commonActionId] = new UnitActionAbsorbRedMist(commonActionId);
|
||
continue;
|
||
case UnitActionType.REMILIAABSORB:
|
||
ActionLogicDict [commonActionId] = new UnitActionRemiliaAbsorb(commonActionId);
|
||
continue;
|
||
case UnitActionType.KANAKOSIT:
|
||
ActionLogicDict [commonActionId] = new UnitActionKanakoSit(commonActionId);
|
||
continue;
|
||
case UnitActionType.KANAKOUNSIT:
|
||
ActionLogicDict [commonActionId] = new UnitActionKanakoUnSit(commonActionId);
|
||
continue;
|
||
case UnitActionType.AYAMOVEAGAIN:
|
||
ActionLogicDict [commonActionId] = new UnitActionAyaMoveAgain(commonActionId);
|
||
continue;
|
||
case UnitActionType.KomeijiRinFire:
|
||
ActionLogicDict [commonActionId] = new UnitActionKomeijiRinFire(commonActionId);
|
||
continue;
|
||
case UnitActionType.KomeijiRinCityExp:
|
||
ActionLogicDict [commonActionId] = new UnitActionKomeijiRinCityExp(commonActionId);
|
||
continue;
|
||
case UnitActionType.KomeijiBonePileBoom:
|
||
ActionLogicDict [commonActionId] = new UnitActionKomeijiBonePileBoom(commonActionId);
|
||
continue;
|
||
case UnitActionType.ReisenFrenchBoom:
|
||
ActionLogicDict [commonActionId] = new UnitActionReisenFrenchBoom(commonActionId);
|
||
continue;
|
||
default:
|
||
ActionLogicDict[commonActionId] = new UnitActionAction(commonActionId);
|
||
continue;
|
||
}
|
||
|
||
}
|
||
|
||
foreach (UnitType unitType in Enum.GetValues(typeof(UnitType)))
|
||
if(!(unitType is UnitType.None or UnitType.Giant)
|
||
&& Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unitType,GiantType.None,0,out var info)
|
||
&& info.LandType == LandType.WaterAndAshore)
|
||
{
|
||
if (unitType == UnitType.Boat) continue;
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.UnitAction,
|
||
UnitType = unitType
|
||
};
|
||
ActionLogicDict[commonActionId] = new UnitActionAction(commonActionId);
|
||
}
|
||
|
||
//登记所有学习科技的action
|
||
foreach (TechType techType in System.Enum.GetValues(typeof(TechType)))
|
||
{
|
||
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.LearnTech,
|
||
TechType = techType
|
||
};
|
||
ActionLogicDict[commonActionId] = new LearnTechAction(commonActionId);
|
||
}
|
||
|
||
//登记6种unitskill类的科技,该科技会让该玩家当前的和未来的所有unit都拥有某个skill
|
||
foreach (SkillType skillType in System.Enum.GetValues(typeof(SkillType)))
|
||
{
|
||
if (skillType == SkillType.MOUNTAINMOVE || skillType == SkillType.MOUNTAINDEFENSE ||
|
||
skillType == SkillType.WATERMOVE || skillType == SkillType.WATERDEFENSE ||
|
||
skillType == SkillType.OCEANMOVE || skillType == SkillType.OCEANDEFENSE ||
|
||
skillType == SkillType.FORESTDEFENSE)
|
||
{
|
||
commonActionId = new CommonActionId
|
||
{
|
||
ActionType = CommonActionType.UnitSkill,
|
||
SkillType = skillType
|
||
};
|
||
ActionLogicDict[commonActionId] = new UnitSkillAction(commonActionId);
|
||
}
|
||
}
|
||
|
||
//登记playerAction
|
||
RefreshPlayerAction();
|
||
|
||
if (_actionLogicIdDict == null)
|
||
{
|
||
_actionLogicIdDict = new Dictionary<uint, ActionLogicBase>();
|
||
foreach (var kv in ActionLogicDict)
|
||
{
|
||
_actionLogicIdDict[kv.Key.Id] = kv.Value;
|
||
}
|
||
}
|
||
}
|
||
|
||
public static ActionLogicBase GetActionLogic(CommonActionId actionId)
|
||
{
|
||
Refresh();
|
||
foreach (var kv in ActionLogicDict)
|
||
{
|
||
if (kv.Key != actionId) continue;
|
||
return kv.Value;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
// 返回一个玩家有什么 Action
|
||
public static bool PlayerHasAction(CommonActionParams param, out List<ActionLogicBase> actionList)
|
||
{
|
||
param.MainObjectType = MainObjectType.Player;
|
||
actionList = new List<ActionLogicBase>();
|
||
var actionDict = GetActionLogicDict();
|
||
foreach (var action in actionDict.Values)
|
||
{
|
||
if(action.CheckCan(param))
|
||
actionList.Add(action);
|
||
}
|
||
|
||
return actionList.Count > 0;
|
||
}
|
||
|
||
//返回一个 grid 对于 player data 而言有什么 action
|
||
public static bool GridHasAction(CommonActionParams param, out List<ActionLogicBase> actionList)
|
||
{
|
||
param.MainObjectType = MainObjectType.Grid;
|
||
actionList = new List<ActionLogicBase>();
|
||
var actionDict = GetActionLogicDict();
|
||
foreach (var action in actionDict.Values)
|
||
{
|
||
if (!action.CheckCan(param)) continue;
|
||
actionList.Add(action);
|
||
}
|
||
return actionList.Count > 0;
|
||
}
|
||
|
||
//返回一个 grid 对于 player data 而言有什么 action
|
||
public static bool GridHasAction(CommonActionParams param, List<ActionLogicBase> actionList)
|
||
{
|
||
param.MainObjectType = MainObjectType.Grid;
|
||
var actionDict = GetActionLogicDict();
|
||
bool hasAction = false;
|
||
foreach (var action in actionDict.Values)
|
||
{
|
||
if (!action.CheckCan(param)) continue;
|
||
actionList.Add(action);
|
||
hasAction = true;
|
||
}
|
||
return hasAction;
|
||
}
|
||
|
||
public static bool CityHasAction(CommonActionParams param, out List<ActionLogicBase> actionList)
|
||
{
|
||
param.MainObjectType = MainObjectType.City;
|
||
actionList = new List<ActionLogicBase>();
|
||
var actionDict = GetActionLogicDict();
|
||
foreach (var action in actionDict.Values)
|
||
{
|
||
if(action.CheckCan(param))
|
||
actionList.Add(action);
|
||
}
|
||
|
||
return actionList.Count > 0;
|
||
}
|
||
|
||
//返回一个 unit 对于 player data 而言有什么 action
|
||
public static bool UnitHasAction(CommonActionParams param, out List<ActionLogicBase> actionList)
|
||
{
|
||
param.MainObjectType = MainObjectType.Unit;
|
||
actionList = new List<ActionLogicBase>();
|
||
bool hasCP = param.UnitData.GetActionPoint(ActionPointType.Capture) > 0;
|
||
var actionDict = GetActionLogicDict();
|
||
foreach (var action in actionDict.Values)
|
||
{
|
||
if (!action.CheckCan(param)) continue;
|
||
// 无行动点时,仅保留不需要行动点的action(自身CheckCan已通过即可)
|
||
if (!hasCP && action.ActionId.ActionType == CommonActionType.UnitAction)
|
||
{
|
||
var unitActionType = action.ActionId.UnitActionType;
|
||
if (unitActionType != UnitActionType.KomeijiBonePileBoom
|
||
&& unitActionType != UnitActionType.REMILIAABSORB
|
||
&& unitActionType != UnitActionType.ReimuPayClearExtermination
|
||
&& unitActionType != UnitActionType.SuikaCreateMiniByHp
|
||
&& unitActionType != UnitActionType.HakureiAbsorbRune
|
||
&& unitActionType != UnitActionType.Demolish
|
||
&& unitActionType != UnitActionType.Raid
|
||
&& unitActionType != UnitActionType.WarCry) continue;
|
||
}
|
||
actionList.Add(action);
|
||
}
|
||
return actionList.Count > 0;
|
||
}
|
||
|
||
// 返回一个 unit 的移动和攻击行为
|
||
public static bool UnitHasMoveAndAttackAction(MapData map, UnitData unit, out List<AIActionBase> actionList)
|
||
{
|
||
if (!unit.IsAlive())
|
||
{
|
||
actionList = null;
|
||
return false;
|
||
}
|
||
|
||
var attackID = new CommonActionId { ActionType = CommonActionType.UnitAttack };
|
||
var moveId = new CommonActionId { ActionType = CommonActionType.UnitMove };
|
||
var attackAction = new UnitAttackAction(attackID);
|
||
var moveAction = new UnitMoveAction(moveId);
|
||
|
||
actionList = new List<AIActionBase>();
|
||
Main.UnitLogic.CalcUnitMoveInfo(map, unit.Id);
|
||
if (!unit.Player(map, out var player)) return false;
|
||
foreach (var grid in map.GridMap.GridList)
|
||
{
|
||
var result = Main.UnitLogic.CheckUnitCanMoveOrAttack(map, unit, grid);
|
||
if (result == MoveAttackType.None) continue;
|
||
|
||
if (result == MoveAttackType.Attack)
|
||
{
|
||
if (unit.GetActionPoint(ActionPointType.Attack) <= 0 || !grid.VisibleUnit(map,player, out var targetUnit)) continue;
|
||
var param = new CommonActionParams(map, unitData:unit, targetUnit:targetUnit);
|
||
param.RefreshParams();
|
||
actionList.Add(new AIActionBase(param, attackAction));
|
||
|
||
if (map.GetGridDataByUnitId(unit.Id, out var unitGrid))
|
||
{
|
||
//Debug.Log($"生成Action 小兵攻击 {unit.Id}, 攻击范围{unit.GetAttackRange()}" +$"位置{unitGrid.Pos.X}, {unitGrid.Pos.Y}, " + $"目标 {targetUnit.Id}, " + $"目标位置{grid.Pos.X}, {grid.Pos.Y}");
|
||
}
|
||
|
||
continue;
|
||
}
|
||
|
||
if (unit.GetActionPoint(ActionPointType.Move) > 0)
|
||
{
|
||
var param = new CommonActionParams(map, unitData:unit, gridData:grid);
|
||
param.RefreshParams();
|
||
actionList.Add(new AIActionBase(param, moveAction));
|
||
}
|
||
}
|
||
|
||
return actionList.Count > 0;
|
||
}
|
||
|
||
//返回一个主体能够显示的action(注意,并非能“做”的action,有些行为没钱或者没科技,他也可以显示)
|
||
public static bool MainObjectCanShowAction(CommonActionParams param, out List<ActionLogicBase> actionList,out List<ShowType> actionCantTypeList)
|
||
{
|
||
//注意,要将所有cityLevelUp直接屏蔽掉 在这里
|
||
|
||
actionList = new List<ActionLogicBase>();
|
||
actionCantTypeList = new List<ShowType>();
|
||
var actionDict = GetActionLogicDict();
|
||
foreach (var action in actionDict.Values)
|
||
{
|
||
|
||
if (!action.CheckShow(param,out var actionCantType)) continue;
|
||
if (action.ActionId.ActionType == CommonActionType.CityLevelUpAction) continue;
|
||
actionList.Add(action);
|
||
actionCantTypeList.Add(actionCantType);
|
||
|
||
}
|
||
return actionList.Count > 0;
|
||
}
|
||
|
||
public static List<ActionLogicBase> GetActionLogicByType(CommonActionType type)
|
||
{
|
||
var actionList = new List<ActionLogicBase>();
|
||
foreach (var action in GetActionLogicDict().Values)
|
||
{
|
||
if (action.ActionId.ActionType != type) continue;
|
||
actionList.Add(action);
|
||
}
|
||
return actionList;
|
||
}
|
||
|
||
public static MainObjectType GetMainObjectType(CommonActionType actionType)
|
||
{
|
||
if (actionType == CommonActionType.Gain) return MainObjectType.Grid;
|
||
if (actionType == CommonActionType.Build) return MainObjectType.Grid;
|
||
if (actionType == CommonActionType.StartWonder) return MainObjectType.Player;
|
||
if (actionType == CommonActionType.BuildWonder) return MainObjectType.Grid;
|
||
if (actionType == CommonActionType.TrainUnit) return MainObjectType.City;
|
||
if (actionType == CommonActionType.GridMisc) return MainObjectType.Grid;
|
||
if (actionType == CommonActionType.UnitAction) return MainObjectType.Unit;
|
||
if (actionType == CommonActionType.CityLevelUpAction) return MainObjectType.City;
|
||
if (actionType == CommonActionType.CityAction) return MainObjectType.City;
|
||
if (actionType == CommonActionType.HakureiEinherjarCityDevelopment) return MainObjectType.City;
|
||
if (actionType == CommonActionType.UnitSkill) return MainObjectType.Unit;
|
||
if (actionType == CommonActionType.LearnTech) return MainObjectType.Player;
|
||
if (actionType == CommonActionType.UnitMove) return MainObjectType.Unit;
|
||
if (actionType == CommonActionType.UnitAttack) return MainObjectType.Unit;
|
||
if (actionType == CommonActionType.PlayerAction) return MainObjectType.Player;
|
||
if (actionType == CommonActionType.AIParamControl) return MainObjectType.Player;
|
||
if (actionType == CommonActionType.UnitAttackAlly) return MainObjectType.Unit;
|
||
if (actionType == CommonActionType.UnitAttackGround) return MainObjectType.Unit;
|
||
if (actionType == CommonActionType.BuyCultureCard) return MainObjectType.Player;
|
||
return MainObjectType.Player;
|
||
}
|
||
}
|
||
|
||
public class CityAction : ActionLogicBase
|
||
{
|
||
public CityAction(CommonActionId id) : base(id)
|
||
{
|
||
}
|
||
|
||
protected override bool Execute(CommonActionParams actionParams)
|
||
{
|
||
if (_actionId.CityActionType != CityActionType.BuildCityWall) return false;
|
||
if (!CheckCan(actionParams)) return false;
|
||
|
||
actionParams.PlayerData.SpendCoin(GetCost(actionParams));
|
||
actionParams.CityData.CityWall = true;
|
||
var cityGrid = actionParams.CityData.Grid(actionParams.MapData);
|
||
actionParams.CityData.SetCityRenderer(actionParams.MapData);
|
||
cityGrid?.Renderer(actionParams.MapData)?.InstantUpdateGrid(true);
|
||
if (actionParams.MapData == Main.MapData
|
||
&& (cityGrid?.InMainSight() ?? false)
|
||
&& cityGrid.MainSelfPlayerVisibleUnit(out var unit))
|
||
{
|
||
unit.Renderer(actionParams.MapData)?.RenderUpdateUnitDefense();
|
||
}
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckCan(CommonActionParams actionParams)
|
||
{
|
||
if (_actionId.CityActionType != CityActionType.BuildCityWall) return false;
|
||
if (actionParams?.MapData == null) return false;
|
||
if (actionParams.PlayerData == null) return false;
|
||
if (actionParams.CityData == null) return false;
|
||
if (actionParams.MainObjectType != MainObjectType.City) return false;
|
||
if (actionParams.CityData.CityWall) return false;
|
||
if (!actionParams.MapData.CheckCityIdBelongPlayerId(actionParams.CityData.Id, actionParams.PlayerData.Id)) return false;
|
||
if (!actionParams.PlayerData.TechTree.CheckIfHasTechAtom(TechAtom.BuildCityWall)) return false;
|
||
if (actionParams.PlayerData.PlayerCoin < GetCost(actionParams)) return false;
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckShow(CommonActionParams actionParams, out ShowType showType)
|
||
{
|
||
showType = ShowType.None;
|
||
if (_actionId.CityActionType != CityActionType.BuildCityWall) return false;
|
||
if (actionParams?.MapData == null) return false;
|
||
if (actionParams.PlayerData == null) return false;
|
||
if (actionParams.CityData == null) return false;
|
||
if (actionParams.MainObjectType != MainObjectType.City) return false;
|
||
if (actionParams.CityData.CityWall) return false;
|
||
if (!actionParams.MapData.CheckCityIdBelongPlayerId(actionParams.CityData.Id, actionParams.PlayerData.Id)) return false;
|
||
if (!actionParams.PlayerData.TechTree.CheckIfHasTechAtom(TechAtom.BuildCityWall))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
if (actionParams.PlayerData.PlayerCoin < GetCost(actionParams)) showType = ShowType.Cost;
|
||
return true;
|
||
}
|
||
}
|
||
|
||
|
||
public class HakureiEinherjarCityDevelopmentAction : ActionLogicBase
|
||
{
|
||
public HakureiEinherjarCityDevelopmentAction(CommonActionId id) : base(id)
|
||
{
|
||
}
|
||
|
||
protected override bool Execute(CommonActionParams actionParams)
|
||
{
|
||
if (!CheckCan(actionParams)) return false;
|
||
if (!actionParams.CityData.Grid(actionParams.MapData, out var cityGrid)) return false;
|
||
|
||
var cost = GetEinherjarCost(actionParams);
|
||
if (!HakureiEinherjarCounter.TrySpendPlayerPoints(actionParams.MapData, actionParams.PlayerData, cost))
|
||
return false;
|
||
if (!Main.CityLogic.GridGiveCityExp_LogicView(actionParams.MapData, actionParams.PlayerData, cityGrid, actionParams.CityData, 1))
|
||
return false;
|
||
|
||
HakureiEinherjarCounter.AddCityOfferingLevel(actionParams.MapData, actionParams.CityData, 1);
|
||
actionParams.CityData.SetCityRenderer(actionParams.MapData);
|
||
cityGrid.Renderer(actionParams.MapData)?.InstantUpdateGrid(true);
|
||
return true;
|
||
}
|
||
|
||
public int GetEinherjarCost(CommonActionParams actionParams)
|
||
{
|
||
return HakureiEinherjarCounter.GetCityDevelopmentCost(actionParams?.CityData);
|
||
}
|
||
|
||
public override int GetCost(CommonActionParams actionParams)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
public override bool CheckCan(CommonActionParams actionParams)
|
||
{
|
||
if (!CheckShow(actionParams, out _)) return false;
|
||
return HakureiEinherjarCounter.GetPlayerPoints(actionParams.PlayerData) >= GetEinherjarCost(actionParams);
|
||
}
|
||
|
||
public override bool CheckShow(CommonActionParams actionParams, out ShowType showType)
|
||
{
|
||
showType = ShowType.None;
|
||
if (actionParams?.MapData == null) return false;
|
||
if (actionParams.PlayerData == null) return false;
|
||
if (actionParams.CityData == null) return false;
|
||
if (actionParams.MainObjectType != MainObjectType.City) return false;
|
||
if (!IsHakureiEmpirePlayer(actionParams.PlayerData)) return false;
|
||
if (!actionParams.MapData.CheckCityIdBelongPlayerId(actionParams.CityData.Id, actionParams.PlayerData.Id)) return false;
|
||
if (!actionParams.CityData.Grid(actionParams.MapData, out var cityGrid)) return false;
|
||
if (cityGrid.Resource != ResourceType.CityCenter) return false;
|
||
if (HakureiEinherjarCounter.GetPlayerPoints(actionParams.PlayerData) < GetEinherjarCost(actionParams))
|
||
showType = ShowType.Cost;
|
||
return true;
|
||
}
|
||
|
||
private static bool IsHakureiEmpirePlayer(PlayerData player)
|
||
{
|
||
return player != null
|
||
&& player.CivEnum == CivEnum.Norway
|
||
&& player.ForceEnum == ForceEnum.Reimu;
|
||
}
|
||
}
|
||
|
||
// 行为基类
|
||
public abstract class ActionLogicBase
|
||
{
|
||
protected CommonActionId _actionId;
|
||
public CommonActionId ActionId => _actionId;
|
||
protected float _duration;
|
||
public float Duration
|
||
{
|
||
get => _duration;
|
||
set => _duration = value;
|
||
}
|
||
|
||
// 所有ActionLogic共享的临时集合(行为是顺序执行的,不会并发)
|
||
protected static List<GridData> _sharedAroundBuf;
|
||
private static HashSet<uint> _sharedDataUnitIdSet;
|
||
private static List<uint> _sharedRendererOnlyUnitIds;
|
||
private static List<uint> _sharedDataOnlyUnitIds;
|
||
private static List<UnitData> _sharedBoatUnitsOnLand;
|
||
private static StringBuilder _sharedRendererUnitMismatchLogBuilder;
|
||
private static bool _boatUnitOnLandDiagnosticFailedLogged;
|
||
private static bool _unitRendererMismatchDiagnosticFailedLogged;
|
||
private static bool _unitAttackRendererMissingAfterKillDiagnosticFailedLogged;
|
||
private static bool _unitAttackAllyRendererMissingAfterKillDiagnosticFailedLogged;
|
||
private const int MaxRendererUnitMismatchLogCount = 12;
|
||
|
||
public ActionLogicBase(CommonActionId id)
|
||
{
|
||
_actionId = id;
|
||
_duration = 0;
|
||
}
|
||
|
||
public bool CheckEqualActionId(CommonActionId actionId)
|
||
{
|
||
return _actionId == actionId;
|
||
}
|
||
|
||
//湖区特殊费用计算逻辑
|
||
public bool GetPreserveCost(MapData map, GridData grid, out int cost)
|
||
{
|
||
cost = 20;
|
||
if (map == null || grid == null) return false;
|
||
var city = grid.BelongButNotOnGridCity(map);
|
||
if(city == null)return false;
|
||
int count = 0;
|
||
foreach (var gid in city.Territory.TerritoryArea)
|
||
{
|
||
if (map.GridMap.GetGridDataByGid(gid, out var targetGrid) &&
|
||
targetGrid.Resource == ResourceType.Preserve) count++;
|
||
}
|
||
|
||
cost = count * 5;
|
||
return true;
|
||
}
|
||
|
||
public virtual int GetCost(CommonActionParams actionParams)
|
||
{
|
||
if (!Table.Instance.ActionDataAssets.GetActionInfo(_actionId, out var info)) return 0;
|
||
//preserve特别的计算cost方式
|
||
if (_actionId.ResourceType == ResourceType.Preserve)
|
||
{
|
||
if(GetPreserveCost(actionParams.MapData,actionParams.GridData,out var cost))return cost;
|
||
}
|
||
|
||
return info.Cost;
|
||
}
|
||
|
||
public virtual float GetAnimTime(CommonActionParams actionParams)
|
||
{
|
||
//TODO 这里通用返回值要读表,暂时先直接返回0.1f
|
||
return 0.05f;
|
||
}
|
||
|
||
public virtual bool CameraControl(CommonActionParams actionParams)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
public float GetValue()
|
||
{
|
||
return 1f;
|
||
}
|
||
|
||
// 完整的执行调用, 供外部使用
|
||
public virtual bool CompleteExecute(CommonActionParams actionParams)
|
||
{
|
||
// 此处限制了只有在当前 Player 回合内才能执行对应Player的Action
|
||
if (actionParams.PlayerId != 0
|
||
&& ActionId.ActionType != CommonActionType.TurnStart
|
||
&& ActionId.ActionType != CommonActionType.PlayerSurrender
|
||
&& actionParams.MapData.CurPlayer != null && actionParams.MapData.CurPlayer.Id != actionParams.PlayerId)
|
||
{
|
||
LogSystem.LogError($"CompleteExecute Player 不一致 {ActionId.GetStringLog()}");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ActionSyncMismatch);
|
||
|
||
return false;
|
||
}
|
||
if (Main.Instance.IsNetActionExecuting)
|
||
{
|
||
return ExecuteWithoutFullActionPeriod(actionParams);
|
||
}
|
||
|
||
if (actionParams.MapData == Main.MapData)
|
||
{
|
||
if (actionParams.MapData.Net.Mode == NetMode.Multi)
|
||
{
|
||
if (LobbyManager.Instance.Lobby.IsLobbyOwner())
|
||
{
|
||
if (!GameNetSender.Instance.ActionExecute(_actionId, actionParams))
|
||
{
|
||
LogSystem.LogError($"ActionExecute broadcast failed, abort owner execute: {ActionId.GetStringLog()}");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ActionSendFailed);
|
||
return false;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (!GameNetSender.Instance.ActionConfirm(_actionId, actionParams))
|
||
{
|
||
LogSystem.LogError($"ActionConfirm send failed, abort local execute: {ActionId.GetStringLog()}");
|
||
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.ActionSendFailed);
|
||
return false;
|
||
}
|
||
if (ActionId.ActionType == CommonActionType.TurnEnd) return false;
|
||
}
|
||
}
|
||
Main.Instance.IsNetActionExecuting = true;
|
||
}
|
||
|
||
// LogSystem.LogWarning($"CompleteExecute : {NetData.GetMapDataHash(Main.MapData)}");
|
||
BeforeExecute(actionParams);
|
||
var ret = Execute(actionParams);
|
||
AfterExecute(actionParams);
|
||
if (actionParams.MapData == Main.MapData) Main.Instance.IsNetActionExecuting = false;
|
||
|
||
|
||
return ret;
|
||
}
|
||
|
||
//仅仅复用Action的execute逻辑,但是不被视作一个完整的completeExecute,不会走网络同步和时序等等,通常是嵌套在另一个主体Action之中的一段逻辑
|
||
public virtual bool ExecuteWithoutFullActionPeriod(CommonActionParams actionParams)
|
||
{
|
||
return Execute(actionParams);
|
||
}
|
||
|
||
// 网络调用
|
||
public virtual bool NetCompleteExecute(CommonActionParams actionParams)
|
||
{
|
||
BeforeExecute(actionParams);
|
||
var wasNetActionExecuting = Main.Instance.IsNetActionExecuting;
|
||
Main.Instance.IsNetActionExecuting = true;
|
||
try
|
||
{
|
||
var ret = Execute(actionParams);
|
||
AfterExecute(actionParams);
|
||
return ret;
|
||
}
|
||
finally
|
||
{
|
||
Main.Instance.IsNetActionExecuting = wasNetActionExecuting;
|
||
}
|
||
}
|
||
|
||
// 实际的执行逻辑
|
||
protected abstract bool Execute(CommonActionParams actionParams);
|
||
|
||
// 执行前逻辑
|
||
protected virtual void BeforeExecute(CommonActionParams actionParams)
|
||
{
|
||
// 必须在 action 执行之前记录
|
||
if (actionParams.MapData != Main.MapData) return;
|
||
|
||
ReportBeforeActionDiagnostics(actionParams);
|
||
|
||
var actionData = new ActionNetData();
|
||
actionData.Version = Main.MapData.Net.GetActionVersion();
|
||
actionData.MapHash = NetData.GetMapDataHash(Main.MapData);
|
||
actionData.Param = actionParams;
|
||
actionData.ActionId = _actionId;
|
||
actionData.Time = Time.time;
|
||
|
||
#if CHECK_ACTIONDEFFERENCE
|
||
if (actionParams.MapData == Main.MapData &&
|
||
Main.Instance.CheckMapData != null &&
|
||
NetData.GetMapDataHash(Main.MapData) != NetData.GetMapDataHash(Main.Instance.CheckMapData))
|
||
{
|
||
var allStr = "";
|
||
var diff = Main.MapData.GetCompareEqual(Main.Instance.CheckMapData);
|
||
foreach (var str in diff) allStr += str + "\n";
|
||
diff = MapData.FindDifferences(Main.MapData, Main.Instance.CheckMapData);
|
||
foreach (var str in diff) allStr += str + "\n";
|
||
|
||
if (Main.MapData.Net.Actions.Count == 0)
|
||
{
|
||
LogSystem.LogError($" Mapdata 不一致,数据:{allStr}\n " +
|
||
$" 前Action:无,后Action:{_actionId.GetStringLog()} \n");
|
||
}
|
||
else
|
||
{
|
||
LogSystem.LogError($" Mapdata 不一致,数据:{allStr}\n " +
|
||
$" 前Action:{Main.MapData.Net.Actions[^1].ActionId.GetStringLog()},后Action:{_actionId.GetStringLog()} \n");
|
||
}
|
||
}
|
||
#endif
|
||
|
||
Main.MapData.Net.Actions.Add(actionData);
|
||
}
|
||
|
||
private void ReportBeforeActionDiagnostics(CommonActionParams actionParams)
|
||
{
|
||
try
|
||
{
|
||
ReportBoatUnitOnLandBeforeAction(actionParams);
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
if (!_boatUnitOnLandDiagnosticFailedLogged)
|
||
{
|
||
_boatUnitOnLandDiagnosticFailedLogged = true;
|
||
LogSystem.LogWarning($"BoatUnitOnLandBeforeAction diagnostic failed: {e}");
|
||
}
|
||
}
|
||
|
||
try
|
||
{
|
||
ReportUnitRendererMismatchBeforeAction(actionParams);
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
if (!_unitRendererMismatchDiagnosticFailedLogged)
|
||
{
|
||
_unitRendererMismatchDiagnosticFailedLogged = true;
|
||
LogSystem.LogWarning($"UnitRendererMismatchBeforeAction diagnostic failed: {e}");
|
||
}
|
||
}
|
||
}
|
||
|
||
private void ReportBoatUnitOnLandBeforeAction(CommonActionParams actionParams)
|
||
{
|
||
var mapData = actionParams.MapData;
|
||
if (mapData?.UnitMap?.UnitList == null) return;
|
||
if (Table.Instance?.UnitTypeDataAssets == null) return;
|
||
|
||
_sharedBoatUnitsOnLand ??= new List<UnitData>();
|
||
_sharedRendererUnitMismatchLogBuilder ??= new StringBuilder(2048);
|
||
|
||
_sharedBoatUnitsOnLand.Clear();
|
||
foreach (var unitData in mapData.UnitMap.UnitList)
|
||
{
|
||
if (unitData == null) continue;
|
||
if (!mapData.GetGridDataByUnitId(unitData.Id, out var gridData) || gridData == null) continue;
|
||
if (gridData.Terrain != TerrainType.Land) continue;
|
||
|
||
var landType = unitData.GetLandType();
|
||
if (landType is LandType.WaterAndAshore or LandType.WaterOnly)
|
||
_sharedBoatUnitsOnLand.Add(unitData);
|
||
}
|
||
|
||
if (_sharedBoatUnitsOnLand.Count == 0) return;
|
||
|
||
var sb = _sharedRendererUnitMismatchLogBuilder;
|
||
sb.Clear();
|
||
sb.AppendLine("[BoatUnitOnLandBeforeAction] 船形态Unit停留在陆地格");
|
||
sb.Append("MapId=").Append(mapData.MapID)
|
||
.Append(", ActionIndex=").Append(mapData.Net?.Actions?.Count ?? 0)
|
||
.Append(", Count=").Append(_sharedBoatUnitsOnLand.Count)
|
||
.AppendLine();
|
||
|
||
AppendPreviousActionsLog(sb, mapData);
|
||
AppendBoatUnitsOnLandLog(sb, mapData);
|
||
|
||
LogSystem.LogError(sb.ToString());
|
||
}
|
||
|
||
private void ReportUnitRendererMismatchBeforeAction(CommonActionParams actionParams)
|
||
{
|
||
var mapData = actionParams.MapData;
|
||
if (mapData?.UnitMap?.UnitList == null) return;
|
||
|
||
var mapRenderer = MapRenderer.Current;
|
||
if (mapRenderer?.ROUnitMap == null) return;
|
||
|
||
_sharedDataUnitIdSet ??= new HashSet<uint>();
|
||
_sharedRendererOnlyUnitIds ??= new List<uint>();
|
||
_sharedDataOnlyUnitIds ??= new List<uint>();
|
||
_sharedRendererUnitMismatchLogBuilder ??= new StringBuilder(2048);
|
||
|
||
_sharedDataUnitIdSet.Clear();
|
||
_sharedRendererOnlyUnitIds.Clear();
|
||
_sharedDataOnlyUnitIds.Clear();
|
||
|
||
foreach (var unitData in mapData.UnitMap.UnitList)
|
||
{
|
||
if (unitData == null) continue;
|
||
_sharedDataUnitIdSet.Add(unitData.Id);
|
||
|
||
if (!mapRenderer.ROUnitMap.TryGetValue(unitData.Id, out var renderer) || renderer == null || !renderer.IsValid)
|
||
_sharedDataOnlyUnitIds.Add(unitData.Id);
|
||
}
|
||
|
||
foreach (var kv in mapRenderer.ROUnitMap)
|
||
{
|
||
if (!_sharedDataUnitIdSet.Contains(kv.Key))
|
||
_sharedRendererOnlyUnitIds.Add(kv.Key);
|
||
}
|
||
|
||
if (_sharedRendererOnlyUnitIds.Count == 0 && _sharedDataOnlyUnitIds.Count == 0) return;
|
||
|
||
var sb = _sharedRendererUnitMismatchLogBuilder;
|
||
sb.Clear();
|
||
sb.AppendLine("[UnitRendererMismatchBeforeAction] 数据层Unit与渲染层Unit不一致");
|
||
sb.Append("MapId=").Append(mapData.MapID)
|
||
.Append(", ActionIndex=").Append(mapData.Net?.Actions?.Count ?? 0)
|
||
.Append(", DataUnits=").Append(_sharedDataUnitIdSet.Count)
|
||
.Append(", RenderUnits=").Append(mapRenderer.ROUnitMap.Count)
|
||
.Append(", RenderOnly=").Append(_sharedRendererOnlyUnitIds.Count)
|
||
.Append(", DataOnly=").Append(_sharedDataOnlyUnitIds.Count)
|
||
.AppendLine();
|
||
|
||
AppendPreviousActionsLog(sb, mapData);
|
||
AppendRendererOnlyUnitsLog(sb, mapData, mapRenderer);
|
||
AppendDataOnlyUnitsLog(sb, mapData, mapRenderer);
|
||
|
||
LogSystem.LogError(sb.ToString());
|
||
mapRenderer.RepairUnitRenderersToData(mapData, _sharedRendererOnlyUnitIds, _sharedDataOnlyUnitIds);
|
||
}
|
||
|
||
private static void AppendPreviousActionsLog(StringBuilder sb, MapData mapData)
|
||
{
|
||
var actions = mapData.Net?.Actions;
|
||
if (actions == null || actions.Count == 0)
|
||
{
|
||
sb.AppendLine("PrevActions: 无");
|
||
return;
|
||
}
|
||
|
||
var count = Math.Min(actions.Count, 2);
|
||
sb.Append("PrevActions(count=").Append(count).AppendLine("):");
|
||
for (var i = 0; i < count; i++)
|
||
{
|
||
var actionIndex = actions.Count - 1 - i;
|
||
sb.Append("PrevAction[").Append(actionIndex).AppendLine("]:");
|
||
AppendActionNetDataLog(sb, actions[actionIndex]);
|
||
}
|
||
}
|
||
|
||
private static void AppendRecentActionsLog(StringBuilder sb, MapData mapData, int maxCount)
|
||
{
|
||
var actions = mapData.Net?.Actions;
|
||
if (actions == null || actions.Count == 0)
|
||
{
|
||
sb.AppendLine("RecentActions: 无");
|
||
return;
|
||
}
|
||
|
||
var count = Math.Min(actions.Count, maxCount);
|
||
sb.Append("RecentActions(count=").Append(count).AppendLine("):");
|
||
for (var i = 0; i < count; i++)
|
||
{
|
||
var actionIndex = actions.Count - 1 - i;
|
||
sb.Append("RecentAction[").Append(actionIndex).AppendLine("]:");
|
||
AppendActionNetDataLog(sb, actions[actionIndex]);
|
||
}
|
||
}
|
||
|
||
private static void AppendActionNetDataLog(StringBuilder sb, ActionNetData actionData)
|
||
{
|
||
if (actionData == null)
|
||
{
|
||
sb.AppendLine("null");
|
||
return;
|
||
}
|
||
|
||
sb.Append("Version=").Append(actionData.Version)
|
||
.Append(", MapHash=").Append(actionData.MapHash)
|
||
.AppendLine();
|
||
if (actionData.ActionId != null)
|
||
sb.AppendLine(actionData.ActionId.GetStringLog());
|
||
if (actionData.Param != null)
|
||
{
|
||
sb.AppendLine("Param:");
|
||
sb.AppendLine(actionData.Param.GetStringLog());
|
||
}
|
||
}
|
||
|
||
private static void AppendBoatUnitsOnLandLog(StringBuilder sb, MapData mapData)
|
||
{
|
||
sb.Append("BoatUnitsOnLand(count=").Append(_sharedBoatUnitsOnLand.Count).AppendLine("):");
|
||
|
||
var count = Math.Min(_sharedBoatUnitsOnLand.Count, MaxRendererUnitMismatchLogCount);
|
||
for (var i = 0; i < count; i++)
|
||
{
|
||
var unitData = _sharedBoatUnitsOnLand[i];
|
||
sb.Append(" ").Append(i + 1).Append(". ");
|
||
AppendUnitDataLog(sb, mapData, unitData);
|
||
if (mapData.GetGridDataByUnitId(unitData.Id, out var gridData) && gridData != null)
|
||
{
|
||
sb.Append(", gridTerrain=").Append(gridData.Terrain)
|
||
.Append(", gridResource=").Append(gridData.Resource)
|
||
.Append(", gridPos=(").Append(gridData.Pos.X).Append(",").Append(gridData.Pos.Y).Append(")");
|
||
}
|
||
|
||
sb.Append(", landType=").Append(unitData.GetLandType())
|
||
.Append(", carry=").Append(unitData.CarryUnitType)
|
||
.Append("/")
|
||
.Append(unitData.CarryGiantType)
|
||
.Append("/")
|
||
.Append(unitData.CarryUnitLevel)
|
||
.AppendLine();
|
||
}
|
||
|
||
if (_sharedBoatUnitsOnLand.Count > count)
|
||
sb.Append(" ... more=").Append(_sharedBoatUnitsOnLand.Count - count).AppendLine();
|
||
}
|
||
|
||
private static void AppendRendererOnlyUnitsLog(StringBuilder sb, MapData mapData, MapRenderer mapRenderer)
|
||
{
|
||
sb.Append("RendererOnlyUnits(count=").Append(_sharedRendererOnlyUnitIds.Count).AppendLine("):");
|
||
if (_sharedRendererOnlyUnitIds.Count == 0) return;
|
||
|
||
var count = Math.Min(_sharedRendererOnlyUnitIds.Count, MaxRendererUnitMismatchLogCount);
|
||
for (var i = 0; i < count; i++)
|
||
{
|
||
var unitId = _sharedRendererOnlyUnitIds[i];
|
||
sb.Append(" ").Append(i + 1).Append(". ");
|
||
if (mapRenderer.ROUnitMap.TryGetValue(unitId, out var renderer) && renderer != null)
|
||
sb.AppendLine(renderer.GetDiagnosticString(mapData));
|
||
else
|
||
sb.Append("uid=").Append(unitId).AppendLine(", renderer=null");
|
||
}
|
||
|
||
if (_sharedRendererOnlyUnitIds.Count > count)
|
||
sb.Append(" ... more=").Append(_sharedRendererOnlyUnitIds.Count - count).AppendLine();
|
||
}
|
||
|
||
private static void AppendDataOnlyUnitsLog(StringBuilder sb, MapData mapData, MapRenderer mapRenderer)
|
||
{
|
||
sb.Append("DataOnlyUnits(count=").Append(_sharedDataOnlyUnitIds.Count).AppendLine("):");
|
||
if (_sharedDataOnlyUnitIds.Count == 0) return;
|
||
|
||
var count = Math.Min(_sharedDataOnlyUnitIds.Count, MaxRendererUnitMismatchLogCount);
|
||
for (var i = 0; i < count; i++)
|
||
{
|
||
var unitId = _sharedDataOnlyUnitIds[i];
|
||
sb.Append(" ").Append(i + 1).Append(". ");
|
||
if (mapData.UnitMap.GetUnitDataByUnitId(unitId, out var unitData) && unitData != null)
|
||
AppendUnitDataLog(sb, mapData, unitData);
|
||
else
|
||
sb.Append("uid=").Append(unitId).Append(", dataMissingAfterCollect=true");
|
||
|
||
if (!mapRenderer.ROUnitMap.TryGetValue(unitId, out var renderer) || renderer == null)
|
||
sb.AppendLine(", rendererMissing=true");
|
||
else
|
||
sb.Append(", renderer=").AppendLine(renderer.GetDiagnosticString(mapData));
|
||
}
|
||
|
||
if (_sharedDataOnlyUnitIds.Count > count)
|
||
sb.Append(" ... more=").Append(_sharedDataOnlyUnitIds.Count - count).AppendLine();
|
||
}
|
||
|
||
private static void AppendUnitDataLog(StringBuilder sb, MapData mapData, UnitData unitData)
|
||
{
|
||
mapData.GetGridIdByUnitId(unitData.Id, out var gridId);
|
||
mapData.GetCityIdByUnitId(unitData.Id, out var cityId);
|
||
mapData.GetPlayerIdByUnitId(unitData.Id, out var playerId);
|
||
|
||
sb.Append("uid=").Append(unitData.Id)
|
||
.Append(", type=").Append(unitData.UnitType)
|
||
.Append("/")
|
||
.Append(unitData.GiantType)
|
||
.Append("/")
|
||
.Append(unitData.UnitLevel)
|
||
.Append(", hp=").Append(unitData.Health)
|
||
.Append(", alive=").Append(unitData.IsAlive())
|
||
.Append(", grid=").Append(gridId)
|
||
.Append(", city=").Append(cityId)
|
||
.Append(", player=").Append(playerId);
|
||
}
|
||
|
||
protected static void ReportUnitAttackRendererMissingAfterKill(
|
||
CommonActionParams actionParams,
|
||
CommonActionId actionId,
|
||
FragmentType fragmentType,
|
||
int attackDmg,
|
||
int counterDmg,
|
||
bool originAliveBefore,
|
||
bool targetAliveBefore,
|
||
bool originKilled,
|
||
bool targetKilled,
|
||
GridData originGrid,
|
||
GridData targetGrid,
|
||
UnitRenderer originUnitRenderer,
|
||
UnitRenderer targetUnitRenderer)
|
||
{
|
||
try
|
||
{
|
||
var mapData = actionParams.MapData;
|
||
if (mapData == null) return;
|
||
|
||
_sharedRendererUnitMismatchLogBuilder ??= new StringBuilder(2048);
|
||
var sb = _sharedRendererUnitMismatchLogBuilder;
|
||
sb.Clear();
|
||
sb.AppendLine("[UnitAttackRendererMissingAfterKill] 攻击击杀后Renderer缺失导致死亡动画无法入队");
|
||
sb.Append("MapId=").Append(mapData.MapID)
|
||
.Append(", ActionIndex=").Append(mapData.Net?.Actions?.Count ?? 0)
|
||
.Append(", FragmentType=").Append(fragmentType)
|
||
.Append(", AttackDmg=").Append(attackDmg)
|
||
.Append(", CounterDmg=").Append(counterDmg)
|
||
.Append(", OriginRendererMissing=").Append(originUnitRenderer == null)
|
||
.Append(", TargetRendererMissing=").Append(targetUnitRenderer == null)
|
||
.Append(", OriginGridMissing=").Append(originGrid == null)
|
||
.Append(", TargetGridMissing=").Append(targetGrid == null)
|
||
.Append(", OriginKilled=").Append(originKilled)
|
||
.Append(", TargetKilled=").Append(targetKilled)
|
||
.Append(", PresentationBusy=").Append(PresentationManager.Busy)
|
||
.Append(", BusyType=").Append(PresentationManager.BusyType ?? "null")
|
||
.AppendLine();
|
||
|
||
sb.AppendLine("CurrentAction:");
|
||
if (actionId != null)
|
||
sb.AppendLine(actionId.GetStringLog());
|
||
if (actionParams != null)
|
||
{
|
||
sb.AppendLine("CurrentParam:");
|
||
sb.AppendLine(actionParams.GetStringLog());
|
||
}
|
||
|
||
AppendRecentActionsLog(sb, mapData, 3);
|
||
|
||
sb.Append("Origin: aliveBefore=").Append(originAliveBefore)
|
||
.Append(", aliveAfter=").Append(actionParams.UnitData?.IsAlive())
|
||
.Append(", validAfter=").Append(actionParams.UnitData?.IsValidOnMap(mapData))
|
||
.Append(", renderer=");
|
||
if (originUnitRenderer != null)
|
||
sb.AppendLine(originUnitRenderer.GetDiagnosticString(mapData));
|
||
else
|
||
sb.AppendLine("null");
|
||
if (actionParams.UnitData != null)
|
||
{
|
||
sb.Append("OriginData: ");
|
||
AppendUnitDataLog(sb, mapData, actionParams.UnitData);
|
||
sb.Append(", landType=").Append(actionParams.UnitData.GetLandType())
|
||
.Append(", APAttack=").Append(actionParams.UnitData.GetActionPoint(ActionPointType.Attack))
|
||
.Append(", APMove=").Append(actionParams.UnitData.GetActionPoint(ActionPointType.Move))
|
||
.AppendLine();
|
||
}
|
||
|
||
sb.Append("Target: aliveBefore=").Append(targetAliveBefore)
|
||
.Append(", aliveAfter=").Append(actionParams.TargetUnitData?.IsAlive())
|
||
.Append(", validAfter=").Append(actionParams.TargetUnitData?.IsValidOnMap(mapData))
|
||
.Append(", renderer=");
|
||
if (targetUnitRenderer != null)
|
||
sb.AppendLine(targetUnitRenderer.GetDiagnosticString(mapData));
|
||
else
|
||
sb.AppendLine("null");
|
||
if (actionParams.TargetUnitData != null)
|
||
{
|
||
sb.Append("TargetData: ");
|
||
AppendUnitDataLog(sb, mapData, actionParams.TargetUnitData);
|
||
sb.Append(", landType=").Append(actionParams.TargetUnitData.GetLandType())
|
||
.AppendLine();
|
||
}
|
||
|
||
LogSystem.LogError(sb.ToString());
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
if (!_unitAttackRendererMissingAfterKillDiagnosticFailedLogged)
|
||
{
|
||
_unitAttackRendererMissingAfterKillDiagnosticFailedLogged = true;
|
||
LogSystem.LogWarning($"UnitAttackRendererMissingAfterKill diagnostic failed: {e}");
|
||
}
|
||
}
|
||
}
|
||
|
||
protected static void ReportUnitAttackAllyRendererMissingAfterKill(
|
||
CommonActionParams actionParams,
|
||
CommonActionId actionId,
|
||
bool originAliveBefore,
|
||
bool targetAliveBefore,
|
||
bool originKilled,
|
||
bool targetKilled,
|
||
bool handledByLifecycle,
|
||
bool notProjectile,
|
||
bool unit1Die,
|
||
GridData originGrid,
|
||
GridData targetGrid,
|
||
UnitRenderer originUnitRenderer,
|
||
UnitRenderer targetUnitRenderer)
|
||
{
|
||
try
|
||
{
|
||
var mapData = actionParams?.MapData;
|
||
if (mapData == null) return;
|
||
|
||
_sharedRendererUnitMismatchLogBuilder ??= new StringBuilder(2048);
|
||
var sb = _sharedRendererUnitMismatchLogBuilder;
|
||
sb.Clear();
|
||
sb.AppendLine("[UnitAttackAllyRendererMissingAfterKill] 友军攻击击杀后Renderer缺失导致死亡表现无法入队");
|
||
sb.Append("MapId=").Append(mapData.MapID)
|
||
.Append(", ActionIndex=").Append(mapData.Net?.Actions?.Count ?? 0)
|
||
.Append(", OriginRendererMissing=").Append(originUnitRenderer == null)
|
||
.Append(", TargetRendererMissing=").Append(targetUnitRenderer == null)
|
||
.Append(", OriginGridMissing=").Append(originGrid == null)
|
||
.Append(", TargetGridMissing=").Append(targetGrid == null)
|
||
.Append(", OriginKilled=").Append(originKilled)
|
||
.Append(", TargetKilled=").Append(targetKilled)
|
||
.Append(", HandledByLifecycle=").Append(handledByLifecycle)
|
||
.Append(", NotProjectile=").Append(notProjectile)
|
||
.Append(", Unit1DieFlag=").Append(unit1Die)
|
||
.Append(", PresentationBusy=").Append(PresentationManager.Busy)
|
||
.Append(", BusyType=").Append(PresentationManager.BusyType ?? "null")
|
||
.AppendLine();
|
||
|
||
sb.AppendLine("CurrentAction:");
|
||
if (actionId != null)
|
||
sb.AppendLine(actionId.GetStringLog());
|
||
if (actionParams != null)
|
||
{
|
||
sb.AppendLine("CurrentParam:");
|
||
sb.AppendLine(actionParams.GetStringLog());
|
||
}
|
||
|
||
AppendRecentActionsLog(sb, mapData, 3);
|
||
|
||
sb.Append("Origin: aliveBefore=").Append(originAliveBefore)
|
||
.Append(", aliveAfter=").Append(actionParams?.UnitData?.IsAlive())
|
||
.Append(", validAfter=").Append(actionParams?.UnitData?.IsValidOnMap(mapData))
|
||
.Append(", renderer=");
|
||
if (originUnitRenderer != null)
|
||
sb.AppendLine(originUnitRenderer.GetDiagnosticString(mapData));
|
||
else
|
||
sb.AppendLine("null");
|
||
if (actionParams?.UnitData != null)
|
||
{
|
||
sb.Append("OriginData: ");
|
||
AppendUnitDataLog(sb, mapData, actionParams.UnitData);
|
||
sb.Append(", landType=").Append(actionParams.UnitData.GetLandType())
|
||
.Append(", APAttack=").Append(actionParams.UnitData.GetActionPoint(ActionPointType.Attack))
|
||
.Append(", APMove=").Append(actionParams.UnitData.GetActionPoint(ActionPointType.Move))
|
||
.AppendLine();
|
||
}
|
||
|
||
sb.Append("Target: aliveBefore=").Append(targetAliveBefore)
|
||
.Append(", aliveAfter=").Append(actionParams?.TargetUnitData?.IsAlive())
|
||
.Append(", validAfter=").Append(actionParams?.TargetUnitData?.IsValidOnMap(mapData))
|
||
.Append(", renderer=");
|
||
if (targetUnitRenderer != null)
|
||
sb.AppendLine(targetUnitRenderer.GetDiagnosticString(mapData));
|
||
else
|
||
sb.AppendLine("null");
|
||
if (actionParams?.TargetUnitData != null)
|
||
{
|
||
sb.Append("TargetData: ");
|
||
AppendUnitDataLog(sb, mapData, actionParams.TargetUnitData);
|
||
sb.Append(", landType=").Append(actionParams.TargetUnitData.GetLandType())
|
||
.AppendLine();
|
||
}
|
||
|
||
LogSystem.LogError(sb.ToString());
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
if (!_unitAttackAllyRendererMissingAfterKillDiagnosticFailedLogged)
|
||
{
|
||
_unitAttackAllyRendererMissingAfterKillDiagnosticFailedLogged = true;
|
||
LogSystem.LogWarning($"UnitAttackAllyRendererMissingAfterKill diagnostic failed: {e}");
|
||
}
|
||
}
|
||
}
|
||
|
||
// 执行后逻辑
|
||
protected virtual void AfterExecute(CommonActionParams actionParams)
|
||
{
|
||
// 行为执行后触发技能生命周期
|
||
actionParams.MapData.OnActionExecuted(this, actionParams);
|
||
|
||
// 这里限制住了部分逻辑执行后的好感度刷新,尽量控制在每帧最多执行一次,17人局平均执行时长为 3ms
|
||
if (actionParams.MapData == Main.MapData && _actionId.ActionType != CommonActionType.TurnEnd
|
||
&& _actionId.ActionType != CommonActionType.TurnStart
|
||
&& _actionId.ActionType != CommonActionType.AIParamControl)
|
||
{
|
||
foreach (var pData in actionParams.MapData.PlayerMap.PlayerDataList)
|
||
{
|
||
pData.RefreshFeelingValue(actionParams.MapData);
|
||
pData.RefreshDiplomacyState(actionParams.MapData);
|
||
}
|
||
}
|
||
Main.PlayerLogic.CalcAllPlayerScore(actionParams.MapData);
|
||
|
||
//刷新当前玩家的每回合获取金币数
|
||
if (actionParams.MapData == Main.MapData && Main.MapData.CurPlayer == Main.MapData.PlayerMap.SelfPlayerData)
|
||
{
|
||
EventManager.Publish(new UpdateUITopTopBar() { UpdateType = UpdateTopBarType.UpdateCoinPerTurn });
|
||
EventManager.Publish(new UpdateUITopTopBar() { UpdateType = UpdateTopBarType.UpdateTechPerTurn });
|
||
EventManager.Publish(new UpdateUITopTopBar() { UpdateType = UpdateTopBarType.UpdateCulturePerTurn });
|
||
EventManager.Publish(new UpdateUITopTopBar() { UpdateType = UpdateTopBarType.UpdateFaith });
|
||
}
|
||
if (actionParams.MapData == Main.MapData)
|
||
{
|
||
// AI 演算不调用 PlayerLogic.Update,节省性能
|
||
Main.PlayerLogic.Update(actionParams.MapData);
|
||
MatchSettlementLogicFactory.RefreshMatchSettlementInfo(actionParams.MapData);
|
||
foreach (var item in actionParams.PlayerData.MomentData.Items)
|
||
{
|
||
item.OnActionExecuted(actionParams.MapData, this, actionParams);
|
||
}
|
||
}
|
||
|
||
//如果是教程 通知教程面板刷新任务完成情况
|
||
if (Main.MapData.MatchSettlement.SettlementType == MatchSettlementType.Tutor && actionParams.MapData == Main.MapData)
|
||
{
|
||
EventManager.Publish(new UpdateUIBottomTutor() { });
|
||
}
|
||
//更新Ranking界面的显示
|
||
if (actionParams.MapData == Main.MapData && Main.MapData.CurPlayer == Main.MapData.PlayerMap.SelfPlayerData)
|
||
{
|
||
EventManager.Publish(new UpdateUIBottomRanking() { });
|
||
}
|
||
|
||
#if CHECK_ACTIONDEFFERENCE
|
||
if (actionParams.MapData == Main.MapData)
|
||
{
|
||
byte[] bt = TH1Serialization.Serialize(Main.MapData);
|
||
Main.Instance.CheckMapData = TH1Serialization.Deserialize<MapData>(bt);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
public abstract bool CheckCan(CommonActionParams actionParams);
|
||
|
||
public virtual bool CheckShow(CommonActionParams actionParams,out ShowType showType)
|
||
{
|
||
showType = ShowType.None;
|
||
return CheckCan(actionParams);
|
||
}
|
||
|
||
public virtual ActionShowState CheckShowState(CommonActionParams actionParams) { return ActionShowState.None; }
|
||
|
||
public void TH1Debug()
|
||
{
|
||
Debug.Log($"{_actionId.ActionType} : {_actionId.ResourceType}/{_actionId.UnitType}/{_actionId.WonderType}");
|
||
}
|
||
}
|
||
|
||
|
||
// 建造奇观逻辑类 #buildwonder
|
||
public class BuildWonderAction : ActionLogicBase
|
||
{
|
||
|
||
|
||
public BuildWonderAction(CommonActionId id) : base(id)
|
||
{
|
||
|
||
}
|
||
|
||
protected override bool Execute(CommonActionParams actionParams)
|
||
{
|
||
//Step #0 鲁棒性处理
|
||
if(!actionParams.MapData.GetCityDataByTerritoryGid(actionParams.GridData.Id,out var city)) return false;
|
||
if (!Table.Instance.GridAndResourceDataAssets.GetWonderInfoByType(_actionId.WonderType,actionParams.PlayerData, out var wonderInfo)) return false;
|
||
if (actionParams.PlayerData == null) return false;
|
||
|
||
//Step #1 逻辑层:更新gridData相关信息,设置奇观完成情况
|
||
var player = actionParams.PlayerData;
|
||
actionParams.GridData.ResourceUnderBuilding = actionParams.GridData.Resource;
|
||
actionParams.GridData.Resource = ResourceType.Wonder;
|
||
actionParams.GridData.Wonder = wonderInfo.Wonder;
|
||
player.Wonder.SetWonderState(wonderInfo.WonderType,WonderState.FINISH_BUILD);
|
||
|
||
//Step #2 逻辑层+视觉层:城市获得3点经验
|
||
Main.CityLogic.UpdateGrid_ViewOnly(actionParams.MapData,actionParams.GridData);
|
||
|
||
Main.CityLogic.GridGiveCityExp_LogicView(actionParams.MapData,actionParams.PlayerData,actionParams.GridData,city,wonderInfo.Exp);
|
||
|
||
// moment
|
||
foreach (var item in player.MomentData.Items)
|
||
{
|
||
item.OnWonderBuild(actionParams.MapData, player, _actionId.WonderType);
|
||
}
|
||
|
||
//Step #3 更新成就完成情况
|
||
AchievementDataManager.Instance.OnBuildWonder(actionParams.MapData, player, city, actionParams.GridData);
|
||
try
|
||
{
|
||
if (actionParams.GridData.RealUnit(actionParams.MapData, out var wonderGridUnit))
|
||
BoatUnitOnLandDiagnostic.ReportIfNeeded(actionParams.MapData, wonderGridUnit, actionParams.GridData, $"BuildWonderAction:{_actionId.WonderType}");
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
LogSystem.LogWarning($"BuildWonderAction boat-on-land diagnostic failed: {e}");
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
public override bool CheckCan(CommonActionParams actionParam)
|
||
{
|
||
//鲁棒性+基础数据准备,必须准备player(谁来建设?)和grid(在哪里建设?)
|
||
if (actionParam.PlayerData == null) return false;
|
||
if (actionParam.GridData == null) return false;
|
||
var player = actionParam.PlayerData;
|
||
var grid = actionParam.GridData;
|
||
var map = actionParam.MapData;
|
||
//找到grid所属于的player,如果是无主领土就return
|
||
if (!map.GetPlayerDataByTerritoryGridId(grid.Id, out var gridPlayer))
|
||
return false;
|
||
//如果player操作的grid都不属于自己return
|
||
if (player != gridPlayer) return false;
|
||
//如果根本无法建设这个奇观 return
|
||
if (player.Wonder.GetWonderState(_actionId.WonderType) != WonderState.FINISH_NOT_BUILD) return false;
|
||
//如果是山地或者深海,无法建设这个奇观 return false(拥有NapoleonicCode可在森林建造)
|
||
bool wonderAllowForest = player.TechTree.CheckIfHasTechAtom(TechAtom.KaguyaFrenchNapoleonicCode);
|
||
if (grid.Feature == TerrainFeature.Mountain
|
||
|| grid.Terrain == TerrainType.DeepSea
|
||
|| (grid.Vegetation == Vegetation.Trees && !wonderAllowForest))
|
||
return false;
|
||
//如果有非联盟的敌人站在上面
|
||
if (grid.VisibleUnit(map,player, out var unit) && !map.CheckUnitIdBelongPlayerIdUnion(unit.Id, player.Id))
|
||
return false;
|
||
//如果上面是常规资源,那么就是可以建设的 return true
|
||
if (grid.Resource == ResourceType.None
|
||
|| grid.Resource == ResourceType.Fish
|
||
|| grid.Resource == ResourceType.Starfish
|
||
|| grid.Resource == ResourceType.Crop
|
||
|| grid.Resource == ResourceType.Fruit
|
||
)
|
||
return true;
|
||
if (GetCost(actionParam) > actionParam.PlayerData.PlayerCoin)
|
||
return false;
|
||
return false;
|
||
}
|
||
|
||
public override bool CheckShow(CommonActionParams actionParam,out ShowType showType)
|
||
{
|
||
showType = ShowType.None;
|
||
return CheckCan(actionParam);
|
||
}
|
||
|
||
public override ActionShowState CheckShowState(CommonActionParams actionParams) { return ActionShowState.None; }
|
||
|
||
}
|
||
|
||
|
||
// Gain 收获资源逻辑类
|
||
public class GainResourceAction : ActionLogicBase
|
||
{
|
||
public GainResourceAction(CommonActionId id) : base(id)
|
||
{
|
||
|
||
}
|
||
|
||
protected override bool Execute(CommonActionParams actionParams)
|
||
{
|
||
if (!CheckCan(actionParams)) return false;
|
||
CityData cityData = actionParams.CityData;
|
||
if (cityData == null)
|
||
actionParams.MapData.GetCityDataByTerritoryGid(actionParams.GridData.Id,out cityData);
|
||
|
||
//Step #1 逻辑层 扣钱,消除资源
|
||
actionParams.PlayerData.SpendCoin(GetCost(actionParams));
|
||
actionParams.GridData.Resource = ResourceType.None;
|
||
|
||
//Step #2 逻辑层 & 表现层 :gridupdate + city获得exp
|
||
var exp = Table.Instance.QueryActionExp(_actionId);
|
||
|
||
Main.CityLogic.UpdateGrid_ViewOnly(actionParams.MapData,actionParams.GridData);
|
||
|
||
Main.CityLogic.GridGiveCityExp_LogicView(actionParams.MapData,actionParams.PlayerData,actionParams.GridData,cityData,exp);
|
||
|
||
|
||
//更新所有建筑的buildingLevel
|
||
Main.PlayerLogic.UpdateTerritoryAllBuildingLevel(actionParams.MapData,actionParams.PlayerData.Id);
|
||
try
|
||
{
|
||
if (actionParams.GridData.RealUnit(actionParams.MapData, out var gainGridUnit))
|
||
BoatUnitOnLandDiagnostic.ReportIfNeeded(actionParams.MapData, gainGridUnit, actionParams.GridData, $"GainResourceAction:{_actionId.ResourceType}");
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
LogSystem.LogWarning($"GainResourceAction boat-on-land diagnostic failed: {e}");
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckCan(CommonActionParams actionParam)
|
||
{
|
||
//如果是unit或者city来执行gain操作,直接return false
|
||
if (actionParam.MainObjectType != MainObjectType.Grid) return false;
|
||
var grid = actionParam.GridData;
|
||
var map = actionParam.MapData;
|
||
var player = actionParam.PlayerData;
|
||
if (grid == null || map == null || player == null) return false;
|
||
|
||
//如果没有对应科技,return false
|
||
if (!player.TechTree.CheckActionCan(_actionId)) return false;
|
||
//确认grid实际属于的player是谁
|
||
map.GetPlayerDataByTerritoryGridId(actionParam.GridData.Id, out var gridPlayer);
|
||
//如果不在我方领土,return false
|
||
if (gridPlayer == null || player.Id != gridPlayer.Id) return false;
|
||
//钱不够 return
|
||
if (GetCost(actionParam) > actionParam.PlayerData.PlayerCoin)
|
||
return false;
|
||
//如果有非联盟的敌人站在上面
|
||
if (grid.VisibleUnit(map,player, out var unit) && !map.CheckUnitIdBelongPlayerIdUnion(unit.Id, player.Id))
|
||
return false;
|
||
|
||
return _actionId.ResourceType == actionParam.GridData.Resource;
|
||
}
|
||
|
||
public override bool CheckShow(CommonActionParams actionParam,out ShowType showType)
|
||
{
|
||
showType = ShowType.None;
|
||
if (actionParam.MainObjectType != MainObjectType.Grid) return false;
|
||
var grid = actionParam.GridData;
|
||
var map = actionParam.MapData;
|
||
var player = actionParam.PlayerData;
|
||
if (grid == null || map == null || player == null) return false;
|
||
|
||
//确认grid实际属于的player是谁
|
||
map.GetPlayerDataByTerritoryGridId(actionParam.GridData.Id, out var gridPlayer);
|
||
//如果不在我方领土,return false
|
||
if (gridPlayer == null || player.Id != gridPlayer.Id) return false;
|
||
|
||
//如果有非联盟的敌人站在上面
|
||
if (grid.VisibleUnit(map,player, out var unit) && !map.CheckUnitIdBelongPlayerIdUnion(unit.Id, player.Id))
|
||
return false;
|
||
//如果techpool里就没有这个技能,不显示
|
||
if (!Table.Instance.PlayerDataAssets.CheckActionInTechPool(_actionId, actionParam.PlayerData)) return false;
|
||
|
||
//资源类型不匹配,不显示
|
||
if (_actionId.ResourceType != actionParam.GridData.Resource) return false;
|
||
|
||
//没有科技,显示为灰色锁定
|
||
if (!player.TechTree.CheckActionCan(_actionId)) showType = ShowType.Locked;
|
||
//钱不够,显示为Cost
|
||
else if (GetCost(actionParam) > player.PlayerCoin) showType = ShowType.Cost;
|
||
|
||
return true;
|
||
}
|
||
|
||
public override ActionShowState CheckShowState(CommonActionParams actionParams) { return ActionShowState.None; }
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
//CityLevelUp 行动逻辑类
|
||
public class CityLevelUpActionAction : ActionLogicBase
|
||
{
|
||
public CityLevelUpActionAction(CommonActionId id) : base(id)
|
||
{
|
||
|
||
}
|
||
|
||
public static UnitFullType GetBigGuyCityLevelUpUnitFullType(PlayerData playerData)
|
||
{
|
||
if (playerData == null)
|
||
return new UnitFullType(UnitType.BigGuy, GiantType.None, 0);
|
||
|
||
return GetBigGuyCityLevelUpUnitFullType(playerData.PlayerCivId, playerData.PlayerForceId);
|
||
}
|
||
|
||
public static UnitFullType GetBigGuyCityLevelUpUnitFullType(uint civId, uint forceId)
|
||
{
|
||
// Multiple players may share the same Empire; the spawned special BigGuy unit is keyed by Empire.
|
||
if (civId == 1 && forceId == 1)
|
||
return new UnitFullType(UnitType.KaguyaFrenchWolf, GiantType.None, 0);
|
||
if (civId == 0 && forceId == 0)
|
||
return new UnitFullType(UnitType.RemiliaEgyptianKoakuma, GiantType.None, 0);
|
||
if (civId == 2 && forceId == 2)
|
||
return new UnitFullType(UnitType.MoriyaHebi, GiantType.None, 3);
|
||
if (civId == 3 && forceId == 3)
|
||
return new UnitFullType(UnitType.KomeijiIndianBigGuy, GiantType.None, 0);
|
||
if (civId == 4 && forceId == 4)
|
||
return new UnitFullType(UnitType.HakureiValkyrie, GiantType.None, 0);
|
||
|
||
return new UnitFullType(UnitType.BigGuy, GiantType.None, 0);
|
||
}
|
||
|
||
public override bool CompleteExecute(CommonActionParams actionParams)
|
||
{
|
||
if (!CheckCanBeforeExecute(actionParams, "CompleteExecute")) return false;
|
||
return base.CompleteExecute(actionParams);
|
||
}
|
||
|
||
public override bool NetCompleteExecute(CommonActionParams actionParams)
|
||
{
|
||
if (!CheckCanBeforeExecute(actionParams, "NetCompleteExecute")) return false;
|
||
return base.NetCompleteExecute(actionParams);
|
||
}
|
||
|
||
private bool CheckCanBeforeExecute(CommonActionParams actionParams, string context)
|
||
{
|
||
if (actionParams != null && CheckCan(actionParams)) return true;
|
||
if (actionParams?.MapData == Main.MapData)
|
||
{
|
||
LogSystem.LogError($"CityLevelUpAction CheckCan failed before {context}: {ActionId.GetStringLog()}\nParam:\n{actionParams?.GetStringLog()}");
|
||
}
|
||
return false;
|
||
}
|
||
|
||
protected override bool Execute(CommonActionParams actionParams)
|
||
{
|
||
//必须传入参数 paramMap paramCity
|
||
//需要参数:paramMap paramCity paramGrid paramPlayer
|
||
|
||
//鲁棒性判断
|
||
if (actionParams.CityData == null) return false;
|
||
if (!actionParams.MapData.GetGridDataByCityId(actionParams.CityData.Id, out var paramGrid)) return false;
|
||
if (actionParams.CityData.CityLevelUpPoint <= 0) return false;
|
||
if (!actionParams.MapData.GetPlayerDataByCityId(actionParams.CityData.Id, out var paramPlayer)) return false;
|
||
var paramMap = actionParams.MapData;
|
||
var paramCity = actionParams.CityData;
|
||
actionParams.CityData.CityLevelUpPoint --;
|
||
var cityLevelUpSucceeded = false;
|
||
|
||
//不管哪个选项,都播放音效
|
||
if (actionParams.CityData.Grid(actionParams.MapData)?.InMainSight() ?? false)
|
||
{
|
||
AudioManager.Instance.PlayAudio("SFX/UNIT_treasure");
|
||
}
|
||
|
||
|
||
if (_actionId.CityLevelUpActionType == CityLevelUpActionType.Explorer)
|
||
{
|
||
// 1. 锁定玩家输入
|
||
//main.InputLogic.LockInput();
|
||
|
||
// 2. 创建探索者,然后一边处理逻辑一边处理视觉,这里暂时用了maprenderer 来负责
|
||
MapRenderer.Instance.CreateTemporaryExplorer(actionParams.MapData,paramGrid, 3f);
|
||
|
||
cityLevelUpSucceeded = true;
|
||
}
|
||
else if (_actionId.CityLevelUpActionType == CityLevelUpActionType.Workshop)
|
||
{
|
||
paramCity.Workshop = true;
|
||
paramCity.SetCityRenderer(actionParams.MapData);
|
||
if((paramGrid?.InMainSight()??false) && paramPlayer == Main.MapData.PlayerMap.SelfPlayerData)
|
||
paramGrid.Renderer(paramMap)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Coin));
|
||
cityLevelUpSucceeded = true;
|
||
}
|
||
else if (_actionId.CityLevelUpActionType == CityLevelUpActionType.CityWealth)
|
||
{
|
||
if(PresentationManager.GetCityLevelupActionPos(_actionId.CityLevelUpActionType,out var pos))
|
||
paramPlayer.AddCoin(5,pos);
|
||
else
|
||
paramPlayer.AddCoin(5);
|
||
paramCity.SetCityRenderer(paramMap);
|
||
cityLevelUpSucceeded = true;
|
||
}
|
||
|
||
else if (_actionId.CityLevelUpActionType == CityLevelUpActionType.CityWall)
|
||
{
|
||
paramPlayer.AddCulturePoint(2);
|
||
cityLevelUpSucceeded = true;
|
||
}
|
||
else if (_actionId.CityLevelUpActionType == CityLevelUpActionType.Expand)
|
||
{
|
||
Main.CityLogic.CityLevelUpActionExpand(actionParams.MapData,actionParams.CityData);
|
||
actionParams.CityData.SetCityRenderer(actionParams.MapData);
|
||
|
||
|
||
|
||
//要更新玩家所有城市所有格子的levelbuilding
|
||
Main.PlayerLogic.UpdateTerritoryAllBuildingLevel(actionParams.MapData,paramPlayer.Id);
|
||
|
||
//更新所有国家的联通情况
|
||
Main.PlayerLogic.UpdateAllPlayerConnected(actionParams.MapData);
|
||
|
||
//更新特殊占领技能
|
||
if(paramPlayer.TechTree.CheckIfHasTechAtom(TechAtom.KaguyaFrenchNapoleonicCode))
|
||
Main.PlayerLogic.SetCityTerritoryGridSp(actionParams.MapData,actionParams.CityData,GridSpType.KaguyaGrid);
|
||
|
||
cityLevelUpSucceeded = true;
|
||
}
|
||
else if (_actionId.CityLevelUpActionType == CityLevelUpActionType.Population)
|
||
{
|
||
//Step #2 逻辑层 & 表现层 :city获得exp
|
||
var exp = 3;
|
||
Main.CityLogic.GridGiveCityExp_LogicView(actionParams.MapData,paramPlayer,actionParams.CityData?.Grid(actionParams.MapData),actionParams.CityData,exp);
|
||
cityLevelUpSucceeded = true;
|
||
}
|
||
|
||
else if (_actionId.CityLevelUpActionType == CityLevelUpActionType.Park)
|
||
{
|
||
actionParams.CityData.ParkCount++;
|
||
actionParams.CityData.SetCityRenderer(actionParams.MapData);
|
||
if((paramGrid?.InMainSight()??false) && paramPlayer == Main.MapData.PlayerMap.SelfPlayerData)
|
||
paramGrid.Renderer(paramMap)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Luxury));
|
||
cityLevelUpSucceeded = true;
|
||
}
|
||
|
||
else if (_actionId.CityLevelUpActionType == CityLevelUpActionType.BigGuy)
|
||
{
|
||
if(paramGrid.RealUnit(paramMap,out var unitData))
|
||
Main.UnitLogic.PassiveMoveAway(paramMap, unitData);
|
||
var fullType = GetBigGuyCityLevelUpUnitFullType(paramPlayer);
|
||
if (!actionParams.MapData.AddUnitData(paramGrid.Id, actionParams.CityData.Id,fullType ,out var _))
|
||
return false;
|
||
paramCity.SetCityRenderer(paramMap);
|
||
cityLevelUpSucceeded = true;
|
||
}
|
||
|
||
if (!cityLevelUpSucceeded) return false;
|
||
PresentationManager.EnqueuePendingCityLevelUpChoice(actionParams.MapData, actionParams.CityData);
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckCan(CommonActionParams actionParams)
|
||
{
|
||
if (actionParams.CityData == null) return false;
|
||
//如果没有升级行动的点数,退出
|
||
if (actionParams.CityData.CityLevelUpPoint <= 0)
|
||
return false;
|
||
//用于处理连续升级的情况
|
||
var addition = actionParams.CityData.CityLevelUpPoint - 1;
|
||
if (_actionId.CityLevelUpActionType == CityLevelUpActionType.Explorer)
|
||
return actionParams.CityData.Level == 3 + addition;
|
||
if (_actionId.CityLevelUpActionType == CityLevelUpActionType.Workshop)
|
||
return actionParams.CityData.Level == 3 + addition;
|
||
if (_actionId.CityLevelUpActionType == CityLevelUpActionType.CityWall)
|
||
return actionParams.CityData.Level == 4 + addition;
|
||
if (_actionId.CityLevelUpActionType == CityLevelUpActionType.CityWealth)
|
||
return actionParams.CityData.Level == 4 + addition;
|
||
if (_actionId.CityLevelUpActionType == CityLevelUpActionType.Population)
|
||
return actionParams.CityData.Level == 5 + addition;
|
||
if (_actionId.CityLevelUpActionType == CityLevelUpActionType.Expand)
|
||
return actionParams.CityData.Level == 5 + addition;
|
||
if (_actionId.CityLevelUpActionType == CityLevelUpActionType.Park)
|
||
return actionParams.CityData.Level > 5 + addition;
|
||
if (_actionId.CityLevelUpActionType == CityLevelUpActionType.BigGuy)
|
||
return actionParams.CityData.Level > 5 + addition;
|
||
return false;
|
||
}
|
||
public override bool CheckShow(CommonActionParams actionParams,out ShowType showType)
|
||
{
|
||
showType = ShowType.None;
|
||
if (actionParams.CityData == null) return false;
|
||
//如果没有升级行动的点数,退出
|
||
if (actionParams.CityData.CityLevelUpPoint <= 0)
|
||
return false;
|
||
var addition = actionParams.CityData.CityLevelUpPoint - 1;
|
||
if (_actionId.CityLevelUpActionType == CityLevelUpActionType.Explorer)
|
||
return actionParams.CityData.Level == 3 + addition;
|
||
if (_actionId.CityLevelUpActionType == CityLevelUpActionType.Workshop)
|
||
return actionParams.CityData.Level == 3 + addition;
|
||
if (_actionId.CityLevelUpActionType == CityLevelUpActionType.CityWall)
|
||
return actionParams.CityData.Level == 4 + addition;
|
||
if (_actionId.CityLevelUpActionType == CityLevelUpActionType.CityWealth)
|
||
return actionParams.CityData.Level == 4 + addition;
|
||
if (_actionId.CityLevelUpActionType == CityLevelUpActionType.Population)
|
||
return actionParams.CityData.Level == 5 + addition;
|
||
if (_actionId.CityLevelUpActionType == CityLevelUpActionType.Expand)
|
||
return actionParams.CityData.Level == 5 + addition;
|
||
if (_actionId.CityLevelUpActionType == CityLevelUpActionType.Park)
|
||
return actionParams.CityData.Level > 5 + addition;
|
||
if (_actionId.CityLevelUpActionType == CityLevelUpActionType.BigGuy)
|
||
return actionParams.CityData.Level > 5 + addition;
|
||
return false;
|
||
}
|
||
|
||
public override ActionShowState CheckShowState(CommonActionParams actionParams) { return ActionShowState.None; }
|
||
}
|
||
|
||
|
||
// 小兵移动
|
||
public class UnitMoveAction : ActionLogicBase
|
||
{
|
||
public UnitMoveAction(CommonActionId id) : base(id)
|
||
{
|
||
}
|
||
|
||
protected override bool Execute(CommonActionParams actionParams)
|
||
{
|
||
//Step #0 存储服务于anim的数据
|
||
GridData originGrid = null;
|
||
GridData targetGrid = null;
|
||
UnitData unit = null;
|
||
unit = actionParams.UnitData;
|
||
originGrid = unit?.Grid(actionParams.MapData);
|
||
targetGrid = actionParams.GridData;
|
||
if (unit == null || originGrid == null || targetGrid == null) return false;
|
||
if (!unit.IsAlive())
|
||
{
|
||
LogSystem.LogError($"unit is dead , cannot move!");
|
||
return false;
|
||
}
|
||
|
||
//提前记录player,因为有可能移动后unit就死了
|
||
PlayerData player = unit.Player(actionParams.MapData);
|
||
if (player == null) return false;
|
||
|
||
//Step #1记录路径 确定MoveType
|
||
var path1 = new List<Vector2Int>();
|
||
//TODO 这里有风险,movepath依赖一个在时序上完全无关的 movecostinfo的计算
|
||
Main.UnitLogic.CalcUnitMoveInfo(actionParams.MapData, unit.Id);
|
||
MoveAttackType moveAttackType = Main.UnitLogic.GetMovePath(actionParams.MapData, originGrid.Pos.V2(), targetGrid.Pos.V2(), out path1);
|
||
MoveType moveType = moveAttackType switch
|
||
{
|
||
MoveAttackType.MoveTeleport => MoveType.SkillMove,
|
||
_ => MoveType.ActiveMove
|
||
};
|
||
|
||
//Step #2 处理移动数据
|
||
bool isMove = Main.UnitLogic.MoveToLogic(actionParams.MapData, actionParams.UnitData, actionParams.GridData, moveType, path1);
|
||
if (!isMove) return false;
|
||
|
||
//Step #3 将移动动画加入队列
|
||
bool need = Main.MapData == actionParams.MapData;
|
||
//如果数据出错 不播放动画
|
||
if (originGrid == null || targetGrid == null || unit == null) need = false;
|
||
|
||
//如果整个路径都在视野外,不播放动画
|
||
if (!Main.MapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(originGrid.Id) &&
|
||
!Main.MapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(targetGrid.Id)) need = false;
|
||
// UtsuhoBase的移动动画由UtsuhoReadyMoveSkill自行控制
|
||
if (unit.GetSkill(SkillType.UtsuhoBase, out var _)) need = false;
|
||
if (need)
|
||
{
|
||
//Step #2-1 处理闪现类动画
|
||
if (moveAttackType == MoveAttackType.MoveTeleport)
|
||
{
|
||
MapRenderer.Instance.ROUnitMap.TryGetValue(unit.Id, out var uintRenderer);
|
||
var fragment = FragmentFactory.Create(FragmentType.MoveTeleport,FragmentDataFactory.Create(FragmentType.Move,uintRenderer,originGrid,targetGrid,path1));
|
||
PresentationManager.EnqueueTask(new FragmentSequencerTask(fragment));
|
||
}
|
||
//Step #2-2 将移动动画加入队列
|
||
if (moveAttackType == MoveAttackType.Move)
|
||
{
|
||
MapRenderer.Instance.ROUnitMap.TryGetValue(unit.Id, out var uintRenderer);
|
||
var fragment = FragmentFactory.Create(FragmentType.Move,FragmentDataFactory.Create(FragmentType.Move,uintRenderer,originGrid,targetGrid,path1));
|
||
PresentationManager.EnqueueTask(new FragmentSequencerTask(fragment));
|
||
|
||
}
|
||
}
|
||
|
||
//Step #4 更新Sight的数据,同时将Sight相关动画加入演出队列
|
||
//更新路径视野
|
||
foreach (var pathnode in path1)
|
||
//需要传入player,因为可能unitData已经死了
|
||
actionParams.PlayerData.Sight.UpdateSightByPath(actionParams.UnitData,player,pathnode,actionParams.MapData);
|
||
|
||
//Step #5 Moment
|
||
if (actionParams.MapData == Main.MapData
|
||
&& targetGrid.CityOnGrid(actionParams.MapData,out var targetCity)
|
||
&& targetCity.Player(actionParams.MapData) == Main.MapData.PlayerMap.SelfPlayerData
|
||
&& unit.Player(actionParams.MapData) != Main.MapData.PlayerMap.SelfPlayerData)
|
||
{
|
||
EventManager.Publish(new ShowUINotifyMoment()
|
||
{
|
||
MomentSubType = MomentSubType.BattleCityFire,
|
||
Empire = Main.MapData.PlayerMap.SelfPlayerData.Empire
|
||
});
|
||
}
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckCan(CommonActionParams actionParams)
|
||
{
|
||
|
||
// TODO 需要完整更新CheckCan
|
||
var unit = actionParams.UnitData;
|
||
var map = actionParams.MapData;
|
||
var player = actionParams.PlayerData;
|
||
var grid = actionParams.GridData;
|
||
if (unit is null || map is null || grid is null || player is null) return false;
|
||
|
||
|
||
//如果grid上有可见的敌人,returnfalse
|
||
if (grid.VisibleUnit(map,player, out var _)) return false;
|
||
|
||
|
||
var unitPlayer = unit.Player(map);
|
||
if (unitPlayer != player) return false;
|
||
|
||
//判断是否只能在我方领土上移动(目前仅用于suwako)
|
||
if (unit.IsLimitMoveToSelfTerrain(map) && grid.Player(map) != unit.Player(map)) return false;
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckShow(CommonActionParams actionParams,out ShowType showType)
|
||
{
|
||
showType = ShowType.None;
|
||
return false;
|
||
}
|
||
|
||
public override ActionShowState CheckShowState(CommonActionParams actionParams) { return ActionShowState.None; }
|
||
public override bool CameraControl(CommonActionParams actionParams)
|
||
{
|
||
var main = GameObject.Find("Main").GetComponent<Main>();
|
||
if (actionParams.GridData != null && main != null && MapRenderer.Instance.CameraController != null
|
||
&& actionParams.MapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(actionParams.GridData.Id))
|
||
MapRenderer.Instance.CameraController?.CameraFocusOnGrid(actionParams.GridData);
|
||
return true;
|
||
}
|
||
}
|
||
|
||
public class UnitPassiveMoveAction : ActionLogicBase
|
||
{
|
||
public UnitPassiveMoveAction(CommonActionId id) : base(id)
|
||
{
|
||
}
|
||
|
||
protected override bool Execute(CommonActionParams actionParams)
|
||
{
|
||
//Step #0 存储服务于anim的数据
|
||
GridData originGrid = null;
|
||
GridData targetGrid = null;
|
||
UnitData unit = null;
|
||
originGrid = actionParams.UnitData.Grid(actionParams.MapData);
|
||
targetGrid = actionParams.GridData;
|
||
unit = actionParams.UnitData;
|
||
//提前记录player,因为unit可能在移动后直接死了
|
||
PlayerData player = unit.Player(actionParams.MapData);
|
||
|
||
|
||
//Step #1 处理移动数据
|
||
Main.UnitLogic.MoveToLogic(actionParams.MapData, actionParams.UnitData, actionParams.GridData,MoveType.PassiveMove);
|
||
//记录路径
|
||
var path1 = new List<Vector2Int>();
|
||
path1.Add(originGrid.Pos.V2());
|
||
path1.Add(targetGrid.Pos.V2());
|
||
//Main.UnitLogic.GetMovePath(actionParams.MapData, originGrid.Pos.V2(), targetGrid.Pos.V2(), out path1);
|
||
|
||
//Step #2 将移动动画加入队列
|
||
bool need = Main.MapData == actionParams.MapData;
|
||
//如果数据出错 不播放动画
|
||
if (originGrid == null || targetGrid == null || unit == null) need = false;
|
||
//如果在迷雾 不播放动画
|
||
if (!Main.MapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(originGrid.Id) &&
|
||
!Main.MapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(targetGrid.Id)) need = false;
|
||
//如果单位不可见 不播房动画
|
||
if(unit.IsHideAndCantSee(actionParams.MapData,Main.MapData.PlayerMap.SelfPlayerData))
|
||
need = false;
|
||
if (need)
|
||
{
|
||
MapRenderer.Instance.ROUnitMap.TryGetValue(unit.Id, out var uintRenderer);
|
||
var fragment = FragmentFactory.Create(FragmentType.Move,FragmentDataFactory.Create(FragmentType.Move,uintRenderer,originGrid,targetGrid,path1));
|
||
//FragmentManager.Instance.AddFragment(fragment);
|
||
PresentationManager.EnqueueTask(new FragmentSequencerTask(fragment));
|
||
}
|
||
|
||
//Step #3 更新Sight的数据,同时将Sight相关动画加入演出队列
|
||
//更新路径视野
|
||
foreach (var pathnode in path1)
|
||
actionParams.PlayerData.Sight.UpdateSightByPath(actionParams.UnitData,player,pathnode,actionParams.MapData);
|
||
|
||
|
||
|
||
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckCan(CommonActionParams actionParams)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckShow(CommonActionParams actionParams,out ShowType showType)
|
||
{
|
||
showType = ShowType.None;
|
||
return false;
|
||
}
|
||
}
|
||
|
||
internal static class ActiveAttackActionRecorder
|
||
{
|
||
public static void MarkStarted(MapData mapData, PlayerData player)
|
||
{
|
||
if (mapData == null || player == null) return;
|
||
player.TurnNoAttack = 0;
|
||
}
|
||
|
||
public static void MarkStarted(MapData mapData, UnitData unit)
|
||
{
|
||
if (mapData == null || unit == null) return;
|
||
var player = unit.Player(mapData);
|
||
MarkStarted(mapData, player);
|
||
}
|
||
}
|
||
|
||
public class UnitAttackAction : ActionLogicBase
|
||
{
|
||
public UnitAttackAction(CommonActionId id) : base(id)
|
||
{
|
||
}
|
||
|
||
protected override bool Execute(CommonActionParams actionParams)
|
||
{
|
||
//Step #0 鲁棒性保护
|
||
if (actionParams?.UnitData == null || actionParams.TargetUnitData == null || actionParams.MapData == null) return false;
|
||
ActiveAttackActionRecorder.MarkStarted(actionParams.MapData, actionParams.UnitData);
|
||
|
||
// 开启 PendingAnimScope,逻辑运算期间技能产生的动画步骤会收集到 scope 中
|
||
using var scope = PresentationManager.BeginScope();
|
||
using var visualCollector = ActionVisualEventCollector.Begin(
|
||
actionParams.MapData, actionParams.UnitData.Id, actionParams.TargetUnitData.Id);
|
||
|
||
//Step #0 为anim做数据准备
|
||
GridData originGrid = null;
|
||
GridData targetGrid = null;
|
||
CityData originCity = null;
|
||
CityData targetCity = null;
|
||
UnitRenderer originUnitRenderer = null;
|
||
UnitRenderer targetUnitRenderer = null;
|
||
ProjectileType originUnitProjectileType = ProjectileType.None;
|
||
ProjectileType targetUnitProjectileType = ProjectileType.None;
|
||
PlayerData originPlayer = null;
|
||
|
||
//如果不是AI预测相关的行为
|
||
if (actionParams.MapData == Main.MapData)
|
||
{
|
||
originGrid = actionParams.UnitData.Grid(Main.MapData);
|
||
targetGrid = actionParams.TargetUnitData.Grid(Main.MapData);
|
||
originCity = actionParams.UnitData.City(Main.MapData);
|
||
targetCity = actionParams.TargetUnitData.City(Main.MapData);
|
||
originPlayer = actionParams.UnitData.Player(Main.MapData);
|
||
MapRenderer.Instance?.ROUnitMap?.TryGetValue(actionParams.UnitData.Id, out originUnitRenderer);
|
||
MapRenderer.Instance?.ROUnitMap?.TryGetValue(actionParams.TargetUnitData.Id, out targetUnitRenderer);
|
||
if (targetGrid != null)
|
||
{
|
||
originUnitProjectileType = actionParams.UnitData.GetProjectileType(actionParams.MapData,targetGrid);
|
||
targetUnitProjectileType = actionParams.TargetUnitData.GetProjectileType(actionParams.MapData,targetGrid);
|
||
}
|
||
}
|
||
|
||
var originAliveBeforeAttack = actionParams.UnitData.IsAlive();
|
||
var targetAliveBeforeAttack = actionParams.TargetUnitData.IsAlive();
|
||
|
||
//Step #2 处理逻辑计算,提前存储anim使用到的数据
|
||
bool targetCanNotBeKilled = !actionParams.TargetUnitData.CanBeKilled(actionParams.MapData);
|
||
Main.UnitLogic.Attack(actionParams.MapData, actionParams.UnitData, actionParams.TargetUnitData, out var attackDmg,out var counterDmg,out var fragmentType, out var yuugiPushResult);
|
||
var originKilledByAttack = originAliveBeforeAttack && !actionParams.UnitData.IsAlive();
|
||
var targetKilledByAttack = targetAliveBeforeAttack && !actionParams.TargetUnitData.IsAlive();
|
||
|
||
|
||
//Step #3 处理攻击动画
|
||
//如果是AI预测行为,return(scope.Dispose会自动清理pending steps)
|
||
if (actionParams.MapData != Main.MapData) return true;
|
||
//如果数据出错,return
|
||
if (originGrid == null || targetGrid == null || originUnitRenderer == null || targetUnitRenderer == null)
|
||
{
|
||
if (originKilledByAttack || targetKilledByAttack)
|
||
{
|
||
ReportUnitAttackRendererMissingAfterKill(
|
||
actionParams,
|
||
_actionId,
|
||
fragmentType,
|
||
attackDmg,
|
||
counterDmg,
|
||
originAliveBeforeAttack,
|
||
targetAliveBeforeAttack,
|
||
originKilledByAttack,
|
||
targetKilledByAttack,
|
||
originGrid,
|
||
targetGrid,
|
||
originUnitRenderer,
|
||
targetUnitRenderer);
|
||
|
||
try
|
||
{
|
||
if (targetKilledByAttack && targetUnitRenderer != null)
|
||
targetUnitRenderer.Die();
|
||
if (originKilledByAttack && originUnitRenderer != null)
|
||
originUnitRenderer.Die();
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
LogSystem.LogWarning($"UnitAttackRendererMissingAfterKill cleanup failed: {e}");
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
//Step #3-1 检测YuugiPush是否发生了推动。推击致死时 target 已经从数据层移除,
|
||
//不能再靠 target 当前 grid 推断,必须使用 YuugiPushSkill 返回的移动结果。
|
||
var targetGridAfter = actionParams.TargetUnitData.Grid(Main.MapData);
|
||
bool pushed = yuugiPushResult is { Pushed: true, TargetGridAfterPush: not null };
|
||
if (pushed && targetGridAfter == null)
|
||
targetGridAfter = yuugiPushResult.TargetGridAfterPush;
|
||
|
||
//如果在视野,播放动画
|
||
if (Main.MapData?.PlayerMap?.SelfPlayerData?.Sight != null &&
|
||
(Main.MapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(originGrid.Id) ||
|
||
Main.MapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(targetGrid.Id)))
|
||
{
|
||
//如果发生了推动,先播放双方的移动动画,再播放攻击动画。
|
||
//推击可能致死:targetGridAfter 可能来自存活单位的当前格,也可能来自 YuugiPushResult 中记录的死亡格。
|
||
if (pushed && targetGridAfter != null)
|
||
{
|
||
//target往后退一格的移动动画
|
||
var targetMovePath = new List<Vector2Int> { targetGrid.Pos.V2(), targetGridAfter.Pos.V2() };
|
||
var targetMoveData = FragmentDataFactory.Create(FragmentType.Move, targetUnitRenderer, targetGrid, targetGridAfter, targetMovePath);
|
||
var targetMoveFragment = FragmentFactory.Create(FragmentType.Move, targetMoveData);
|
||
PresentationManager.EnqueueTask(new FragmentSequencerTask(targetMoveFragment));
|
||
|
||
//origin往前进一格的移动动画
|
||
var originMovePath = new List<Vector2Int> { originGrid.Pos.V2(), targetGrid.Pos.V2() };
|
||
var originMoveData = FragmentDataFactory.Create(FragmentType.Move, originUnitRenderer, originGrid, targetGrid, originMovePath);
|
||
var originMoveFragment = FragmentFactory.Create(FragmentType.Move, originMoveData);
|
||
PresentationManager.EnqueueTask(new FragmentSequencerTask(originMoveFragment));
|
||
|
||
//攻击动画使用移动后的位置:origin在targetGrid,target在targetGridAfter
|
||
var attackData = FragmentDataFactory.Create(fragmentType, originUnitRenderer,
|
||
targetUnitRenderer, targetGrid, targetGridAfter, originUnitProjectileType, targetUnitProjectileType, attackDmg,
|
||
counterDmg, originCity, targetCity, targetCanNotBeKilled);
|
||
var attackFragment = FragmentFactory.Create(fragmentType, attackData);
|
||
// 将技能产生的 pending steps 注入到攻击 fragment 中
|
||
scope.FlushTo(attackFragment);
|
||
visualCollector?.FlushTo(attackFragment);
|
||
PresentationManager.EnqueueTask(new FragmentSequencerTask(attackFragment));
|
||
_duration = attackFragment.Duration;
|
||
}
|
||
else
|
||
{
|
||
var data = fragmentType switch
|
||
{
|
||
FragmentType.Attack or FragmentType.AttackAndCounter or FragmentType.MoveKill or FragmentType.NotMoveKill or FragmentType.AttackAndCounterDie
|
||
=> FragmentDataFactory.Create(fragmentType, originUnitRenderer,
|
||
targetUnitRenderer, originGrid, targetGrid, originUnitProjectileType, targetUnitProjectileType, attackDmg,
|
||
counterDmg,originCity,targetCity,targetCanNotBeKilled),
|
||
_ => null
|
||
};
|
||
var fragment = FragmentFactory.Create(fragmentType,data);
|
||
// 将技能产生的 pending steps 注入到攻击 fragment 中
|
||
scope.FlushTo(fragment);
|
||
visualCollector?.FlushTo(fragment);
|
||
PresentationManager.EnqueueTask(new FragmentSequencerTask(fragment));
|
||
_duration = fragment.Duration;
|
||
}
|
||
}
|
||
|
||
//Step #4 处理视野变化(move kill / push move)
|
||
//如果MoveKill,要给unit所在的player更新sight,如果学者攻击,也是会更新sight
|
||
if(fragmentType == FragmentType.MoveKill)
|
||
//传入originPlayer, 避免unit已经死了,无法通过unit拿到player的情况
|
||
actionParams.UnitData.Player(actionParams.MapData)?.Sight.UpdateSightByPath(actionParams.UnitData,originPlayer,targetGrid.Pos.V2(),actionParams.MapData);
|
||
//如果YuugiPush推动发生了,攻击者也移动了,需要更新视野
|
||
else if(pushed)
|
||
originPlayer?.Sight.UpdateSightByPath(actionParams.UnitData,originPlayer,targetGrid.Pos.V2(),actionParams.MapData);
|
||
|
||
|
||
//Step #5 更新攻击前后两个格子的显示,比如学者攻击了在城市里的角色,那么城市里会有fire
|
||
//更正!这一步不能在这里做,要在fragment里做!!
|
||
//originGrid.Renderer(actionParams.MapData)?.SetUpdateGrid();
|
||
//targetGrid.Renderer(actionParams.MapData)?.SetUpdateGrid();
|
||
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckCan(CommonActionParams actionParams)
|
||
{
|
||
if (!actionParams.UnitData.IsAlive() || !actionParams.TargetUnitData.IsAlive()) return false;
|
||
if (actionParams.UnitData.Id == actionParams.TargetUnitData.Id) return false;
|
||
if (!actionParams.UnitData.CanAttackAll(actionParams.MapData) &&
|
||
actionParams.MapData.IsLeagueUnitByUnit(actionParams.UnitData.Id, actionParams.TargetUnitData.Id)) return false;
|
||
if (actionParams.UnitData.IsLimitSelfAttack(actionParams.MapData)) return false;
|
||
if (!actionParams.MapData.GetPlayerDataByUnitId(actionParams.UnitData.Id, out _)) return false;
|
||
if (!actionParams.MapData.GetPlayerDataByUnitId(actionParams.TargetUnitData.Id, out _)) return false;
|
||
if (!actionParams.MapData.GetCityDataByUnitId(actionParams.UnitData.Id, out _)) return false;
|
||
if (!actionParams.MapData.GetCityDataByUnitId(actionParams.TargetUnitData.Id, out _)) return false;
|
||
//单独处理: 学者无法攻击英雄
|
||
if (actionParams.UnitData.GetSkill(SkillType.CONVERT, out var _) && actionParams.TargetUnitData.UnitFullType.UnitType == UnitType.Giant) return false;
|
||
//技能层细粒度过滤(如 INFILTRATE 仅允许打城心上的敌人)
|
||
if (!actionParams.UnitData.IsCanAttackTargetUnit(actionParams.MapData, actionParams.TargetUnitData)) return false;
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckShow(CommonActionParams actionParams,out ShowType showType)
|
||
{
|
||
showType = ShowType.None;
|
||
return false;
|
||
}
|
||
public override ActionShowState CheckShowState(CommonActionParams actionParams) { return ActionShowState.None; }
|
||
public override bool CameraControl(CommonActionParams actionParams)
|
||
{
|
||
var main = GameObject.Find("Main").GetComponent<Main>();
|
||
if(actionParams.GridData != null && main != null && MapRenderer.Instance.CameraController != null
|
||
&& actionParams.MapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(actionParams.GridData.Id))
|
||
MapRenderer.Instance.CameraController?.CameraFocusOnGrid(actionParams.GridData);
|
||
return true;
|
||
}
|
||
}
|
||
|
||
|
||
public class UnitAttackGroundAction : ActionLogicBase
|
||
{
|
||
public UnitAttackGroundAction(CommonActionId id) : base(id)
|
||
{
|
||
}
|
||
|
||
protected override bool Execute(CommonActionParams actionParams)
|
||
{
|
||
//Step #0 为anim做数据准备
|
||
GridData originGrid = actionParams.UnitData.Grid(actionParams.MapData);
|
||
GridData targetGrid = actionParams.GridData;
|
||
UnitRenderer originUnitRenderer = null;
|
||
GridRenderer targetGridRenderer = null;
|
||
|
||
bool isTrueMap = actionParams.MapData == Main.MapData;
|
||
|
||
//如果不是AI预测相关的行为,获取两个格子的Renderer
|
||
if (isTrueMap)
|
||
{
|
||
MapRenderer.Instance.ROUnitMap.TryGetValue(actionParams.UnitData.Id, out originUnitRenderer);
|
||
targetGridRenderer = targetGrid.Renderer(Main.MapData);
|
||
}
|
||
|
||
|
||
//Step #1 处理所有攻击友军的逻辑
|
||
UnitData unit1 = actionParams.UnitData;
|
||
//UnitData unit2 = actionParams.TargetUnitData;
|
||
CityData city1 = unit1.City(actionParams.MapData);
|
||
//MapData mapData = actionParams.MapData;
|
||
|
||
SkillType animSkillData = SkillType.NONE;
|
||
UnitData suwakoUnit = null;
|
||
bool hasSuwakoAttack = unit1.GetSkill(SkillType.SUWAKOATTACK, out var _);
|
||
bool handledBySkillAttackGround = false;
|
||
|
||
if (unit1.GetSkill(SkillType.INFILTRATE, out _)
|
||
&& targetGrid.Resource == ResourceType.CityCenter
|
||
&& targetGrid.RealUnit(actionParams.MapData, out var hiddenTargetUnit))
|
||
{
|
||
var actingPlayer = actionParams.PlayerData;
|
||
if (actingPlayer == null)
|
||
unit1.Player(actionParams.MapData, out actingPlayer);
|
||
if (hiddenTargetUnit.IsHideAndCantSee(actionParams.MapData, actingPlayer))
|
||
{
|
||
hiddenTargetUnit.OnBeInteractTarget(actionParams.MapData, unit1, targetGrid);
|
||
return true;
|
||
}
|
||
}
|
||
|
||
//处理SUWAKO的地面攻击
|
||
if (!hasSuwakoAttack &&
|
||
unit1.AttackGroundExecute(actionParams.MapData, targetGrid, out animSkillData))
|
||
{
|
||
handledBySkillAttackGround = true;
|
||
ActiveAttackActionRecorder.MarkStarted(actionParams.MapData, unit1);
|
||
}
|
||
|
||
if (!handledBySkillAttackGround && hasSuwakoAttack)
|
||
{
|
||
animSkillData = SkillType.SUWAKOATTACK;
|
||
if (targetGrid.RealUnit(actionParams.MapData, out var targetGridUnit))
|
||
{
|
||
ActiveAttackActionRecorder.MarkStarted(actionParams.MapData, unit1);
|
||
targetGridUnit.OnBeInteractTarget(actionParams.MapData, unit1, targetGrid);
|
||
return true;
|
||
}
|
||
|
||
// Suwako 对空地生成白蛇不是攻击,不能重置和平奇观的无攻击回合计数。
|
||
unit1.ClearActionPoint();
|
||
var summonedHebiLevel = unit1.UnitLevel >= 4 ? 2u : 1u;
|
||
var fullType = new UnitFullType()
|
||
{ UnitType = UnitType.MoriyaHebi, GiantType = GiantType.None, UnitLevel = summonedHebiLevel };
|
||
|
||
// TODO 这里的延迟产生单位形象,后续要加入presentationlist
|
||
if (!actionParams.MapData.AddUnitData(targetGrid.Id, city1.Id, fullType, out suwakoUnit, 0.2f))
|
||
return false;
|
||
}
|
||
else if (!handledBySkillAttackGround)
|
||
{
|
||
ActiveAttackActionRecorder.MarkStarted(actionParams.MapData, unit1);
|
||
}
|
||
|
||
//处理 INFILTRATE 的地面攻击:渗透单位攻击敌方空城心 → 直接偷金 + 自杀 + 生叛军,跳过远程攻击(Bomb)动画
|
||
bool infiltrateConsumed = false;
|
||
if (!handledBySkillAttackGround && unit1.GetSkill(SkillType.INFILTRATE, out var infSkillBase) && infSkillBase is InfiltrateSkill infSkill)
|
||
{
|
||
infiltrateConsumed = infSkill.PerformInfiltrateOnAttackGround(actionParams.MapData, unit1, targetGrid);
|
||
if (infiltrateConsumed)
|
||
ActiveAttackActionRecorder.MarkStarted(actionParams.MapData, actionParams.PlayerData);
|
||
}
|
||
|
||
//Step #3 处理动画
|
||
//如果是AI预测行为,return
|
||
if (!isTrueMap) return true;
|
||
//如果数据出错,return
|
||
if (originGrid == null || targetGrid == null || originUnitRenderer == null || targetGridRenderer == null) return false;
|
||
|
||
//如果在视野,更新相关视觉。INFILTRATE 渗透单位已自杀消失,不应播放远程攻击(Bomb)动画
|
||
if (!infiltrateConsumed &&
|
||
(Main.MapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(originGrid.Id) ||
|
||
Main.MapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(targetGrid.Id)))
|
||
{
|
||
var data = new FragmentAttackGroundData(FragmentType.AttackGround, originUnitRenderer,
|
||
targetGridRenderer, originGrid, targetGrid, animSkillData,city1);
|
||
|
||
var fragment = FragmentFactory.Create(FragmentType.AttackGround,data);
|
||
PresentationManager.EnqueueTask(new FragmentSequencerTask(fragment));
|
||
_duration = fragment.Duration;
|
||
suwakoUnit?.Grid(actionParams.MapData)?.Renderer(actionParams.MapData)?.InstantUpdateGrid();
|
||
}
|
||
|
||
//处理SUWAKO的地面攻击 刷新白蛇周围的视野
|
||
if (unit1.GetSkill(SkillType.SUWAKOATTACK, out var _) && suwakoUnit != null)
|
||
{
|
||
actionParams.PlayerData.Sight.UpdateSightByPath(suwakoUnit,actionParams.PlayerData,suwakoUnit.Grid(actionParams.MapData)?.Pos.V2() ?? Vector2Int.zero,actionParams.MapData);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckCan(CommonActionParams actionParams)
|
||
{
|
||
//Step #1 确定数据正确性
|
||
if (actionParams.MapData == null || actionParams.PlayerData == null || actionParams.UnitData == null || actionParams.GridData == null) return false;
|
||
|
||
//Step #2 player操作的unit 必须属于player
|
||
if (actionParams.UnitData.Player(actionParams.MapData) != actionParams.PlayerData) return false;
|
||
|
||
//Step #3 unit是否能够攻击targetGrid
|
||
if (!actionParams.UnitData.IsCanAttackTargetGrid(actionParams.MapData,actionParams.GridData)) return false;
|
||
|
||
//Step #4 判断是否有AP
|
||
var hasKasenBeastGuideReady =
|
||
HakureiNorwayHeroSkillUtil.TryGetKasenBeastGuideReady(actionParams.UnitData, out var kasenGuideSkill) &&
|
||
kasenGuideSkill.CanSetBeastGuide(actionParams.MapData, actionParams.UnitData, actionParams.GridData);
|
||
if (actionParams.UnitData.GetActionPoint(ActionPointType.Attack) <= 0 &&
|
||
!hasKasenBeastGuideReady)
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckShow(CommonActionParams actionParams,out ShowType showType)
|
||
{
|
||
showType = ShowType.None;
|
||
return false;
|
||
}
|
||
public override ActionShowState CheckShowState(CommonActionParams actionParams) { return ActionShowState.None; }
|
||
public override bool CameraControl(CommonActionParams actionParams)
|
||
{
|
||
var main = GameObject.Find("Main").GetComponent<Main>();
|
||
if(actionParams.GridData != null && main != null && MapRenderer.Instance.CameraController != null
|
||
&& actionParams.MapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(actionParams.GridData.Id))
|
||
MapRenderer.Instance.CameraController?.CameraFocusOnGrid(actionParams.GridData);
|
||
return true;
|
||
}
|
||
}
|
||
|
||
|
||
public class StartWonderAction : ActionLogicBase
|
||
{
|
||
public StartWonderAction(CommonActionId id) : base(id)
|
||
{
|
||
|
||
}
|
||
protected override bool Execute(CommonActionParams actionParams)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
public override bool CheckCan(CommonActionParams actionParams)
|
||
{
|
||
return false;
|
||
}
|
||
public override bool CheckShow(CommonActionParams actionParams,out ShowType showType)
|
||
{
|
||
showType = ShowType.None;
|
||
return false;
|
||
}
|
||
}
|
||
|
||
//TODO 没有填写Skill
|
||
public class UnitSkillAction : ActionLogicBase
|
||
{
|
||
public UnitSkillAction(CommonActionId id) : base(id)
|
||
{
|
||
|
||
}
|
||
protected override bool Execute(CommonActionParams actionParams)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
public override bool CheckCan(CommonActionParams actionParams)
|
||
{
|
||
return false;
|
||
}
|
||
public override bool CheckShow(CommonActionParams actionParams,out ShowType showType)
|
||
{
|
||
showType = ShowType.None;
|
||
return false;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
public class LearnTechAction : ActionLogicBase
|
||
{
|
||
public LearnTechAction(CommonActionId id) : base(id)
|
||
{
|
||
|
||
}
|
||
protected override bool Execute(CommonActionParams actionParams)
|
||
{
|
||
if (actionParams.MainObjectType != MainObjectType.Player)
|
||
return false;
|
||
if (actionParams.PlayerData == null)
|
||
return false;
|
||
int cost = Main.PlayerLogic.GetTechCost(actionParams.MapData, actionParams.PlayerData, _actionId.TechType);
|
||
Main.PlayerLogic.ResearchTech(actionParams.MapData,actionParams.PlayerData,_actionId.TechType,cost);
|
||
|
||
//发出学习科技的通知
|
||
if(actionParams.PlayerData.IsSelfPlayer())
|
||
EventManager.Publish(new ShowUINotifyCommon(){UINotifyCommonType = UINotifyCommonType.Tech});
|
||
|
||
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckCan(CommonActionParams actionParams)
|
||
{
|
||
//鲁棒性判断,不是玩家执行这个操作就退出
|
||
if (actionParams.MainObjectType != MainObjectType.Player)
|
||
return false;
|
||
if (actionParams.PlayerData == null)
|
||
return false;
|
||
if (actionParams.PlayerData.TechTree.CheckIfHasTech(_actionId.TechType))
|
||
return false;
|
||
//如果科技不在该玩家的tech pool里,return
|
||
if (!Table.Instance.PlayerDataAssets.GetPlayerInfo(actionParams.PlayerData, out var playerInfo))
|
||
return false;
|
||
if (!playerInfo.TechPool.Contains(_actionId.TechType))
|
||
return false;
|
||
//如果没有父科技
|
||
var techInfo = Table.Instance.TechDataAssets.GetTechInfo(_actionId.TechType);
|
||
if (!actionParams.PlayerData.TechTree.CheckIfHasTech(techInfo.FatherTechList))
|
||
return false;
|
||
//判断钱够不够
|
||
|
||
int cost = Main.PlayerLogic.GetTechCost(actionParams.MapData, actionParams.PlayerData, _actionId.TechType);
|
||
if(cost > actionParams.PlayerData.PlayerCoin + actionParams.PlayerData.PlayerTechPoint)
|
||
return false;
|
||
|
||
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckShow(CommonActionParams actionParams,out ShowType showType)
|
||
{
|
||
showType = ShowType.None;
|
||
return false;
|
||
}
|
||
}
|
||
|
||
|
||
public class PlayerTurnStartAction : ActionLogicBase
|
||
{
|
||
public PlayerTurnStartAction(CommonActionId id) : base(id)
|
||
{
|
||
}
|
||
|
||
protected override bool Execute(CommonActionParams actionParams)
|
||
{
|
||
if (!CheckCan(actionParams)) return false;
|
||
// collect 调用
|
||
CollectManager.Instance.OnTurnStartCollect(actionParams.MapData, actionParams.PlayerData);
|
||
|
||
actionParams.MapData.OnTurnStart(actionParams.PlayerData.Id);
|
||
//这里需要一个AfterTurnStart周期。 否则 onturnstart自动给周围人上buff的技能,会出问题
|
||
actionParams.MapData.OnAfterTurnStart(actionParams.PlayerData.Id);
|
||
|
||
// 回合开始时刷新一次外交关系:LeagueRupture→Neutral、War→Neutral 等跨回合状态过渡依赖此处触发。
|
||
// AfterExecute 的同名刷新显式过滤了 TurnStart/TurnEnd,所以这里必须显式补上,否则新回合 UI 不会立刻刷新。
|
||
if (actionParams.MapData == Main.MapData)
|
||
{
|
||
foreach (var pData in actionParams.MapData.PlayerMap.PlayerDataList)
|
||
{
|
||
pData.RefreshFeelingValue(actionParams.MapData);
|
||
pData.RefreshDiplomacyState(actionParams.MapData);
|
||
}
|
||
}
|
||
|
||
// 回合开始后:同步所有 UnitMono 的 SkillStatusInfo,让 buff/debuff 图标与最新数据保持一致
|
||
if (actionParams.MapData == Main.MapData && MapRenderer.Instance?.ROUnitMap != null)
|
||
{
|
||
foreach (var unitRenderer in MapRenderer.Instance.ROUnitMap.Values)
|
||
{
|
||
unitRenderer?.SyncStatusWithUnitSkills();
|
||
}
|
||
}
|
||
|
||
if(actionParams.PlayerData.IsSelfPlayer())
|
||
{
|
||
PresentationManager.EnqueuePendingDanegeldChoices(actionParams.MapData, actionParams.PlayerData);
|
||
PresentationManager.EnqueuePendingCityLevelUpChoices(actionParams.MapData, actionParams.PlayerData);
|
||
EventManager.Publish(new ShowUIBottomBottomBarNextTurn(){});
|
||
}
|
||
PresentationManager.EnqueuePendingTreasureChoice(actionParams.MapData, actionParams.PlayerData);
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckCan(CommonActionParams actionParams)
|
||
{
|
||
if (actionParams.PlayerData == null)
|
||
return false;
|
||
if (actionParams.MapData.CurPlayer != null && actionParams.MapData.CurPlayer.Id == actionParams.PlayerData.Id)
|
||
return false;
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckShow(CommonActionParams actionParams,out ShowType showType)
|
||
{
|
||
showType = ShowType.None;
|
||
return false;
|
||
}
|
||
}
|
||
|
||
|
||
public class PlayerTurnEndAction : ActionLogicBase
|
||
{
|
||
public PlayerTurnEndAction(CommonActionId id) : base(id)
|
||
{
|
||
}
|
||
|
||
protected override bool Execute(CommonActionParams actionParams)
|
||
{
|
||
if (!CheckCan(actionParams)) return false;
|
||
PlayerActionDiplomacy.RejectPendingDanegeldDemandsInTurnEnd(actionParams.MapData, actionParams.PlayerData);
|
||
actionParams.MapData.OnTurnEnd(actionParams.PlayerData.Id);
|
||
PlayerActionDiplomacy.ClearExpiredDanegeldRefusals(actionParams.MapData, actionParams.PlayerData);
|
||
if (actionParams.MapData == Main.MapData)
|
||
PresentationManager.DismissTurnBoundUIOnTurnEnd();
|
||
if(actionParams.PlayerData.IsSelfPlayer())
|
||
EventManager.Publish(new HideUIBottomBottomBarNextTurn(){});
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckCan(CommonActionParams actionParams)
|
||
{
|
||
if (actionParams.PlayerData == null)
|
||
return false;
|
||
if (actionParams.MapData.CurPlayer == null)
|
||
return false;
|
||
if (actionParams.MapData.CurPlayer.Id != actionParams.PlayerData.Id)
|
||
return false;
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckShow(CommonActionParams actionParams,out ShowType showType)
|
||
{
|
||
showType = ShowType.None;
|
||
return false;
|
||
}
|
||
}
|
||
|
||
|
||
public class AIParamControlAction : ActionLogicBase
|
||
{
|
||
public AIParamControlAction(CommonActionId id) : base(id)
|
||
{
|
||
}
|
||
|
||
protected override bool Execute(CommonActionParams actionParams)
|
||
{
|
||
if (_actionId.AIParamType is AIParamControlType.APClear or AIParamControlType.AllClear)
|
||
{
|
||
actionParams.UnitData.ReduceActionPoint(ActionPointType.Attack);
|
||
}
|
||
if (_actionId.AIParamType is AIParamControlType.CPClear or AIParamControlType.AllClear)
|
||
{
|
||
actionParams.UnitData.ReduceActionPoint(ActionPointType.Capture);
|
||
}
|
||
if (_actionId.AIParamType is AIParamControlType.MPClear or AIParamControlType.AllClear)
|
||
{
|
||
actionParams.UnitData.ReduceActionPoint(ActionPointType.Move);
|
||
}
|
||
|
||
if (_actionId.AIParamType == AIParamControlType.AIMoney)
|
||
{
|
||
if (!CheckAIMoney(actionParams)) return false;
|
||
// AI 难度加钱
|
||
int tt = 0;
|
||
if (actionParams.MapData.MapConfig.AIDiff == AIDifficult.EASY)
|
||
{
|
||
actionParams.PlayerData.SpendCoin(actionParams.PlayerData.PlayerCoin / 2);
|
||
}
|
||
else if (actionParams.MapData.MapConfig.AIDiff == AIDifficult.NORMAL)
|
||
{
|
||
actionParams.PlayerData.SpendCoin(actionParams.PlayerData.PlayerCoin / 5);
|
||
}
|
||
else
|
||
{
|
||
if (actionParams.PlayerData.Turn < 10) tt = (int)actionParams.MapData.MapConfig.AIDiff;
|
||
actionParams.PlayerData.AddCoin((int)actionParams.PlayerData.Turn / 5 * (int)Main.MapData.MapConfig.AIDiff + tt);
|
||
actionParams.PlayerData.AddCulturePoint(tt);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckCan(CommonActionParams actionParams)
|
||
{
|
||
if (_actionId.AIParamType == AIParamControlType.AIMoney)return CheckAIMoney(actionParams);
|
||
if (actionParams.UnitData == null) return false;
|
||
if (_actionId.AIParamType == AIParamControlType.MPClear) return actionParams.UnitData.GetActionPoint(ActionPointType.Move) > 0;
|
||
if (_actionId.AIParamType == AIParamControlType.CPClear) return actionParams.UnitData.GetActionPoint(ActionPointType.Capture) > 0;
|
||
if (_actionId.AIParamType == AIParamControlType.APClear) return actionParams.UnitData.GetActionPoint(ActionPointType.Attack) > 0;
|
||
if (_actionId.AIParamType == AIParamControlType.AllClear)
|
||
return actionParams.UnitData.GetActionPoint(ActionPointType.Move) > 0 || actionParams.UnitData.GetActionPoint(ActionPointType.Capture) > 0 || actionParams.UnitData.GetActionPoint(ActionPointType.Attack) > 0;
|
||
return false;
|
||
}
|
||
|
||
public override bool CheckShow(CommonActionParams actionParams,out ShowType showType)
|
||
{
|
||
showType = ShowType.None;
|
||
return false;
|
||
}
|
||
|
||
private bool CheckAIMoney(CommonActionParams actionParams)
|
||
{
|
||
if (actionParams.PlayerData == null)
|
||
return false;
|
||
if (actionParams.MapData.Net.Mode == NetMode.Multi)
|
||
{
|
||
if (actionParams.MapData.CheckIsRealPlayer(actionParams.PlayerData.Id))return false;
|
||
}
|
||
else
|
||
{
|
||
if (actionParams.MapData.PlayerMap.SelfPlayerData.Id == actionParams.PlayerData.Id) return false;
|
||
}
|
||
return true;
|
||
}
|
||
}
|
||
|
||
|
||
public class SurrenderAction : ActionLogicBase
|
||
{
|
||
public SurrenderAction(CommonActionId id) : base(id)
|
||
{
|
||
}
|
||
|
||
protected override bool Execute(CommonActionParams actionParams)
|
||
{
|
||
actionParams.PlayerData.Surrender(actionParams.MapData);
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckCan(CommonActionParams actionParams)
|
||
{
|
||
if (actionParams.PlayerData == null) return false;
|
||
if (!actionParams.PlayerData.IsSurvival) return false;
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckShow(CommonActionParams actionParams,out ShowType showType)
|
||
{
|
||
showType = ShowType.None;
|
||
return false;
|
||
}
|
||
}
|
||
|
||
|
||
public class BuyCultureCardAction : ActionLogicBase
|
||
{
|
||
public BuyCultureCardAction(CommonActionId id) : base(id)
|
||
{
|
||
}
|
||
|
||
protected override bool Execute(CommonActionParams actionParams)
|
||
{
|
||
var map = actionParams.MapData;
|
||
var player = actionParams.PlayerData;
|
||
player.PlayerCultureInfo.TryBuyCultureCard(map, player, _actionId.CultureCardType);
|
||
return true;
|
||
}
|
||
|
||
public override bool CheckCan(CommonActionParams actionParams)
|
||
{
|
||
if (actionParams.MainObjectType != MainObjectType.Player) return false;
|
||
var map = actionParams.MapData;
|
||
var player = actionParams.PlayerData;
|
||
return player.PlayerCultureInfo.CheckCanBuyCultureCard(map, player, _actionId.CultureCardType);
|
||
}
|
||
|
||
public override bool CheckShow(CommonActionParams actionParams,out ShowType showType)
|
||
{
|
||
showType = ShowType.None;
|
||
if (actionParams.MainObjectType != MainObjectType.Player) return false;
|
||
var map = actionParams.MapData;
|
||
var player = actionParams.PlayerData;
|
||
return player.PlayerCultureInfo.CheckCanBuyCultureCard(map, player, _actionId.CultureCardType);
|
||
}
|
||
}
|
||
}
|