/* * @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 PlayerDataList; [MemoryPackIgnore] public uint SelfPlayerId; [MemoryPackIgnore] public PlayerData SelfPlayerData => GetPlayerData(SelfPlayerId); private Dictionary _playerDataDict; // 无数据初始化 [MemoryPackConstructor] public PlayerMapData() { PlayerDataList = new List(); _playerDataDict = new Dictionary(); } // 配置初始化 public PlayerMapData(MapData map, MapIdGenerator idGenerator, NetMode netMode) { PlayerDataList = new List(); _playerDataDict = new Dictionary(); 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(); _playerDataDict = new Dictionary(); 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(); _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 MeetPlayers; public List CurAttackPlayers; public List 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 _tmpOriginCityBuf; [MemoryPackIgnore] private List _tmpCityListBuf; [MemoryPackIgnore] private HashSet _tmpCitySetBuf; [MemoryPackIgnore] private HashSet _tmpTerritoryGridIdBuf; [MemoryPackIgnore] private HashSet _tmpSelfTerritoryInfluenceGridIdBuf; [MemoryPackIgnore] private Dictionary _tmpPlayerMilitaryScoreBuf; [MemoryPackIgnore] private Dictionary> _tmpPlayerUnitGridIdListBuf; [MemoryPackIgnore] private Dictionary> _tmpPlayerTerritoryGridIdSetBuf; [MemoryPackIgnore] private Dictionary _tmpSelfToOtherDiplomacyStateBuf; // 无参数初始化 [MemoryPackConstructor] public PlayerData() { Sight = new MapSightData(); TechTree = new TechTreeData(); Wonder = new WonderData(); DiplomacyData = new DiplomacyData(); MeetPlayers = new List(); CurAttackPlayers = new List(); LastAttackPlayers = new List(); 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(); LastAttackPlayers = new List(); PlayerNamerData = new PlayerNamerData(); MomentData = new MomentData(); MomentData.InitItems(); PlayerCultureInfo = new PlayerCultureInfo(); MeetPlayers = new List(); 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(); foreach (var id in copyData.CurAttackPlayers) CurAttackPlayers.Add(id); LastAttackPlayers = new List(); foreach (var id in copyData.LastAttackPlayers) LastAttackPlayers.Add(id); MeetPlayers = new List(); 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(); _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(); _tmpSelfTerritoryInfluenceGridIdBuf ??= new HashSet(); _tmpPlayerMilitaryScoreBuf ??= new Dictionary(); _tmpPlayerUnitGridIdListBuf ??= new Dictionary>(); _tmpPlayerTerritoryGridIdSetBuf ??= new Dictionary>(); _tmpSelfToOtherDiplomacyStateBuf ??= new Dictionary(); _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(); _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(); _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的军事分 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(); _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 targetSet, List sourceList) { foreach (var id in sourceList) { if (targetSet.Contains(id)) return true; } return false; } private bool HasAnyOverlap(HashSet setA, HashSet 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(); _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(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 WonderInfoDict; public HashSet WonderRenderMark; [MemoryPackConstructor] public WonderData() { WonderInfoDict = new Dictionary(); WonderRenderMark = new HashSet(); foreach (WonderTypeEnum wonderType in System.Enum.GetValues(typeof(WonderTypeEnum))) WonderInfoDict[wonderType] = WonderState.NO_HINT; } public WonderData(WonderData copyData) { WonderInfoDict = new Dictionary(copyData.WonderInfoDict); WonderRenderMark = new HashSet(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 SightGidSet; [MemoryPackConstructor] public MapSightData() { SightGidSet = new HashSet(); } public MapSightData(MapSightData copyData) { SightGidSet = new HashSet(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 TechSet; [MemoryPackIgnore] public HashSet TechAtomActionCacheSet; [MemoryPackIgnore] public HashSet TechAtomCacheSet; [MemoryPackConstructor] public TechTreeData() { TechSet = new HashSet(); TechAtomActionCacheSet = new HashSet(); TechAtomCacheSet = new HashSet(); } public TechTreeData(PlayerData player, List startTechList = null) { TechSet = new HashSet(); TechAtomActionCacheSet = new HashSet(); TechAtomCacheSet = new HashSet(); if (startTechList != null) { foreach(var t in startTechList) LearnTech(t, player); } } public TechTreeData(TechTreeData copyData) { TechSet = new HashSet(copyData.TechSet); TechAtomActionCacheSet = new HashSet(copyData.TechAtomActionCacheSet); TechAtomCacheSet = new HashSet(copyData.TechAtomCacheSet); } // MemoryPack 反序列化之后的后处理 [MemoryPackOnDeserialized] public void OnAfterMemoryPackDeserialize() { TechAtomActionCacheSet ??= new HashSet(); 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) { 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 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 Info; private Dictionary _infoDict; [MemoryPackConstructor] public DiplomacyData() { Info = new List(); } public DiplomacyData(DiplomacyData copyData) { Info = new List(); } // 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(); 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 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(); } public CountryDiplomacyInfo(CountryDiplomacyInfo copyData) { PlayerId = copyData.PlayerId; AttackTurn = copyData.AttackTurn; DiplomacyState = copyData.DiplomacyState; FeelingValue = copyData.FeelingValue; FeelingState = copyData.FeelingState; FeelingStrategyList= new List(); 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 HeroList; public GiantType LeaderGiantType; public Dictionary HeroTaskDict; [MemoryPackIgnore] private List _tmpGridListBuf; [MemoryPackConstructor] public PlayerHeroData() { MaxHeroCount = 1; HeroList = new List(); LeaderGiantType = GiantType.None; HeroTaskDict = new Dictionary(); } public PlayerHeroData(PlayerHeroData copyData) { MaxHeroCount = copyData.MaxHeroCount; HeroList = new List(copyData.HeroList); LeaderGiantType = copyData.LeaderGiantType; HeroTaskDict = new Dictionary(); foreach (var kv in copyData.HeroTaskDict) HeroTaskDict.TryAdd(kv.Key,kv.Value?.GetCopyHeroTaskContent()); } public void DeepCopy(PlayerHeroData copyData) { MaxHeroCount = copyData.MaxHeroCount; HeroList = new List(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(); _tmpGridListBuf.Clear(); map.GridMap.GetAroundGridData(1, 1, cityGrid, _tmpGridListBuf); using var pooledRandomList = THCollectionPool.GetListHandle(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 _cityNamer; [MemoryPackInclude] private int _cityNamerPointer; [MemoryPackConstructor] public PlayerNamerData() { _cityNamer = new List(); _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(copyData._cityNamer); _cityNamerPointer = copyData._cityNamerPointer; } public void DeepCopy(PlayerNamerData copyData) { _cityNamer = new List(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 CultureCardList; public List CardList; [MemoryPackConstructor] public PlayerCultureInfo() { PlayerCulture = 0; CultureCardList = new List(); CardList = new List(); } public PlayerCultureInfo(PlayerCultureInfo copyData) { PlayerCulture = copyData.PlayerCulture; CultureCardList = new List(); foreach (var card in copyData.CultureCardList) CultureCardList.Add(card); CardList = new List(); foreach (var card in copyData.CardList) { CardList.Add(CultureCardFactory.GetCultureCardBase(card.GetCardType())); } } public void DeepCopy(PlayerCultureInfo copyData) { PlayerCulture = copyData.PlayerCulture; CultureCardList ??= new List(); CultureCardList.Clear(); foreach (var card in copyData.CultureCardList) CultureCardList.Add(card); CardList ??= new List(); 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(); 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; } } }