外交系统实现

This commit is contained in:
wuwenbo 2025-07-11 19:06:30 +08:00
parent 4956b27a23
commit 19c56ecf8f
4 changed files with 338 additions and 2 deletions

View File

@ -8,7 +8,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Logic.Action;
using Logic.AI;
using NUnit.Framework;
using Unity.VisualScripting;
using UnityEngine;
@ -28,6 +31,7 @@ namespace RuntimeData
EYE
}
// 奇观状态枚举
public enum WonderState
{
@ -39,6 +43,28 @@ namespace RuntimeData
}
// 外交关系枚举
public enum DiplomacyState
{
NoDiplomacy,
Neutral,
League,
War,
LeagueRupture,
}
// 好感状态枚举
public enum FeelingState
{
Trust,
Appreciate,
Indifferent,
Suspicion,
Terrible,
}
[Serializable]
public class PlayerMapData : ISerializationCallbackReceiver
{
@ -229,6 +255,7 @@ namespace RuntimeData
public List<uint> MeetPlayers;
public List<uint> CurAttackPlayers;
public List<uint> LastAttackPlayers;
public DiplomacyData DiplomacyData;
//用于记录是否死亡是否要centMessage来宣告死亡信息
public bool DieMark = false;
@ -239,6 +266,7 @@ namespace RuntimeData
Sight = new MapSightData();
TechTree = new TechTreeData();
Wonder = new WonderData();
DiplomacyData = new DiplomacyData();
MeetPlayers = new List<uint>();
CurAttackPlayers = new List<uint>();
LastAttackPlayers = new List<uint>();
@ -257,6 +285,7 @@ namespace RuntimeData
Sight = new MapSightData();
Wonder = new WonderData();
DiplomacyData = new DiplomacyData();
CurAttackPlayers = new List<uint>();
LastAttackPlayers = new List<uint>();
MeetPlayers = new List<uint>();
@ -277,6 +306,7 @@ namespace RuntimeData
Sight = new MapSightData(copyData.Sight);
TechTree = new TechTreeData(copyData.TechTree);
Wonder = new WonderData(copyData.Wonder);
DiplomacyData = new DiplomacyData(copyData.DiplomacyData);
CurAttackPlayers = new List<uint>();
foreach (var id in copyData.CurAttackPlayers) CurAttackPlayers.Add(id);
@ -305,6 +335,7 @@ namespace RuntimeData
Sight.DeepCopy(copyData.Sight);
TechTree.DeepCopy(copyData.TechTree);
Wonder.DeepCopy(copyData.Wonder);
DiplomacyData.DeepCopy(copyData.DiplomacyData);
CurAttackPlayers.Clear();
foreach (var id in copyData.CurAttackPlayers) CurAttackPlayers.Add(id);
@ -358,11 +389,186 @@ namespace RuntimeData
skill.OnTurnStart(this, map);
}
RefreshFeelingValue(map);
}
public void OnTurnEnd(MapData map)
{
foreach (var skill in Skills)skill.OnTurnEnd(this, map);
foreach (var player in map.PlayerMap.PlayerDataList)
{
if (player.Id == Id) continue;
DiplomacyData.GetCountryDiplomacyInfo(player.Id, out var selfToPlayer);
if (selfToPlayer.DiplomacyState == DiplomacyState.LeagueRupture) selfToPlayer.IsLeagueRupture = true;
}
}
// 添加当前回合攻击者
public void AddAttacker(uint playerId)
{
DiplomacyData.AddTurnAttacker(Turn, playerId);
}
// 检查前几回合内是否有攻击者
public bool CheckTurnsAttacker(uint turns, uint playerId)
{
for (int i = 0; i < turns; i++)
{
if (Turn < i) return false;
if (!DiplomacyData.CheckTurnAttacker(Turn - (uint)i, playerId)) continue;
return true;
}
return false;
}
// 刷新对他国好感值
public void RefreshFeelingValue(MapData map)
{
int maxScore = 0;
uint maxScorePlayer = 0;
foreach (var player in map.PlayerMap.PlayerDataList)
{
if (player.PlayerScore <= maxScore) continue;
maxScore = player.PlayerScore;
maxScorePlayer = player.Id;
}
foreach (var player in map.PlayerMap.PlayerDataList)
{
if (player.Id == Id) continue;
DiplomacyData.GetCountryDiplomacyInfo(player.Id, out var selfToPlayer);
player.DiplomacyData.GetCountryDiplomacyInfo(Id, out var playerToSelf);
if (playerToSelf == null || selfToPlayer == null) continue;
var score = 0f;
// 明智的 愚蠢的
var score1 = 0f;
foreach (var otherPlayer in map.PlayerMap.PlayerDataList)
{
if (otherPlayer.Id == Id || otherPlayer.Id == player.Id) continue;
DiplomacyData.GetCountryDiplomacyInfo(player.Id, out var selfToOtherPlayer);
if (selfToOtherPlayer.DiplomacyState == DiplomacyState.NoDiplomacy) continue;
player.DiplomacyData.GetCountryDiplomacyInfo(player.Id, out var playerToOtherPlayer);
if (playerToOtherPlayer.DiplomacyState == DiplomacyState.NoDiplomacy) continue;
if (selfToOtherPlayer.DiplomacyState == playerToOtherPlayer.DiplomacyState) score1 += 5;
else score1 -= 5;
}
if (score1 + 5 > 0) score += Mathf.Min(20, score1);
if (score1 - 5 < 0) score += Mathf.Max(-20, score1);
// 迷人的 恼人的
if (map.MapConfig.AIDiff == AIDifficult.EASY && player.Id == map.PlayerMap.SelfPlayerId &&
Id != map.PlayerMap.SelfPlayerId) score += 15;
if (map.MapConfig.AIDiff == AIDifficult.LUNATIC && player.Id == map.PlayerMap.SelfPlayerId &&
Id != map.PlayerMap.SelfPlayerId) score -= 15;
// 和平的 暴力的
if (!CheckTurnsAttacker(3, player.Id)) score += 15;
else score -= 15;
// 外交的
if (selfToPlayer.IsEmbassy || playerToSelf.IsEmbassy) score += 15;
// 强大的 弱小的
var selfScore = 0f;
var playerScore = 0f;
foreach (var gridId in Sight.SightGidSet)
{
if (!map.GetUnitDataByGid(gridId, out var unit)) continue;
if (!map.GetPlayerIdByUnitId(unit.Id, out var ownerId)) continue;
if (ownerId == Id) selfScore += unit.GetCost();
if (ownerId == player.Id) playerScore += unit.GetCost();
}
if (selfScore < playerScore) score += 15;
if (selfScore > playerScore) score -= 15;
// 勇敢的
if (maxScorePlayer != Id)
{
player.DiplomacyData.GetCountryDiplomacyInfo(maxScorePlayer, out var playerToMaxScorePlayer);
if (playerToMaxScorePlayer.DiplomacyState == DiplomacyState.War) score += 15;
}
// 威胁的
var playerUnit = new HashSet<UnitData>();
map.GetUnitDataListByPlayerId(player.Id, playerUnit);
var selfTerritory = map.GetPlayerTerritoryGridIdSet(Id);
foreach (var gridId in selfTerritory)
{
if (!map.GridMap.GetGridDataByGid(gridId, out var gridData)) continue;
foreach (var unit in playerUnit)
{
if (!map.GetGridDataByUnitId(unit.Id, out var unitGrid)) continue;
var dis = map.GridMap.CalcDistance(unitGrid, gridData);
if (dis > 1) continue;
score -= 15;
return;
}
}
// 侵略的
var playerTerritory = map.GetPlayerTerritoryGridIdSet(player.Id);
foreach (var playerGridId in playerTerritory)
{
if (!map.GridMap.GetGridDataByGid(playerGridId, out var playerGrid)) continue;
foreach (var selfGridId in selfTerritory)
{
if (!map.GridMap.GetGridDataByGid(selfGridId, out var selfGrid)) continue;
var dis = map.GridMap.CalcDistance(playerGrid, selfGrid);
if (dis > 1) continue;
score -= 15;
return;
}
}
// 主宰的
if (maxScorePlayer == player.Id)
{
var count = 0;
var playerCity = new HashSet<CityData>();
map.GetCityDataListByPlayerId(player.Id, playerCity);
foreach (var city in playerCity) if (city.IsCradle) count++;
if (count > 0) count--;
score -= 15 + count * 5;
}
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;
}
}
// 刷新对他国的外交关系
public void RefreshDiplomacyState(MapData map)
{
foreach (var player in map.PlayerMap.PlayerDataList)
{
if (player.Id == Id) continue;
DiplomacyData.GetCountryDiplomacyInfo(player.Id, out var selfToPlayer);
player.DiplomacyData.GetCountryDiplomacyInfo(Id, out var playerToSelf);
// 如果没有外交关系,且没有见过面,则设置为无外交关系
if (!MeetPlayers.Contains(player.Id)) selfToPlayer.DiplomacyState = DiplomacyState.NoDiplomacy;
// 如果没有外交关系,且见过面,则设置为中立
else if (selfToPlayer.DiplomacyState == DiplomacyState.NoDiplomacy)
selfToPlayer.DiplomacyState = DiplomacyState.Neutral;
// 如果上一回合回合内双方未发生战斗,则该回合开始时变为中立关系
if (selfToPlayer.DiplomacyState == DiplomacyState.War && !LastAttackPlayers.Contains(player.Id))
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;
}
}
}
}
@ -640,4 +846,125 @@ namespace RuntimeData
}
}
// 主动攻击我的敌人信息
[Serializable]
public class DiplomacyData : ISerializationCallbackReceiver
{
public List<CountryDiplomacyInfo> Info;
private Dictionary<uint, CountryDiplomacyInfo> _infoDict;
public DiplomacyData()
{
Info = new List<CountryDiplomacyInfo>();
}
public DiplomacyData(DiplomacyData copyData)
{
Info = new List<CountryDiplomacyInfo>();
}
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 turn, uint playerId)
{
GetCountryDiplomacyInfo(playerId, out var info);
if (info == null) return false;
return info.AttackTurn == 0 || info.AttackTurn < turn;
}
public void AddTurnAttacker(uint playerId, uint turn)
{
GetCountryDiplomacyInfo(playerId, out var info);
if (info == null) return;
info.AttackTurn = turn;
}
public void GetCountryDiplomacyInfo(uint playerId, out CountryDiplomacyInfo info)
{
Refresh();
if (!_infoDict.TryGetValue(playerId, out info))
{
info = new CountryDiplomacyInfo { PlayerId = playerId };
Info.Add(info);
_infoDict[playerId] = 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 void OnBeforeSerialize()
{
}
public void OnAfterDeserialize()
{
}
}
// 对他国的数据
[Serializable]
public class CountryDiplomacyInfo
{
public uint PlayerId;
// 我对国家 PlayerId 的外交关系
public DiplomacyState DiplomacyState;
// 我对国家 PlayerId 的好感值
public float FeelingValue;
// 我对国家 PlayerId 的好感态度
public FeelingState FeelingState;
// 国家 PlayerId 攻击我的回合记录
public uint AttackTurn;
// 国家 PlayerId 是否在我国建立了大使馆
public bool IsEmbassy;
// 联盟破裂回合结束标记
public bool IsLeagueRupture;
public CountryDiplomacyInfo()
{
AttackTurn = 0;
IsEmbassy = false;
IsLeagueRupture = false;
}
public CountryDiplomacyInfo(CountryDiplomacyInfo copyData)
{
PlayerId = copyData.PlayerId;
AttackTurn = copyData.AttackTurn;
DiplomacyState = copyData.DiplomacyState;
FeelingValue = copyData.FeelingValue;
FeelingState = copyData.FeelingState;
IsEmbassy = copyData.IsEmbassy;
IsLeagueRupture = copyData.IsLeagueRupture;
}
public void DeepCopy(CountryDiplomacyInfo copyData)
{
PlayerId = copyData.PlayerId;
AttackTurn = copyData.AttackTurn;
DiplomacyState = copyData.DiplomacyState;
FeelingValue = copyData.FeelingValue;
FeelingState = copyData.FeelingState;
IsEmbassy = copyData.IsEmbassy;
IsLeagueRupture = copyData.IsLeagueRupture;
}
}
}

View File

@ -2490,7 +2490,7 @@ namespace Logic.Action
}
}
// 小兵攻击,目前只用于 AI 的行为
public class UnitAttackAction : ActionLogicBase
{

View File

@ -870,7 +870,14 @@ namespace Logic
}
}
// 主动建立同盟
public void SetDiplomacyLeague( PlayerData originPlayer, PlayerData targetPlayer, DiplomacyState state)
{
originPlayer.DiplomacyData.GetCountryDiplomacyInfo(targetPlayer.Id, out var player1ToPlayer2);
targetPlayer.DiplomacyData.GetCountryDiplomacyInfo(originPlayer.Id, out var player2ToPlayer1);
player1ToPlayer2.DiplomacyState = state;
player2ToPlayer1.DiplomacyState = state;
}
}
}

View File

@ -209,8 +209,10 @@ namespace Logic
if (!mapData.GetCityDataByUnitId(unit2.Id, out var city2)) return false;
if (!mapData.GetGridDataByUnitId(unit1.Id, out var grid1)) return false;
if (!mapData.GetGridDataByUnitId(unit2.Id, out var grid2)) return false;
Main.PlayerLogic.SetDiplomacyLeague(player1, player2, DiplomacyState.War);
player1.CurAttackPlayers.Add(player2.Id);
player2.CurAttackPlayers.Add(player1.Id);
player2.AddAttacker(player1.Id);
player1.TurnNoAttack = 0;
// 计算攻击伤害