TH1/Unity/Assets/Scripts/TH1_Data/PlayerData.cs

1928 lines
73 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* @Author: 白哉
* @Description:
* @Date: 2025年04月03日 星期四 11:04:31
* @Modify:
*/
using System;
using System.Collections.Generic;
using Logic.Action;
using Logic.AI;
using Logic.Audio;
using Logic.Pool;
using MemoryPack;
using NUnit.Framework;
using TH1_Core.Events;
using TH1_Core.Managers;
using TH1_Logic.Core;
using TH1_Logic.HeroTask;
using TH1_Logic.MatchConfig;
using TH1Renderer;
using UnityEngine;
namespace RuntimeData
{
// 奇观类型枚举
public enum WonderTypeEnum
{
None,
PEACE,
KNOWLEDGE,
TRADE,
WEALTH,
POWER,
PARK,
EYE
}
// 奇观状态枚举
public enum WonderState
{
NO_HINT,
HAVE_HINT,
FINISH_NOT_BUILD,
FINISH_BUILD,
Max
}
// 外交关系枚举
public enum DiplomacyState
{
NoDiplomacy, // 未建交
Neutral, // 中立的
League, // 联盟
War, // 战争
LeagueRupture, // 联盟破裂
}
// 好感状态枚举
public enum FeelingState
{
Trust,
Appreciate,
Indifferent,
Suspicion,
Terrible,
}
// 好感策略枚举
public enum FeelingStrategy
{
Wise,
Charming,
Peaceful,
Diplomatic,
Powerful,
Brave,
Violent,
Threatening,
Weak,
Annoying,
Foolish,
Intrusive,
Dominative,
}
[MemoryPackable]
public partial class PlayerMapData
{
public List<PlayerData> PlayerDataList;
[MemoryPackIgnore]
public uint SelfPlayerId;
[MemoryPackIgnore]
public PlayerData SelfPlayerData => GetPlayerData(SelfPlayerId);
private Dictionary<uint, PlayerData> _playerDataDict;
// 无数据初始化
[MemoryPackConstructor]
public PlayerMapData()
{
PlayerDataList = new List<PlayerData>();
_playerDataDict = new Dictionary<uint, PlayerData>();
}
// 配置初始化
public PlayerMapData(MapData map, MapIdGenerator idGenerator, NetMode netMode)
{
PlayerDataList = new List<PlayerData>();
_playerDataDict = new Dictionary<uint, PlayerData>();
if (netMode == NetMode.Single)
{
map.MapConfig.EnsurePlayerInfosCount();
for (int i = 0; i < map.MapConfig.PlayerCount; i++)
{
uint civId = (uint)i;
uint forceId = (uint)i;
if (map.MapConfig.GetPlayerInfoByIndex((uint)i, out var playerInfo))
{
civId = playerInfo.CivId;
forceId = playerInfo.ForceId;
}
PlayerData player = new PlayerData(civId, forceId, idGenerator);
PlayerDataList.Add(player);
_playerDataDict[player.Id] = player;
}
}
if (netMode == NetMode.Multi)
{
map.MapConfig.EnsurePlayerInfosCount();
for (int i = 0; i < map.MapConfig.PlayerCount; i++)
{
uint civId = (uint)i;
uint forceId = (uint)i;
if (map.MapConfig.GetPlayerInfoByIndex((uint)i, out var playerInfo))
{
civId = playerInfo.CivId;
forceId = playerInfo.ForceId;
}
var player = new PlayerData(civId, forceId, idGenerator);
PlayerDataList.Add(player);
_playerDataDict[player.Id] = player;
}
foreach (var multiCiv in map.MapConfig.MultiCivs)
{
if (multiCiv.PlayerInfoIndex >= PlayerDataList.Count)
{
multiCiv.PlayerId = 0;
continue;
}
multiCiv.PlayerId = PlayerDataList[(int)multiCiv.PlayerInfoIndex].Id;
}
}
if (netMode == NetMode.Single && map.MapConfig.SelfPlayerInfoIndex < PlayerDataList.Count)
SelfPlayerId = PlayerDataList[(int)map.MapConfig.SelfPlayerInfoIndex].Id;
else
SelfPlayerId = PlayerDataList[0].Id;
foreach (var self in PlayerDataList)
{
foreach (var target in PlayerDataList)
{
if (self.Id == target.Id) continue;
self.DiplomacyData.AddCountryDiplomacyInfo(target.Id);
}
}
}
public PlayerMapData(PlayerMapData copyData)
{
PlayerDataList = new List<PlayerData>();
_playerDataDict = new Dictionary<uint, PlayerData>();
SelfPlayerId = copyData.SelfPlayerId;
foreach (var copyPlayer in copyData.PlayerDataList)
{
var player = new PlayerData(copyPlayer);
PlayerDataList.Add(player);
_playerDataDict[player.Id] = player;
}
}
public void DeepCopy(PlayerMapData copyData)
{
_playerDataDict.Clear();
SelfPlayerId = copyData.SelfPlayerId;
for (int i = 0; i < copyData.PlayerDataList.Count; i++)
{
var copyPlayer = copyData.PlayerDataList[i];
if (i >= PlayerDataList.Count)
{
var player = new PlayerData(copyPlayer);
PlayerDataList.Add(player);
_playerDataDict[player.Id] = player;
}
else
{
PlayerDataList[i].DeepCopy(copyPlayer);
_playerDataDict[PlayerDataList[i].Id] = PlayerDataList[i];
}
}
for (int i = PlayerDataList.Count - 1; i >= copyData.PlayerDataList.Count; i--)
{
_playerDataDict.Remove(PlayerDataList[i].Id);
PlayerDataList.RemoveAt(i);
}
}
// 裁剪玩家列表到指定数量(移除末尾多余的玩家)
public void TrimPlayerCount(int count)
{
for (int i = PlayerDataList.Count - 1; i >= count; i--)
{
_playerDataDict.Remove(PlayerDataList[i].Id);
PlayerDataList.RemoveAt(i);
}
}
// MemoryPack 反序列化之后的后处理
[MemoryPackOnDeserialized]
public void OnAfterMemoryPackDeserialize()
{
_playerDataDict ??= new Dictionary<uint, PlayerData>();
_playerDataDict.Clear();
foreach (var player in PlayerDataList) _playerDataDict[player.Id] = player;
}
// 根据 pid 获取玩家数据
public bool GetPlayerDataByPlayerID(uint pid, out PlayerData playerData)
{
return _playerDataDict.TryGetValue(pid, out playerData);
}
public PlayerData GetPlayerData(uint id)
{
if (_playerDataDict.TryGetValue(id, out var playerData))
{
return playerData;
}
return null;
}
public void OnTurnStart(MapData map, PlayerData player)
{
Main.PlayerLogic.StartNextTurn(map, player);
player.OnTurnStart(map);
}
public void OnTurnEnd(MapData map, PlayerData player)
{
Main.PlayerLogic.EndThisTurn(map, player);
player.OnTurnEnd(map);
}
public int GetMaxPlayerScore()
{
int maxScore = -1;
foreach (var player in PlayerDataList)
{
if (!player.IsSurvival) continue;
if (player.PlayerScore <= maxScore) continue;
maxScore = player.PlayerScore;
}
return maxScore;
}
}
[MemoryPackable]
public partial class PlayerData : IdentifierBase
{
// 文明 id, 阵营 id, 摇篮城市 id
public uint Turn;
public CivEnum CivEnum => Table.Instance.TransCivIdToCivEnum(PlayerCivId);
public ForceEnum ForceEnum => Table.Instance.TransForceIdToForceEnum(PlayerForceId);
public Empire Empire => new Empire(CivEnum, ForceEnum);
public uint PlayerCivId;
public uint PlayerForceId;
public uint CradleCityId;
// 是否存活
public bool Alive;
// 财富总额
public int PlayerCoin => _playerCoin;
public int _playerCoin;
public int PlayerTechPoint => _playerTechPoint;
public int _playerTechPoint;
//造山点(主要给守矢使用)
public int PlayerMountainPointUsed;
//分数
public int PlayerScore;
// 视野
public MapSightData Sight;
// 科技
public TechTreeData TechTree;
// 奇观
public WonderData Wonder;
// 外交
public DiplomacyData DiplomacyData;
//英雄
public PlayerHeroData PlayerHeroData;
//命名器
public PlayerNamerData PlayerNamerData;
// 用于记录已经连续几个 turn 没有进行攻击
public int TurnNoAttack;
// 用于记录累计击杀多少 unit
public int TotalKill;
// 英雄
private bool _heroDictInit;
//记录伟人的累计经验,下标直接从ChessType的Enum对应的uint转过来
public int[] giantExp = new int[6];
//记录伟人的复活冷却cd惩罚
public int[] giantPenalty = new int[6];
public List<uint> MeetPlayers;
public List<uint> CurAttackPlayers;
public List<uint> LastAttackPlayers;
//用于记录是否死亡是否要centMessage来宣告死亡信息
public bool DieMark = false;
//用于记录当前是否正在playing主要是视觉那边使用
public bool IsPlaying;
// Moment
public MomentData MomentData;
//PlayerCulture
public PlayerCultureInfo PlayerCultureInfo;
// 是否投降
public bool IsSurrender;
// 玩家是否存活要用这条来判断
public bool IsSurvival => !IsSurrender && Alive;
// 复用的临时集合避免每回合外交计算GC
[MemoryPackIgnore] private HashSet<uint> _tmpOriginCityBuf;
[MemoryPackIgnore] private List<CityData> _tmpCityListBuf;
[MemoryPackIgnore] private HashSet<CityData> _tmpCitySetBuf;
[MemoryPackIgnore] private HashSet<uint> _tmpTerritoryGridIdBuf;
[MemoryPackIgnore] private HashSet<uint> _tmpSelfTerritoryInfluenceGridIdBuf;
[MemoryPackIgnore] private Dictionary<uint, float> _tmpPlayerMilitaryScoreBuf;
[MemoryPackIgnore] private Dictionary<uint, List<uint>> _tmpPlayerUnitGridIdListBuf;
[MemoryPackIgnore] private Dictionary<uint, HashSet<uint>> _tmpPlayerTerritoryGridIdSetBuf;
[MemoryPackIgnore] private Dictionary<uint, DiplomacyState> _tmpSelfToOtherDiplomacyStateBuf;
// 无参数初始化
[MemoryPackConstructor]
public PlayerData()
{
Sight = new MapSightData();
TechTree = new TechTreeData();
Wonder = new WonderData();
DiplomacyData = new DiplomacyData();
MeetPlayers = new List<uint>();
CurAttackPlayers = new List<uint>();
LastAttackPlayers = new List<uint>();
PlayerNamerData = new PlayerNamerData();
MomentData = new MomentData();
PlayerCultureInfo = new PlayerCultureInfo();
}
// 带 ID 初始化
public PlayerData(uint civId, uint forceId, MapIdGenerator idGenerator)
{
Turn = 0;
Id = idGenerator.GeneratorId();
if(Table.Instance.PlayerDataAssets.GetPlayerInfoByCivId(civId,forceId,out var info))
TechTree = new TechTreeData(this, info.TechStart);
else
TechTree = new TechTreeData();
Sight = new MapSightData();
Wonder = new WonderData();
DiplomacyData = new DiplomacyData();
PlayerHeroData = new PlayerHeroData();
CurAttackPlayers = new List<uint>();
LastAttackPlayers = new List<uint>();
PlayerNamerData = new PlayerNamerData();
MomentData = new MomentData();
MomentData.InitItems();
PlayerCultureInfo = new PlayerCultureInfo();
MeetPlayers = new List<uint>();
MeetPlayers.Add(Id);
InitData(civId,forceId);
}
public PlayerData(PlayerData copyData)
{
Turn = copyData.Turn;
Id = copyData.Id;
PlayerCivId = copyData.PlayerCivId;
PlayerForceId = copyData.PlayerForceId;
CradleCityId = copyData.CradleCityId;
Alive = copyData.Alive;
IsSurrender = copyData.IsSurrender;
_playerCoin = copyData._playerCoin;
_playerTechPoint = copyData._playerTechPoint;
PlayerMountainPointUsed = copyData.PlayerMountainPointUsed;
Sight = new MapSightData(copyData.Sight);
TechTree = new TechTreeData(copyData.TechTree);
Wonder = new WonderData(copyData.Wonder);
DiplomacyData = new DiplomacyData(copyData.DiplomacyData);
PlayerHeroData = new PlayerHeroData(copyData.PlayerHeroData);
PlayerNamerData = new PlayerNamerData(copyData.PlayerNamerData);
MomentData = new MomentData(copyData.MomentData);
PlayerCultureInfo = new PlayerCultureInfo(copyData.PlayerCultureInfo);
CurAttackPlayers = new List<uint>();
foreach (var id in copyData.CurAttackPlayers) CurAttackPlayers.Add(id);
LastAttackPlayers = new List<uint>();
foreach (var id in copyData.LastAttackPlayers) LastAttackPlayers.Add(id);
MeetPlayers = new List<uint>();
foreach (var id in copyData.MeetPlayers) MeetPlayers.Add(id);
TurnNoAttack = copyData.TurnNoAttack;
TotalKill = copyData.TotalKill;
foreach (var skill in copyData.Skills) Skills.Add(skill.GetCopySkill());
//拷贝giantExp和giantPenalty
for (int i = 0; i < 6; i++)
{
giantExp[i] = copyData.giantExp[i];
giantPenalty[i] = copyData.giantPenalty[i];
}
}
public void DeepCopy(PlayerData copyData)
{
Turn = copyData.Turn;
Id = copyData.Id;
PlayerCivId = copyData.PlayerCivId;
PlayerForceId = copyData.PlayerForceId;
CradleCityId = copyData.CradleCityId;
Alive = copyData.Alive;
IsSurrender = copyData.IsSurrender;
_playerCoin = copyData._playerCoin;
_playerTechPoint = copyData._playerTechPoint;
PlayerMountainPointUsed = copyData.PlayerMountainPointUsed;
Sight.DeepCopy(copyData.Sight);
TechTree.DeepCopy(copyData.TechTree);
Wonder.DeepCopy(copyData.Wonder);
DiplomacyData.DeepCopy(copyData.DiplomacyData);
PlayerHeroData.DeepCopy(copyData.PlayerHeroData);
PlayerNamerData.DeepCopy(copyData.PlayerNamerData);
MomentData.DeepCopy(copyData.MomentData);
PlayerCultureInfo.DeepCopy(copyData.PlayerCultureInfo);
CurAttackPlayers.Clear();
foreach (var id in copyData.CurAttackPlayers) CurAttackPlayers.Add(id);
LastAttackPlayers.Clear();
foreach (var id in copyData.LastAttackPlayers) LastAttackPlayers.Add(id);
MeetPlayers.Clear();
foreach (var id in copyData.MeetPlayers) MeetPlayers.Add(id);
TurnNoAttack = copyData.TurnNoAttack;
TotalKill = copyData.TotalKill;
Skills.Clear();
foreach (var skill in copyData.Skills) Skills.Add(skill.GetCopySkill());
for (int i = 0; i < 6; i++)
{
giantExp[i] = copyData.giantExp[i];
giantPenalty[i] = copyData.giantPenalty[i];
}
}
private void InitData(uint civId,uint forceId)
{
Alive = true;
IsSurrender = false;
_playerCoin = 5;
if (civId == 2) _playerCoin = 7;
PlayerCivId = civId;
PlayerForceId = forceId;
_playerTechPoint = 0;
PlayerMountainPointUsed = 0;
TurnNoAttack = 0;
TotalKill = 0;
PlayerHeroData.Init(Table.Instance.TransCivIdToCivEnum(civId) ,Table.Instance.TransForceIdToForceEnum(forceId));
PlayerNamerData.Init(Table.Instance.TransCivIdToCivEnum(civId));
}
// 全局通知调用
public void OnTurnStart(MapData map)
{
Turn++;
//减少1点伟人复活冷却的时间点
for (int i = 0; i < 6; i++)
if (giantPenalty[i] > 0) giantPenalty[i]--;
LastAttackPlayers.Clear();
foreach (var id in CurAttackPlayers) LastAttackPlayers.Add(id);
CurAttackPlayers.Clear();
OnSkillsTurnStart(map);
// 这里是英雄的生命周期,先放在这里
PlayerHeroData.OnTurnStart(map, this);
foreach (var item in MomentData.Items) item.OnTurnStart();
IsPlaying = true;
//更新所有单位的视觉状态 TODO 之后依赖大回合的逻辑处理
foreach (var unit in map.UnitMap.UnitList)
unit.Renderer(map)?.InstantUpdateUnit(true);
}
public void OnTurnEnd(MapData map)
{
OnSkillsTurnEnd(map);
foreach (var player in map.PlayerMap.PlayerDataList)
{
if (player.Id == Id) continue;
this.GetCountryDiplomacyInfo(player.Id, out var selfToPlayer);
// 回合结束处理联盟破裂标记
if (selfToPlayer.DiplomacyState == DiplomacyState.LeagueRupture) selfToPlayer.IsLeagueRupture = true;
// 回合结束处理AI 冷静期计数
if (selfToPlayer.IsCalm > 0) selfToPlayer.IsCalm--;
}
IsPlaying = false;
}
public bool IsSelfPlayer()
{
return Main.MapData.PlayerMap.SelfPlayerData == this;
}
// 添加当前回合攻击者
public void AddAttacker(uint playerId)
{
this.AddTurnAttacker(playerId, Turn);
}
public CivEnum Civ()
{
return Table.Instance.TransCivIdToCivEnum(PlayerCivId);
}
public void AddTechPoint(int tech)
{
_playerTechPoint += tech;
//foreach (var kv in PlayerHeroData.HeroTaskDict) kv.Value.OnAddCoin((uint)coin);
EventManager.Publish(new UpdateUITopTopBar() { UpdateType = UpdateTopBarType.UpdateTech });
}
public void AddCulturePoint(int culture)
{
PlayerCultureInfo.PlayerCulture += culture;
EventManager.Publish(new UpdateUITopTopBar() { UpdateType = UpdateTopBarType.UpdateCulture });
}
//增加金币(目前处理了逻辑和动画 TODO 现在还是逻辑和动画没有分开,后续还是要分开
public void AddCoin(int coin, Vector3 startPos)
{
_playerCoin += coin;
foreach (var kv in PlayerHeroData.HeroTaskDict) kv.Value.OnAddCoin((uint)coin);
//如果加钱的是真人自己玩家,并且有金币来源位置,那么播动画
if (Main.MapData.PlayerMap.SelfPlayerData == this)
{
EventManager.Publish(new UpdateUITopTopBar() { UpdateType = UpdateTopBarType.UpdateCoin });
MapRenderer.Instance.ProjectileManager.CreateCoinProjectile(startPos,coin);
AudioManager.Instance.PlayAudio("SFX/PLAYER_coin");
//真人自己玩家,提前检查财富奇观,方便弹出提示
Main.PlayerLogic.UpdateWonder(Main.MapData,Main.MapData.PlayerMap.SelfPlayerData,WonderTypeEnum.WEALTH);
}
}
public void AddCoin_ViewOnly(int coin, Vector3 startPos)
{
if (Main.MapData.PlayerMap.SelfPlayerData == this)
{
EventManager.Publish(new UpdateUITopTopBar() { UpdateType = UpdateTopBarType.UpdateCoin });
MapRenderer.Instance.ProjectileManager.CreateCoinProjectile(startPos,coin);
AudioManager.Instance.PlayAudio("SFX/PLAYER_coin");
//真人自己玩家,提前检查财富奇观,方便弹出提示
Main.PlayerLogic.UpdateWonder(Main.MapData,Main.MapData.PlayerMap.SelfPlayerData,WonderTypeEnum.WEALTH);
}
}
public void AddCoin_LogicOnly(int coin)
{
_playerCoin += coin;
foreach (var kv in PlayerHeroData.HeroTaskDict) kv.Value.OnAddCoin((uint)coin);
}
public void AddCoin(int coin,GridData grid = null)
{
_playerCoin += coin;
foreach (var kv in PlayerHeroData.HeroTaskDict) kv.Value.OnAddCoin((uint)coin);
//如果加钱的是真人自己玩家并且有金币来源位置那么播动画。暂时用legacy方案
if (Main.MapData.PlayerMap.SelfPlayerData == this && grid != null)
{
EventManager.Publish(new UpdateUITopTopBar() { UpdateType = UpdateTopBarType.UpdateCoin });
var startPos = Table.Instance.GridToWorld(grid);
MapRenderer.Instance.ProjectileManager.CreateCoinProjectile(startPos,coin);
AudioManager.Instance.PlayAudio("SFX/PLAYER_coin");
//真人自己玩家,提前检查财富奇观,方便弹出提示
Main.PlayerLogic.UpdateWonder(Main.MapData,Main.MapData.PlayerMap.SelfPlayerData,WonderTypeEnum.WEALTH);
}
}
public void SpendCoin(int coin, GridData grid = null)
{
_playerCoin -= coin;
EventManager.Publish(new UpdateUITopTopBar() { UpdateType = UpdateTopBarType.UpdateCoin });
}
public void SpendTechPoint(int tech)
{
_playerTechPoint -= tech;
EventManager.Publish(new UpdateUITopTopBar() { UpdateType = UpdateTopBarType.UpdateTech });
}
//增加faith的动画目前处理了逻辑和动画 TODO 现在还是逻辑和动画没有分开,后续还是要分开
public void AddFaith(int faith,Vector3 startPos)
{
//如果加钱的是真人自己玩家,并且有金币来源位置
if (Main.MapData.PlayerMap.SelfPlayerData == this)
{
EventManager.Publish(new UpdateUITopTopBar() { UpdateType = UpdateTopBarType.UpdateFaith });
MapRenderer.Instance.ProjectileManager.CreateProjectileFaith(startPos,faith);
}
}
public void AddFaith(int faith,GridData grid = null)
{
//如果加钱的是真人自己玩家,并且有金币来源位置
if (Main.MapData.PlayerMap.SelfPlayerData == this && grid != null)
{
EventManager.Publish(new UpdateUITopTopBar() { UpdateType = UpdateTopBarType.UpdateFaith });
var startPos = Table.Instance.GridToWorld(grid);
MapRenderer.Instance.ProjectileManager.CreateProjectileFaith(startPos,faith);
}
}
// 检查前几回合内是否有攻击者
public bool CheckTurnsAttacker(uint turns, uint playerId)
{
if (turns >= Turn) turns = Turn;
return this.CheckTurnAttacker(playerId, Turn - turns);
}
// 刷新对他国好感值
public void RefreshFeelingValue(MapData map)
{
var playerDataList = map.PlayerMap.PlayerDataList;
int maxScore = -1;
uint maxScorePlayer = 0;
_tmpOriginCityBuf ??= new HashSet<uint>();
_tmpOriginCityBuf.Clear();
foreach (var player in playerDataList)
{
_tmpOriginCityBuf.Add(player.CradleCityId);
if (!player.IsSurvival) continue;
if (player.PlayerScore <= maxScore) continue;
maxScore = player.PlayerScore;
maxScorePlayer = player.Id;
}
var originCityHalfCount = _tmpOriginCityBuf.Count * 0.5f;
_tmpTerritoryGridIdBuf ??= new HashSet<uint>();
_tmpSelfTerritoryInfluenceGridIdBuf ??= new HashSet<uint>();
_tmpPlayerMilitaryScoreBuf ??= new Dictionary<uint, float>();
_tmpPlayerUnitGridIdListBuf ??= new Dictionary<uint, List<uint>>();
_tmpPlayerTerritoryGridIdSetBuf ??= new Dictionary<uint, HashSet<uint>>();
_tmpSelfToOtherDiplomacyStateBuf ??= new Dictionary<uint, DiplomacyState>();
_tmpPlayerMilitaryScoreBuf.Clear();
_tmpSelfToOtherDiplomacyStateBuf.Clear();
foreach (var kv in _tmpPlayerUnitGridIdListBuf)
{
kv.Value.Clear();
}
foreach (var kv in _tmpPlayerTerritoryGridIdSetBuf)
{
kv.Value.Clear();
}
foreach (var unit in map.UnitMap.UnitList)
{
if (!map.GetPlayerIdByUnitId(unit.Id, out var ownerId)) continue;
_tmpPlayerMilitaryScoreBuf[ownerId] = _tmpPlayerMilitaryScoreBuf.GetValueOrDefault(ownerId) + unit.GetMilitary();
if (!map.GetGridDataByUnitId(unit.Id, out var unitGrid)) continue;
if (!_tmpPlayerUnitGridIdListBuf.TryGetValue(ownerId, out var unitGridIdList))
{
unitGridIdList = new List<uint>();
_tmpPlayerUnitGridIdListBuf[ownerId] = unitGridIdList;
}
unitGridIdList.Add(unitGrid.Id);
}
var selfScore = _tmpPlayerMilitaryScoreBuf.GetValueOrDefault(Id);
_tmpTerritoryGridIdBuf.Clear();
map.GetPlayerTerritoryGridIdSet(Id, _tmpTerritoryGridIdBuf);
_tmpSelfTerritoryInfluenceGridIdBuf.Clear();
foreach (var selfGridId in _tmpTerritoryGridIdBuf)
{
if (!map.GridMap.GetGridDataByGid(selfGridId, out var selfGrid)) continue;
for (int x = selfGrid.Pos.X - 1; x <= selfGrid.Pos.X + 1; x++)
{
for (int y = selfGrid.Pos.Y - 1; y <= selfGrid.Pos.Y + 1; y++)
{
if (!map.GridMap.GetGridDataByPos(x, y, out var aroundGrid)) continue;
_tmpSelfTerritoryInfluenceGridIdBuf.Add(aroundGrid.Id);
}
}
}
foreach (var player in playerDataList)
{
if (!player.IsSurvival) continue;
if (!_tmpPlayerTerritoryGridIdSetBuf.TryGetValue(player.Id, out var territorySet))
{
territorySet = new HashSet<uint>();
_tmpPlayerTerritoryGridIdSetBuf[player.Id] = territorySet;
}
if (player.Id == Id)
{
foreach (var gid in _tmpTerritoryGridIdBuf) territorySet.Add(gid);
}
else
{
map.GetPlayerTerritoryGridIdSet(player.Id, territorySet);
}
}
foreach (var player in playerDataList)
{
if (player.Id == Id) continue;
this.GetCountryDiplomacyInfo(player.Id, out var selfToOtherPlayer);
if (selfToOtherPlayer == null) continue;
_tmpSelfToOtherDiplomacyStateBuf[player.Id] = selfToOtherPlayer.DiplomacyState;
}
var aiDiff = map.MapConfig.AIDiff;
var selfIsAi = map.CheckIsAI(Id);
foreach (var player in playerDataList)
{
if (!player.IsSurvival) continue;
if (player.Id == Id) continue;
this.GetCountryDiplomacyInfo(player.Id, out var selfToPlayer);
player.GetCountryDiplomacyInfo(Id, out var playerToSelf);
selfToPlayer.FeelingStrategyList.Clear();
if (playerToSelf == null || selfToPlayer == null) continue;
var score = 50f;
// 明智的 愚蠢的
var score1 = 0f;
foreach (var otherPlayer in playerDataList)
{
if (!player.IsSurvival) continue;
if (otherPlayer.Id == Id || otherPlayer.Id == player.Id) continue;
if (!_tmpSelfToOtherDiplomacyStateBuf.TryGetValue(otherPlayer.Id, out var selfToOtherPlayerState)) continue;
if (selfToOtherPlayerState == DiplomacyState.NoDiplomacy) continue;
player.GetCountryDiplomacyInfo(otherPlayer.Id, out var playerToOtherPlayer);
if (playerToOtherPlayer.DiplomacyState == DiplomacyState.NoDiplomacy) continue;
if (selfToOtherPlayerState == playerToOtherPlayer.DiplomacyState) score1 += 5;
else score1 -= 5;
}
if (score1> 0)
{
score1 += 5;
score += Mathf.Min(20, score1);
selfToPlayer.FeelingStrategyList.Add(FeelingStrategy.Wise);
}
if (score1 < 0)
{
score1 -= 5;
score += Mathf.Max(-20, score1);
selfToPlayer.FeelingStrategyList.Add(FeelingStrategy.Foolish);
}
// 迷人的 恼人的
if (aiDiff == AIDifficult.EASY && map.CheckIsRealPlayer(player.Id) && selfIsAi)
{
score += 15;
selfToPlayer.FeelingStrategyList.Add(FeelingStrategy.Charming);
}
if (aiDiff == AIDifficult.LUNATIC && map.CheckIsRealPlayer(player.Id) && selfIsAi)
{
score -= 15;
selfToPlayer.FeelingStrategyList.Add(FeelingStrategy.Annoying);
}
// 和平的 暴力的
if (!CheckTurnsAttacker(3, player.Id))
{
score += 15;
selfToPlayer.FeelingStrategyList.Add(FeelingStrategy.Peaceful);
}
else
{
score -= 15;
selfToPlayer.FeelingStrategyList.Add(FeelingStrategy.Violent);
}
// 外交的
if (selfToPlayer.IsEmbassy || playerToSelf.IsEmbassy)
{
score += 15;
selfToPlayer.FeelingStrategyList.Add(FeelingStrategy.Diplomatic);
}
// A的军事分>B 强大的 A的军事分<B弱小的
var playerScore2 = _tmpPlayerMilitaryScoreBuf.GetValueOrDefault(player.Id);
if (selfScore < playerScore2)
{
score += 15;
selfToPlayer.FeelingStrategyList.Add(FeelingStrategy.Powerful);
}
if (selfScore > playerScore2)
{
score -= 15;
selfToPlayer.FeelingStrategyList.Add(FeelingStrategy.Weak);
}
// 勇敢的
if (maxScorePlayer != Id)
{
player.GetCountryDiplomacyInfo(maxScorePlayer, out var playerToMaxScorePlayer);
if (playerToMaxScorePlayer != null && playerToMaxScorePlayer.DiplomacyState == DiplomacyState.War)
{
score += 15;
selfToPlayer.FeelingStrategyList.Add(FeelingStrategy.Brave);
}
}
// 威胁的
if (_tmpPlayerUnitGridIdListBuf.TryGetValue(player.Id, out var enemyUnitGridIdList) &&
HasAnyOverlap(_tmpSelfTerritoryInfluenceGridIdBuf, enemyUnitGridIdList))
{
score -= 15;
selfToPlayer.FeelingStrategyList.Add(FeelingStrategy.Threatening);
}
// 侵略的 A和B的领土边界相邻则持有该观点
if (_tmpPlayerTerritoryGridIdSetBuf.TryGetValue(player.Id, out var playerTerritorySet) &&
HasAnyOverlap(_tmpSelfTerritoryInfluenceGridIdBuf, playerTerritorySet))
{
score -= 15;
selfToPlayer.FeelingStrategyList.Add(FeelingStrategy.Intrusive);
}
// 主宰的
var count = 0f;
if (maxScorePlayer == player.Id)
{
_tmpCityListBuf ??= new List<CityData>();
_tmpCityListBuf.Clear();
map.GetCityDataListByPlayerId(player.Id, _tmpCityListBuf);
foreach (var city in _tmpCityListBuf)
{
if (_tmpOriginCityBuf.Contains(city.Id)) count++;
}
if (count >= originCityHalfCount)
{
score -= 15;
selfToPlayer.FeelingStrategyList.Add(FeelingStrategy.Dominative);
}
}
score = Mathf.Max(0, score);
selfToPlayer.FeelingValue = score;
if (score <= 10) selfToPlayer.FeelingState = FeelingState.Terrible;
else if (score <= 30) selfToPlayer.FeelingState = FeelingState.Suspicion;
else if (score <= 60) selfToPlayer.FeelingState = FeelingState.Indifferent;
else if (score <= 85) selfToPlayer.FeelingState = FeelingState.Appreciate;
else if (score <= 100) selfToPlayer.FeelingState = FeelingState.Trust;
}
}
private bool HasAnyOverlap(HashSet<uint> targetSet, List<uint> sourceList)
{
foreach (var id in sourceList)
{
if (targetSet.Contains(id)) return true;
}
return false;
}
private bool HasAnyOverlap(HashSet<uint> setA, HashSet<uint> setB)
{
if (setA.Count == 0 || setB.Count == 0) return false;
var source = setA.Count <= setB.Count ? setA : setB;
var target = source == setA ? setB : setA;
foreach (var gridId in source)
{
if (target.Contains(gridId)) return true;
}
return false;
}
// 刷新对他国的外交关系,回合开始时调用
public void RefreshDiplomacyState(MapData map)
{
foreach (var player in map.PlayerMap.PlayerDataList)
{
if (!player.IsSurvival) continue;
if (player.Id == Id) continue;
this.GetCountryDiplomacyInfo(player.Id, out var selfToPlayer);
player.GetCountryDiplomacyInfo(Id, out var playerToSelf);
// 如果没有外交关系,且没有见过面,则设置为无外交关系
if (!MeetPlayers.Contains(player.Id)) selfToPlayer.DiplomacyState = DiplomacyState.NoDiplomacy;
// 如果没有外交关系,且见过面,则设置为中立
else if (selfToPlayer.DiplomacyState == DiplomacyState.NoDiplomacy)
selfToPlayer.DiplomacyState = DiplomacyState.Neutral;
// 如果上一回合回合内双方未发生战斗,则该回合开始时变为中立关系
//判断有没有对方单位站在我方city上
bool otherPlayerOnSelfCity = false;
_tmpCitySetBuf ??= new HashSet<CityData>();
_tmpCitySetBuf.Clear();
map.GetCityDataListByPlayerId(Id,_tmpCitySetBuf);
foreach (var city in _tmpCitySetBuf)
{
if(!map.GetGridDataByCityId(city.Id,out var grid))continue;
if(!map.GetUnitDataByGid_Core(grid.Id,out var unit))continue;
if(!map.GetPlayerDataByUnitId(unit.Id,out var tPlayer))continue;
if (tPlayer.Id == player.Id)
{
otherPlayerOnSelfCity = true;
break;
}
}
if (selfToPlayer.DiplomacyState == DiplomacyState.War && !LastAttackPlayers.Contains(player.Id) && !otherPlayerOnSelfCity)
selfToPlayer.DiplomacyState = DiplomacyState.Neutral;
// 联盟破裂转中立
if (selfToPlayer.DiplomacyState == DiplomacyState.LeagueRupture &&
playerToSelf.DiplomacyState == DiplomacyState.LeagueRupture &&
selfToPlayer.IsLeagueRupture && playerToSelf.IsLeagueRupture)
{
selfToPlayer.DiplomacyState = DiplomacyState.Neutral;
playerToSelf.DiplomacyState = DiplomacyState.Neutral;
selfToPlayer.IsLeagueRupture = false;
playerToSelf.IsLeagueRupture = false;
}
}
}
public int GetTechCost(TechInfo techInfo)
{
return Main.MapData.GetCityCount(Id) * techInfo.CostLevel + 4;
}
//判断该玩家能否"显示"某个资源如metal,crop,starfish
public bool CanShowResource(ResourceType resource)
{
if (resource is ResourceType.Metal)
return TechTree.CheckIfHasTechAtom(TechAtom.UnitSkillMOUNTAINMOVE)
|| TechTree.CheckIfHasTechAtom(TechAtom.BuildMine)
|| TechTree.CheckIfHasTechAtom(TechAtom.BuildForge);
if (resource is ResourceType.Starfish)
return TechTree.CheckIfHasTechAtom(TechAtom.UnitSkillOCEANMOVE)
|| TechTree.CheckIfHasTechAtom(TechAtom.UnitActionGather);
if (resource is ResourceType.Crop)
return TechTree.CheckIfHasTechAtom(TechAtom.BuildFarm)
|| TechTree.CheckIfHasTechAtom(TechAtom.GainFruit);
return true;
}
//获取英雄相关所有消耗折扣
public float GetHeroCostDiscount(MapData mapData, out float discount)
{
discount = 1;
foreach (var card in PlayerCultureInfo.CardList)
{
var cardDiscount = card.GetHeroCostDiscount(mapData, this);
if (cardDiscount < discount) discount = cardDiscount;
}
return discount;
}
// 是否可以用文化值购买巨人
public virtual bool CanBuyBigGuyWithCulture(MapData mapData)
{
foreach (var card in PlayerCultureInfo.CardList)
{
if (card.CanBuyBigGuyWithCulture(mapData, this)) return true;
}
return false;
}
// 文化值是否能转换成金币
public virtual bool CanConvertCultureToGold(MapData mapData)
{
foreach (var card in PlayerCultureInfo.CardList)
{
if (card.CanConvertCultureToGold(mapData, this)) return true;
}
return false;
}
// 检查是否开启了对应 Temple Resource
public bool CheckResourceOpen(MapData mapData, ResourceType resourceType)
{
foreach (var card in PlayerCultureInfo.CardList)
{
if (card.CheckResourceOpen(mapData, this, resourceType)) return true;
}
return false;
}
// 投降
public void Surrender(MapData map)
{
// 退出前解除所有盟友/刚背盟关系League / LeagueRupture → Neutral
// LeagueRupture 自动转 Neutral 依赖双方都跑 OnTurnEnd 标 IsLeagueRupture投降后该标记永远凑不齐 → 必须主动清
foreach (var otherPlayer in map.PlayerMap.PlayerDataList)
{
if (otherPlayer.Id == Id) continue;
if (!this.GetCountryDiplomacyInfo(otherPlayer.Id, out var selfToOther)) continue;
if (selfToOther.DiplomacyState != DiplomacyState.League
&& selfToOther.DiplomacyState != DiplomacyState.LeagueRupture) continue;
Main.PlayerLogic.SetDiplomacyLeague(map, this, otherPlayer, DiplomacyState.Neutral);
selfToOther.IsEmbassy = false;
selfToOther.IsLeagueRupture = false;
if (otherPlayer.GetCountryDiplomacyInfo(Id, out var otherToSelf))
{
otherToSelf.IsEmbassy = false;
otherToSelf.IsLeagueRupture = false;
}
}
IsSurrender = true;
using var pooledSelfUnits = THCollectionPool.GetHashSetHandle<UnitData>(out var selfUnits);
map.GetUnitDataListByPlayerId(Id, selfUnits);
// 先播放Fog特效并销毁渲染层再清除数据层
foreach (var unit in selfUnits)
{
unit.Grid(map)?.Renderer(map)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Fog));
unit.Renderer(map)?.Die();
}
foreach (var unit in selfUnits) Main.UnitLogic.UnitUnnaturalDie(map, unit);
}
}
// 奇观信息
[MemoryPackable]
public partial class WonderData
{
// 所有奇观信息
public Dictionary<WonderTypeEnum, WonderState> WonderInfoDict;
public HashSet<WonderTypeEnum> WonderRenderMark;
[MemoryPackConstructor]
public WonderData()
{
WonderInfoDict = new Dictionary<WonderTypeEnum, WonderState>();
WonderRenderMark = new HashSet<WonderTypeEnum>();
foreach (WonderTypeEnum wonderType in System.Enum.GetValues(typeof(WonderTypeEnum)))
WonderInfoDict[wonderType] = WonderState.NO_HINT;
}
public WonderData(WonderData copyData)
{
WonderInfoDict = new Dictionary<WonderTypeEnum, WonderState>(copyData.WonderInfoDict);
WonderRenderMark = new HashSet<WonderTypeEnum>(copyData.WonderRenderMark);
foreach (var kv in copyData.WonderInfoDict)
{
WonderInfoDict[kv.Key] = kv.Value;
}
foreach (var typeEnum in copyData.WonderRenderMark) WonderRenderMark.Add(typeEnum);
}
public void DeepCopy(WonderData copyData)
{
WonderInfoDict.Clear();
WonderRenderMark.Clear();
foreach (var kv in copyData.WonderInfoDict) WonderInfoDict[kv.Key] = kv.Value;
foreach (var typeEnum in copyData.WonderRenderMark) WonderRenderMark.Add(typeEnum);
}
// 获取某个奇观的状态
public WonderState GetWonderState(WonderTypeEnum wonderType)
{
if (WonderInfoDict.TryGetValue(wonderType, out var state))
{
return state;
}
return WonderState.NO_HINT;
}
public void SetWonderState(WonderTypeEnum wonderType, WonderState state)
{
WonderInfoDict[wonderType] = state;
WonderRenderMark.Add(wonderType);
}
// 判断某个奇观是否变动
public bool IsWonderRenderMark(WonderTypeEnum wonderType)
{
return WonderRenderMark.Contains(wonderType);
}
public bool IsWonderLibraryBuilt(WonderLibrary wonderLibrary,PlayerData player)
{
var empire = new Empire(player.CivEnum, player.ForceEnum);
foreach (WonderTypeEnum wonderType in Enum.GetValues(typeof(WonderTypeEnum)))
if (Table.Instance.LibraryDataAssets.GetLibraryInfoByWonder(empire, wonderType, out var info)
&& info.WonderLibraryID == wonderLibrary
&& WonderInfoDict.TryGetValue(wonderType, out var v) && v == WonderState.FINISH_BUILD)
return true;
return false;
}
}
// 视野信息
[MemoryPackable]
public partial class MapSightData
{
public HashSet<uint> SightGidSet;
[MemoryPackConstructor]
public MapSightData()
{
SightGidSet = new HashSet<uint>();
}
public MapSightData(MapSightData copyData)
{
SightGidSet = new HashSet<uint>(copyData.SightGidSet);
}
public void DeepCopy(MapSightData copyData)
{
SightGidSet.Clear();
foreach (var gid in copyData.SightGidSet) SightGidSet.Add(gid);
}
// 判断是否在视野内
public bool CheckIsInSight(uint gid)
{
return SightGidSet.Contains(gid);
}
//更新unit移动路径上的视野 便捷版 unit在gridpos上 更新视野
public bool UpdateSightByPath(UnitData unit,PlayerData player,Vector2Int pos,MapData map)
{
if (!map.GridMap.GetGridDataByPos(pos.x, pos.y, out var grid))
{
Debug.Log("错误!UpdateSightByPath 时无法通过x,y获得grid");
return false;
}
var range = unit?.GetSightRange(map,grid) ?? 1;
var list = map.GridMap.GetAroundGridIdList(range,grid);
var count = Main.PlayerLogic.UpdateSight_LogicView(map, player, list);
unit.HeroTask(map)?.OnExploredGrids(map, unit, count);
foreach (var kv in player.PlayerHeroData.HeroTaskDict) kv.Value.OnAnyExploredGrids(map, unit, count);
return true;
}
}
// 科技信息
[MemoryPackable]
public partial class TechTreeData
{
public HashSet<TechType> TechSet;
[MemoryPackIgnore] public HashSet<uint> TechAtomActionCacheSet;
[MemoryPackIgnore] public HashSet<TechAtom> TechAtomCacheSet;
[MemoryPackConstructor]
public TechTreeData()
{
TechSet = new HashSet<TechType>();
TechAtomActionCacheSet = new HashSet<uint>();
TechAtomCacheSet = new HashSet<TechAtom>();
}
public TechTreeData(PlayerData player, List<TechType> startTechList = null)
{
TechSet = new HashSet<TechType>();
TechAtomActionCacheSet = new HashSet<uint>();
TechAtomCacheSet = new HashSet<TechAtom>();
if (startTechList != null)
{
foreach(var t in startTechList) LearnTech(t, player);
}
}
public TechTreeData(TechTreeData copyData)
{
TechSet = new HashSet<TechType>(copyData.TechSet);
TechAtomActionCacheSet = new HashSet<uint>(copyData.TechAtomActionCacheSet);
TechAtomCacheSet = new HashSet<TechAtom>(copyData.TechAtomCacheSet);
}
// MemoryPack 反序列化之后的后处理
[MemoryPackOnDeserialized]
public void OnAfterMemoryPackDeserialize()
{
TechAtomActionCacheSet ??= new HashSet<uint>();
foreach (var tech in TechSet)
{
var techInfo = Table.Instance.TechDataAssets.GetTechInfo(tech);
foreach (var techAtom in techInfo.TechAtomList)
{
TechAtomCacheSet.Add(techAtom);
if (!Table.Instance.TechDataAssets.GetTechAtomInfo(techAtom, out var info)) continue;
if(info.EnableAction)
foreach(var t in info.TechActions)
TechAtomActionCacheSet.Add(t.Id);
}
}
}
public void DeepCopy(TechTreeData copyData)
{
TechSet.Clear();
TechAtomActionCacheSet.Clear();
foreach (var tech in copyData.TechSet) TechSet.Add(tech);
foreach (var act in copyData.TechAtomActionCacheSet) TechAtomActionCacheSet.Add(act);
foreach (var atom in copyData.TechAtomCacheSet) TechAtomCacheSet.Add(atom);
}
public void LearnTech(TechType techType, PlayerData player)
{
if (!TechSet.Add(techType)) return;
if (player.PlayerHeroData != null)
{
foreach (var kv in player.PlayerHeroData.HeroTaskDict)
kv.Value.OnTechGet(player);
}
var techInfo = Table.Instance.TechDataAssets.GetTechInfo(techType);
foreach (var techAtom in techInfo.TechAtomList)
{
TechAtomCacheSet.Add(techAtom);
if (!Table.Instance.TechDataAssets.GetTechAtomInfo(techAtom, out var info)) continue;
if(info.EnableAction)
foreach(var t in info.TechActions)
TechAtomActionCacheSet.Add(t.Id);
}
}
// 删除科技
public void RemoveTech(TechType techType)
{
TechSet.Remove(techType);
}
// 是否拥有某个科技
public bool CheckIfHasTech(TechType techType) { return TechSet.Contains(techType); }
public bool CheckIfHasTech(List<TechType> techType)
{
foreach (var tech in techType)
if (CheckIfHasTech(tech))
return true;
return false;
}
//是否拥有某个原子科技
public bool CheckIfHasTechAtom(TechAtom atom) { return TechAtomCacheSet.Contains(atom); }
public bool HasAllTech(PlayerData player)
{
bool ret = true;
Table.Instance.PlayerDataAssets.GetPlayerInfo(player, out var info);
foreach (TechType tech in info.TechPool)
{
var techInfo = Table.Instance.TechDataAssets.GetTechInfo(tech);
if (CheckIfHasTech(tech))
continue;
ret = false;
break;
}
return ret;
}
public int GetScore()
{
int ret = 0;
foreach (var tech in TechSet)
{
var techInfo = Table.Instance.TechDataAssets.GetTechInfo(tech);
ret += techInfo.CostLevel * 100;
}
return ret;
}
public bool CheckActionCan(CommonActionId actionId)
{
if (!Table.Instance.ActionDataAssets.GetActionInfo(actionId, out var info)) return false;
if (info.NoNeedTech) return true;
return TechAtomActionCacheSet.Contains(actionId.Id);
}
public bool CheckIfTechUnsee(TechType techType)
{
return !HasFatherTech(Table.Instance.TechDataAssets.GetTechInfo(techType).FatherTechList);
}
private bool HasFatherTech(List<TechType> techList)
{
foreach(var tech in techList)
if (TechSet.Contains(tech))
return true;
return false;
}
public bool CheckIfTechCanLearn(TechType techType)
{
//Debug.Log(techType);
return !TechSet.Contains(techType) && !CheckIfTechUnsee(techType);
}
}
// 外交信息
[MemoryPackable]
public partial class DiplomacyData
{
public List<CountryDiplomacyInfo> Info;
private Dictionary<uint, CountryDiplomacyInfo> _infoDict;
[MemoryPackConstructor]
public DiplomacyData()
{
Info = new List<CountryDiplomacyInfo>();
}
public DiplomacyData(DiplomacyData copyData)
{
Info = new List<CountryDiplomacyInfo>();
}
// MemoryPack 反序列化之后的后处理
[MemoryPackOnDeserialized]
public void OnAfterMemoryPackDeserialize()
{
Refresh();
}
public void DeepCopy(DiplomacyData copyData)
{
for (int i = 0; i < copyData.Info.Count; i++)
{
if (Info.Count <= i) Info.Add(new CountryDiplomacyInfo());
Info[i].DeepCopy(copyData.Info[i]);
}
}
public bool CheckTurnAttacker(uint selfId, uint targetId, uint turn)
{
GetCountryDiplomacyInfo(selfId, targetId, out var info);
if (info == null) return false;
return info.AttackTurn != 0 && info.AttackTurn >= turn;
}
public void AddTurnAttacker(uint selfId, uint targetId, uint turn)
{
GetCountryDiplomacyInfo(selfId, targetId, out var info);
if (info == null) return;
info.AttackTurn = turn;
}
public void AddCountryDiplomacyInfo(uint targetId)
{
Refresh();
if (_infoDict.ContainsKey(targetId)) return;
var info = new CountryDiplomacyInfo { PlayerId = targetId };
Info.Add(info);
}
public bool GetCountryDiplomacyInfo(uint selfId, uint targetId, out CountryDiplomacyInfo info)
{
info = null;
if (selfId == targetId) return false;
Refresh();
return _infoDict.TryGetValue(targetId, out info);
}
private void Refresh()
{
if (_infoDict == null) _infoDict = new Dictionary<uint, CountryDiplomacyInfo>();
if (_infoDict.Count == Info.Count) return;
_infoDict.Clear();
foreach (var countryDiplomacy in Info) _infoDict[countryDiplomacy.PlayerId] = countryDiplomacy;
}
}
public static class DiplomacyDataExtensions
{
public static bool GetCountryDiplomacyInfo(this PlayerData player, uint targetId, out CountryDiplomacyInfo info)
{
return player.DiplomacyData.GetCountryDiplomacyInfo(player.Id, targetId, out info);
}
public static void GetCountryDiplomacyInfo(this PlayerData player, PlayerData targetPlayer, out CountryDiplomacyInfo info)
{
player.DiplomacyData.GetCountryDiplomacyInfo(player.Id, targetPlayer.Id, out info);
}
public static void AddTurnAttacker(this PlayerData player, uint targetId, uint turn)
{
player.DiplomacyData.AddTurnAttacker(player.Id, targetId, turn);
}
public static void AddTurnAttacker(this PlayerData player, PlayerData targetPlayer, uint turn)
{
player.DiplomacyData.AddTurnAttacker(player.Id, targetPlayer.Id, turn);
}
public static bool CheckTurnAttacker(this PlayerData player, uint targetId, uint turn)
{
return player.DiplomacyData.CheckTurnAttacker(player.Id, targetId, turn);
}
public static bool CheckTurnAttacker(this PlayerData player, PlayerData targetPlayer, uint turn)
{
return player.DiplomacyData.CheckTurnAttacker(player.Id, targetPlayer.Id, turn);
}
}
// 对他国的数据
[MemoryPackable]
public partial class CountryDiplomacyInfo
{
public uint PlayerId;
// 我对国家 PlayerId 的外交关系
public DiplomacyState DiplomacyState;
// 我对国家 PlayerId 的好感值
public float FeelingValue;
// 我对国家 PlayerId 的好感态度
public FeelingState FeelingState;
// 我对国家 PlayerId 的好感策略
public List<FeelingStrategy> FeelingStrategyList;
// 国家 PlayerId 攻击我的回合记录
public uint AttackTurn;
// 国家 PlayerId 是否在我国建立了大使馆
public bool IsEmbassy;
// 联盟破裂回合结束标记
public bool IsLeagueRupture;
// 国家对我发起的结盟请求
public bool IsLeagueRequest;
// 冷静期标记
[MemoryPackIgnore]
public int IsCalm;
// PlayerId 是我的队友 (逻辑相当于同盟)
public bool IsTeammate;
[MemoryPackConstructor]
public CountryDiplomacyInfo()
{
AttackTurn = 0;
IsCalm = 0;
IsEmbassy = false;
IsLeagueRupture = false;
IsLeagueRequest = false;
IsTeammate = false;
FeelingStrategyList = new List<FeelingStrategy>();
}
public CountryDiplomacyInfo(CountryDiplomacyInfo copyData)
{
PlayerId = copyData.PlayerId;
AttackTurn = copyData.AttackTurn;
DiplomacyState = copyData.DiplomacyState;
FeelingValue = copyData.FeelingValue;
FeelingState = copyData.FeelingState;
FeelingStrategyList= new List<FeelingStrategy>();
foreach (var strategy in copyData.FeelingStrategyList)FeelingStrategyList.Add(strategy);
IsEmbassy = copyData.IsEmbassy;
IsLeagueRupture = copyData.IsLeagueRupture;
IsLeagueRequest = copyData.IsLeagueRequest;
IsCalm = copyData.IsCalm;
IsTeammate = copyData.IsTeammate;
}
public void DeepCopy(CountryDiplomacyInfo copyData)
{
PlayerId = copyData.PlayerId;
AttackTurn = copyData.AttackTurn;
DiplomacyState = copyData.DiplomacyState;
FeelingValue = copyData.FeelingValue;
FeelingState = copyData.FeelingState;
FeelingStrategyList.Clear();
foreach (var strategy in copyData.FeelingStrategyList)FeelingStrategyList.Add(strategy);
IsEmbassy = copyData.IsEmbassy;
IsLeagueRupture = copyData.IsLeagueRupture;
IsLeagueRequest = copyData.IsLeagueRequest;
IsCalm = copyData.IsCalm;
IsTeammate = copyData.IsTeammate;
}
}
// 英雄的数据
[MemoryPackable]
public partial class PlayerHeroData
{
//当前最多获取英雄数量
public int MaxHeroCount;
//当前已经拥有的英雄数量
public int HeroCount => HeroList.Count;
//当前已经有的英雄
public List<UnitFullType> HeroList;
public GiantType LeaderGiantType;
public Dictionary<UnitFullType, HeroTaskContentBase> HeroTaskDict;
[MemoryPackIgnore] private List<GridData> _tmpGridListBuf;
[MemoryPackConstructor]
public PlayerHeroData()
{
MaxHeroCount = 1;
HeroList = new List<UnitFullType>();
LeaderGiantType = GiantType.None;
HeroTaskDict = new Dictionary<UnitFullType, HeroTaskContentBase>();
}
public PlayerHeroData(PlayerHeroData copyData)
{
MaxHeroCount = copyData.MaxHeroCount;
HeroList = new List<UnitFullType>(copyData.HeroList);
LeaderGiantType = copyData.LeaderGiantType;
HeroTaskDict = new Dictionary<UnitFullType, HeroTaskContentBase>();
foreach (var kv in copyData.HeroTaskDict) HeroTaskDict.TryAdd(kv.Key,kv.Value?.GetCopyHeroTaskContent());
}
public void DeepCopy(PlayerHeroData copyData)
{
MaxHeroCount = copyData.MaxHeroCount;
HeroList = new List<UnitFullType>(copyData.HeroList);
LeaderGiantType = copyData.LeaderGiantType;
HeroTaskDict.Clear();
foreach (var kv in copyData.HeroTaskDict) HeroTaskDict.TryAdd(kv.Key,kv.Value?.GetCopyHeroTaskContent());
}
public void Init(CivEnum civEnum, ForceEnum forceEnum)
{
LeaderGiantType = civEnum switch
{
CivEnum.Egyptian => GiantType.EgyptianRemilia,
CivEnum.French => GiantType.FrenchKaguya,
CivEnum.Germany => GiantType.GermanyKanako,
CivEnum.Indian => GiantType.IndianSatori,
_ => GiantType.None
};
}
public GiantType GetLeaderGiantType()
{
return LeaderGiantType;
}
public void OnTurnStart(MapData map, PlayerData player)
{
foreach (var heroType in HeroList)
{
if (heroType.GiantType != GiantType.IndianKoishi) continue;
var count = 0;
foreach (var unit in map.UnitMap.UnitList)
{
if (unit.GiantType != GiantType.IndianKoishi) continue;
if (!map.GetPlayerIdByUnitId(unit.Id, out var ownerId)) continue;
if (ownerId != player.Id) continue;
count++;
}
if (count >= heroType.UnitLevel) continue;
map.GetCapitalCityDataByPlayerId(player.Id, out var city);
var cityGrid = city?.Grid(map);
if (cityGrid == null) continue;
_tmpGridListBuf ??= new List<GridData>();
_tmpGridListBuf.Clear();
map.GridMap.GetAroundGridData(1, 1, cityGrid, _tmpGridListBuf);
using var pooledRandomList = THCollectionPool.GetListHandle<GridData>(out var randomList);
foreach (var grid in _tmpGridListBuf)
{
if (grid == cityGrid) continue;
if (grid.RealUnit(map,out _)) continue;
if(!map.CheckLandTypeForGrid(heroType, grid))continue;
randomList.Add(grid);
}
if (randomList.Count == 0) continue;
var index = map.Net.GetRandom(map).Next(0, randomList.Count - 1); // 生成 index
if (map.AddUnitData(randomList[index].Id, city.Id, heroType, out var newUnit))
{
// 更新新单位的视野
Main.PlayerLogic.UpdateSight_LogicView(map, player,
map.GridMap.GetAroundGridIdList(newUnit.GetSightRange(map), randomList[index]));
}
}
}
public bool HasHero(GiantType giantType)
{
foreach (var heroType in HeroList)
if (heroType.GiantType == giantType)
return true;
return false;
}
public bool AddHero(MapData map,PlayerData player,GiantType giantType)
{
var type = new UnitFullType(UnitType.Giant, giantType, 1);
//如果是三发英雄起步是Lv2
//if (HeroCount > 1) type.UnitLevel = 2;
if (HasHero(giantType)) return false;
HeroList.Add(type);
//如果Lv3或者其他意外就不需要填加对应task了
if (HeroTaskManager.Instance.GetHeroTaskContent(type, out var task))
{
HeroTaskDict.Add(type,task);
task.OnTaskInit(map,player);
}
// moment
foreach (var item in player.MomentData.Items) item.OnAddHero(map, player, type);
return true;
}
//发生在英雄升级的时候更新heroList让新的英雄type替换旧的type
public bool UpdateHero(MapData map,PlayerData player,UnitFullType newType)
{
if (!HasHero(newType.GiantType)) return false;
//通知UI界面 更新heroButton按钮
if (this == Main.MapData.PlayerMap.SelfPlayerData.PlayerHeroData)
{
EventManager.Publish(new UpdateUIBottomBottomBarHeroButtonAvatar());
//UIManager.Instance.BottomBarUI.UpdateHeroButtonSprite();
}
for(int i =0;i < HeroCount;i++)
if (HeroList[i].GiantType == newType.GiantType)
{
HeroTaskDict.Remove(HeroList[i]);
HeroList[i] = newType;
//如果不是满级,要更新新的任务
if(newType.UnitLevel < 4){
if (!HeroTaskManager.Instance.GetHeroTaskContent(newType, out var task)) return false;
HeroTaskDict.Add(newType,task);
task.OnTaskInit(map,player);
}
return true;
}
return false;
}
public bool ForceFinishHeroTask(GiantType giant)
{
if (!HasHero(giant)) return false;
if(!GetHeroTask(giant,out var task))return false;
//task.Level = task.TargetLevel;
if (task.IsForceFinished) return false;
task.ForceFinish();
return true;
}
public bool GetHeroTask(GiantType giant, out HeroTaskContentBase task)
{
foreach(var t in HeroTaskDict)
if (t.Key.GiantType == giant)
{
task = t.Value;
return true;
}
task = null;
return false;
}
public void UpdateHeroMaxCount(uint unitLevel)
{
if (unitLevel > MaxHeroCount)
MaxHeroCount = Mathf.Min(3, (int)unitLevel);
}
//获取英雄的升级费用值
public int GetHeroFinishTaskCost(GiantType giant, float discount = 1f)
{
foreach (var heroType in HeroList)
if (heroType.GiantType == giant)
{
if(GetHeroTask(giant,out var task))
{
var baseCost = task.GetFinishCost() * (int)heroType.UnitLevel;
if (discount < 1f && discount > 0f)
return Mathf.CeilToInt(baseCost * discount);
return baseCost;
}
}
return 0;
}
public uint GetHeroLevel(GiantType giant)
{
foreach (var heroType in HeroList)
if (heroType.GiantType == giant)
return heroType.UnitLevel;
return 0;
}
//返回现在外部的herobutton应该显示谁的头像
public Sprite GetHeroButtonSprite()
{
var giant = GetLeaderGiantType();
if (HeroCount > 0)
giant = HeroList[HeroCount - 1].GiantType;
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(new UnitFullType(UnitType.Giant, giant, 1),
out var info))
return null;
if (!Table.Instance.UnitTypeDataAssets.GetUnitSpriteByInfo(info,Main.MapData.PlayerMap.SelfPlayerData,out var sprite))
return null;
return sprite;
}
//返回现在外部的herobutton是否应该显示提示符号
public bool GetHeroButtonHint()
{
return HeroCount < MaxHeroCount;
}
}
// 命名器的数据
[MemoryPackable]
public partial class PlayerNamerData
{
//City命名
[MemoryPackInclude]
private List<CityLibrary> _cityNamer;
[MemoryPackInclude]
private int _cityNamerPointer;
[MemoryPackConstructor]
public PlayerNamerData()
{
_cityNamer = new List<CityLibrary>();
_cityNamerPointer = 0;
}
public void Init(CivEnum civEnum)
{
var civId = Table.Instance.TransCivEnumToCivId(civEnum);
if (!Table.Instance.CivDataAssets.GetCivInfo(civId, out var info))
{
Debug.LogError($"PlayerNamerData.Init: CivDataAssets missing for CivEnum={civEnum}, CivId={civId}, fallback to CivId=0");
// fallback: 借用埃及的城市名,防止空列表导致崩溃
if (Table.Instance.CivDataAssets.GetCivInfo(CivEnum.Egyptian, out var fallbackInfo))
{
foreach (var t in fallbackInfo.CityInfoList)
_cityNamer.Add(t.CityNameEnum);
}
else
{
_cityNamer.Add(CityLibrary.Cairo);
}
Table.Instance.ShuffleRange(_cityNamer, 1);
return;
}
foreach (var t in info.CityInfoList)
_cityNamer.Add(t.CityNameEnum);
Table.Instance.ShuffleRange(_cityNamer, 1);
}
public PlayerNamerData(PlayerNamerData copyData)
{
_cityNamer = new List<CityLibrary>(copyData._cityNamer);
_cityNamerPointer = copyData._cityNamerPointer;
}
public void DeepCopy(PlayerNamerData copyData)
{
_cityNamer = new List<CityLibrary>(copyData._cityNamer);
_cityNamerPointer = copyData._cityNamerPointer;
}
public CityLibrary GetSetNextCity()
{
if (_cityNamer.Count == 0)
{
Debug.LogError($"PlayerNamerData: _cityNamer is empty, CivDataAssets may be missing for this player");
return CityLibrary.Cairo; // fallback to prevent crash
}
if (_cityNamerPointer >= _cityNamer.Count)
_cityNamerPointer = 1;//重复命名的时候,跳过首都
if (_cityNamerPointer >= _cityNamer.Count)
_cityNamerPointer = 0; // 如果只有一个城市回到0
return _cityNamer[_cityNamerPointer++];
}
public bool CheckIsCityNameSelfBuild(CityLibrary cityLibrary)
{
for (int i = 0; i < _cityNamerPointer; i++)
{
if (i >= _cityNamer.Count) break;
if (_cityNamer[i] == cityLibrary) return true;
}
return false;
}
}
// 文化值系统
[MemoryPackable]
public partial class PlayerCultureInfo
{
public int PlayerCulture;
public List<CultureCardType> CultureCardList;
public List<CultureCardBase> CardList;
[MemoryPackConstructor]
public PlayerCultureInfo()
{
PlayerCulture = 0;
CultureCardList = new List<CultureCardType>();
CardList = new List<CultureCardBase>();
}
public PlayerCultureInfo(PlayerCultureInfo copyData)
{
PlayerCulture = copyData.PlayerCulture;
CultureCardList = new List<CultureCardType>();
foreach (var card in copyData.CultureCardList) CultureCardList.Add(card);
CardList = new List<CultureCardBase>();
foreach (var card in copyData.CardList)
{
CardList.Add(CultureCardFactory.GetCultureCardBase(card.GetCardType()));
}
}
public void DeepCopy(PlayerCultureInfo copyData)
{
PlayerCulture = copyData.PlayerCulture;
CultureCardList ??= new List<CultureCardType>();
CultureCardList.Clear();
foreach (var card in copyData.CultureCardList) CultureCardList.Add(card);
CardList ??= new List<CultureCardBase>();
CardList.Clear();
foreach (var card in copyData.CardList)
{
CardList.Add(CultureCardFactory.GetCultureCardBase(card.GetCardType()));
}
}
// 购买文化卡
public bool TryBuyCultureCard(MapData map, PlayerData player, CultureCardType cardType)
{
if (!Table.Instance.CultureCardDataAssets.GetCultureCardInfo(cardType, out var info)) return false;
if (!info.CheckEmpireCanUseCard(player.Empire)) return false;
if (!info.CheckPrerequisiteCardsOwned(player)) return false;
if (PlayerCulture < info.Cost) return false;
PlayerCulture -= info.Cost;
CultureCardList.Add(cardType);
var card = CultureCardFactory.GetCultureCardBase(cardType);
card.OnGetCultureCard(map, player);
CardList.Add(card);
return true;
}
// 能否购买文化卡
public bool CheckCanBuyCultureCard(MapData map, PlayerData player, CultureCardType cardType)
{
if (!Table.Instance.CultureCardDataAssets.GetCultureCardInfo(cardType, out var info)) return false;
if (!info.CheckEmpireCanUseCard(player.Empire)) return false;
if (!info.CheckPrerequisiteCardsOwned(player)) return false;
if (PlayerCulture < info.Cost) return false;
return true;
}
public void OnTurnStart(MapData map, PlayerData player)
{
/*if (!player.IsSurvival) return;
if (map.CityMap.GetCityById(player.CradleCityId, out var cradleCity))
{
if (cradleCity.Player(map) == player) PlayerCulture += 3;
}
var cityList = new List<CityData>();
map.GetCityDataListByPlayerId(player.Id, cityList);
foreach (var city in cityList)
{
if (city == cradleCity) continue;
if (city.IsCapital) PlayerCulture += 1;
}*/
}
public void OnUnitTransform(MapData map, UnitData unit, UnitFullType origin, UnitFullType target)
{
if (origin.UnitLevel == 2 && target.UnitLevel == 3) PlayerCulture += 15;
if (origin.UnitLevel == 3 && target.UnitLevel == 4) PlayerCulture += 50;
}
public void OnWonderBuild(MapData map, PlayerData player, WonderTypeEnum wonderType)
{
PlayerCulture += 5;
}
}
}