This commit is contained in:
daixiawu 2025-07-15 00:15:58 +08:00
commit 3527b874ef
19 changed files with 723 additions and 309 deletions

View File

@ -27,7 +27,7 @@ MonoBehaviour:
_version: 3.33 _version: 3.33
_category: _category:
_comments: _comments:
_translation: {x: 71, y: 178} _translation: {x: -189, y: -71}
_zoomFactor: 1 _zoomFactor: 1
_haltSerialization: 0 _haltSerialization: 0
_externalSerializationFile: {fileID: 0} _externalSerializationFile: {fileID: 0}

View File

@ -18,7 +18,7 @@ MonoBehaviour:
_version: 3.33 _version: 3.33
_category: _category:
_comments: _comments:
_translation: {x: 109, y: -993} _translation: {x: 1016, y: 32}
_zoomFactor: 0.8057433 _zoomFactor: 0.25006658
_haltSerialization: 0 _haltSerialization: 0
_externalSerializationFile: {fileID: 0} _externalSerializationFile: {fileID: 0}

File diff suppressed because one or more lines are too long

View File

@ -90,8 +90,8 @@ namespace NodeCanvas.Tasks.Actions
if (unitGrid.Id == grid.Id) continue; if (unitGrid.Id == grid.Id) continue;
if (!param.MapData.GetUnitDataByGid(grid.Id, out var unit)) continue; if (!param.MapData.GetUnitDataByGid(grid.Id, out var unit)) continue;
var dis = param.MapData.GridMap.CalcDistance(grid, target); var dis = param.MapData.GridMap.CalcDistance(grid, target);
if (selfUnits.Contains(unit)) selfScore += unit.GetCost() / (dis + 1f); if (selfUnits.Contains(unit)) selfScore += unit.GetMilitary() / (dis + 1f);
else enemyScore += unit.GetCost() / (dis + 1f); else enemyScore += unit.GetMilitary() / (dis + 1f);
} }
var targetScore = (selfScore - enemyScore) / path.length; var targetScore = (selfScore - enemyScore) / path.length;

View File

@ -0,0 +1,67 @@
/*
* @Author:
* @Description:
* @Date: 20250606 19:06:16
* @Modify:
*/
using System;
using Logic;
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using UnityEngine;
namespace NodeCanvas.Tasks.Actions
{
[Category("AIAction")]
[Serializable]
public class AIParamStandingTrainUnit : ActionTask
{
protected override string info => string.Format($"常备军训练单位");
protected override void OnExecute()
{
// 直接从Blackboard获取AICalculatorData
var data = blackboard.GetVariable<AICalculatorData>("Data");
if (data?.value?.TargetParam.CityData == null || data.value.AIActions.Count == 0)
{
EndAction(false);
return;
}
var city = data.value.TargetParam.CityData;
var count = data.value.TargetParam.MapData.GetUnitCount(city.Id);
if (count >= Mathf.Min(5, city.Level * 0.6f))
{
EndAction(false);
return;
}
var targetUnitType = UnitType.None;
// 如果不能造剑士
bool canSwordsman = false;
foreach (var aiAction in data.value.AIActions)
{
if (aiAction.ActionLogic.ActionId.UnitType == UnitType.Swordsman) canSwordsman = true;
}
if (!canSwordsman) targetUnitType = UnitType.Warrior;
// 如果回合金 < 15
if (targetUnitType == UnitType.None && Main.PlayerLogic.GetPlayerStarsPerTurn(data.value.Map, data.value.Player.Id) < 15)
{
targetUnitType = UnitType.Warrior;
}
// 否则造剑士
if (targetUnitType == UnitType.None) targetUnitType = UnitType.Swordsman;
for (int i = data.value.AIActions.Count - 1; i >= 0; i--)
{
if (data.value.AIActions[i].ActionLogic.ActionId.UnitType == targetUnitType) continue;
data.value.AIActions.RemoveAt(i);
}
EndAction(data.value.AIActions.Count > 0);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 61a78b1e94b24f748619cf5b94d8fe93
timeCreated: 1752491476

View File

@ -944,6 +944,11 @@ namespace RuntimeData
public GameRecord ExportGameRecord() public GameRecord ExportGameRecord()
{ {
var gameRecord = new GameRecord(); var gameRecord = new GameRecord();
gameRecord.Mode = MapConfig.GameMode;
gameRecord.AIDiff = MapConfig.AIDiff;
gameRecord.Turn = PlayerMap.SelfPlayerData.Turn;
gameRecord.PlayerCivId = PlayerMap.SelfPlayerData.PlayerCivId;
gameRecord.PlayerForceId = PlayerMap.SelfPlayerData.PlayerForceId;
gameRecord.Score = PlayerMap.SelfPlayerData.PlayerScore; gameRecord.Score = PlayerMap.SelfPlayerData.PlayerScore;
DateTime now = DateTime.Now; DateTime now = DateTime.Now;
gameRecord.Time = now.ToString("yyyy.MM.dd HH:mm"); gameRecord.Time = now.ToString("yyyy.MM.dd HH:mm");
@ -953,7 +958,6 @@ namespace RuntimeData
gameRecord.MapWidth = MapConfig.Width; gameRecord.MapWidth = MapConfig.Width;
gameRecord.MapHeight = MapConfig.Height; gameRecord.MapHeight = MapConfig.Height;
gameRecord.PlayerCount = MapConfig.PlayerCount - 1; gameRecord.PlayerCount = MapConfig.PlayerCount - 1;
gameRecord.AIDiff = MapConfig.AIDiff;
return gameRecord; return gameRecord;
} }

View File

@ -8,7 +8,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Logic.Action; using Logic.Action;
using Logic.AI;
using NUnit.Framework;
using Unity.VisualScripting; using Unity.VisualScripting;
using UnityEngine; using UnityEngine;
@ -28,6 +31,7 @@ namespace RuntimeData
EYE EYE
} }
// 奇观状态枚举 // 奇观状态枚举
public enum WonderState 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] [Serializable]
public class PlayerMapData : ISerializationCallbackReceiver public class PlayerMapData : ISerializationCallbackReceiver
{ {
@ -229,6 +255,7 @@ namespace RuntimeData
public List<uint> MeetPlayers; public List<uint> MeetPlayers;
public List<uint> CurAttackPlayers; public List<uint> CurAttackPlayers;
public List<uint> LastAttackPlayers; public List<uint> LastAttackPlayers;
public DiplomacyData DiplomacyData;
//用于记录是否死亡是否要centMessage来宣告死亡信息 //用于记录是否死亡是否要centMessage来宣告死亡信息
public bool DieMark = false; public bool DieMark = false;
@ -239,6 +266,7 @@ namespace RuntimeData
Sight = new MapSightData(); Sight = new MapSightData();
TechTree = new TechTreeData(); TechTree = new TechTreeData();
Wonder = new WonderData(); Wonder = new WonderData();
DiplomacyData = new DiplomacyData();
MeetPlayers = new List<uint>(); MeetPlayers = new List<uint>();
CurAttackPlayers = new List<uint>(); CurAttackPlayers = new List<uint>();
LastAttackPlayers = new List<uint>(); LastAttackPlayers = new List<uint>();
@ -257,6 +285,7 @@ namespace RuntimeData
Sight = new MapSightData(); Sight = new MapSightData();
Wonder = new WonderData(); Wonder = new WonderData();
DiplomacyData = new DiplomacyData();
CurAttackPlayers = new List<uint>(); CurAttackPlayers = new List<uint>();
LastAttackPlayers = new List<uint>(); LastAttackPlayers = new List<uint>();
MeetPlayers = new List<uint>(); MeetPlayers = new List<uint>();
@ -277,6 +306,7 @@ namespace RuntimeData
Sight = new MapSightData(copyData.Sight); Sight = new MapSightData(copyData.Sight);
TechTree = new TechTreeData(copyData.TechTree); TechTree = new TechTreeData(copyData.TechTree);
Wonder = new WonderData(copyData.Wonder); Wonder = new WonderData(copyData.Wonder);
DiplomacyData = new DiplomacyData(copyData.DiplomacyData);
CurAttackPlayers = new List<uint>(); CurAttackPlayers = new List<uint>();
foreach (var id in copyData.CurAttackPlayers) CurAttackPlayers.Add(id); foreach (var id in copyData.CurAttackPlayers) CurAttackPlayers.Add(id);
@ -305,6 +335,7 @@ namespace RuntimeData
Sight.DeepCopy(copyData.Sight); Sight.DeepCopy(copyData.Sight);
TechTree.DeepCopy(copyData.TechTree); TechTree.DeepCopy(copyData.TechTree);
Wonder.DeepCopy(copyData.Wonder); Wonder.DeepCopy(copyData.Wonder);
DiplomacyData.DeepCopy(copyData.DiplomacyData);
CurAttackPlayers.Clear(); CurAttackPlayers.Clear();
foreach (var id in copyData.CurAttackPlayers) CurAttackPlayers.Add(id); foreach (var id in copyData.CurAttackPlayers) CurAttackPlayers.Add(id);
@ -358,11 +389,186 @@ namespace RuntimeData
skill.OnTurnStart(this, map); skill.OnTurnStart(this, map);
} }
RefreshFeelingValue(map);
} }
public void OnTurnEnd(MapData map) public void OnTurnEnd(MapData map)
{ {
foreach (var skill in Skills)skill.OnTurnEnd(this, 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.GetMilitary();
if (ownerId == player.Id) playerScore += unit.GetMilitary();
}
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

@ -206,12 +206,20 @@ namespace RuntimeData
return (float)Health / GetMaxHealth(); return (float)Health / GetMaxHealth();
} }
// 获取军事分
public float GetMilitary()
{
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType, GiantType, out var info))
return 0;
if (GetHealthRatio() <= 0.5f) return info.Cost * 0.5f;
return info.Cost;
}
// 获取造价 // 获取造价
public float GetCost() public float GetCost()
{ {
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType, GiantType, out var info)) if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType, GiantType, out var info))
return 0; return 0;
if (GetHealthRatio() <= 0.5f) return info.Cost * 0.5f;
return info.Cost; return info.Cost;
} }

View File

@ -108,6 +108,7 @@ namespace Logic.AI
TechResource, TechResource,
CityTrainDefendAttack, CityTrainDefendAttack,
AroundGapScoreMax, AroundGapScoreMax,
CityTrainMilitary,
} }
@ -118,9 +119,7 @@ namespace Logic.AI
public MapData Map; public MapData Map;
public PlayerData Player; public PlayerData Player;
public Strategy CountryStrategy; public Strategy CountryStrategy;
public PlayerData AttackPlayer; public HashSet<PlayerData> AttackPlayerSet;
public CountryAttackType AttackType;
public PlayerData DefendTarget;
// 策略集合 // 策略集合
public Dictionary<CityData, Strategy> CityStrategy; public Dictionary<CityData, Strategy> CityStrategy;
@ -203,6 +202,7 @@ namespace Logic.AI
public AICalculatorData() public AICalculatorData()
{ {
AttackPlayerSet = new HashSet<PlayerData>();
CityStrategy = new Dictionary<CityData, Strategy>(); CityStrategy = new Dictionary<CityData, Strategy>();
FreeUnitStrategy = new Dictionary<UnitData, Strategy>(); FreeUnitStrategy = new Dictionary<UnitData, Strategy>();
LegionStrategy = new Dictionary<uint, Strategy>(); LegionStrategy = new Dictionary<uint, Strategy>();
@ -255,9 +255,7 @@ namespace Logic.AI
Map = map; Map = map;
Player = player; Player = player;
CountryStrategy = Strategy.None; CountryStrategy = Strategy.None;
AttackPlayer = null; AttackPlayerSet.Clear();
AttackType = CountryAttackType.None;
DefendTarget = null;
IsFinish = false; IsFinish = false;
AIActions.Clear(); AIActions.Clear();
@ -625,7 +623,7 @@ namespace Logic.AI
var score = 0f; var score = 0f;
foreach (var unit in kv.Value) foreach (var unit in kv.Value)
{ {
score += unit.GetCost(); score += unit.GetMilitary();
} }
LegionScore[kv.Key] = score; LegionScore[kv.Key] = score;
} }
@ -637,7 +635,7 @@ namespace Logic.AI
foreach (var unit in kv.Value) foreach (var unit in kv.Value)
{ {
if (!Map.GetGridDataByUnitId(unit.Id, out var grid)) continue; if (!Map.GetGridDataByUnitId(unit.Id, out var grid)) continue;
var ratio = unit.GetCost() / score; var ratio = unit.GetMilitary() / score;
vec += new Vector2(grid.Pos.X, grid.Pos.Y) * ratio; vec += new Vector2(grid.Pos.X, grid.Pos.Y) * ratio;
} }
var center = new Vector2Int((int)Math.Round(vec.X), (int)Math.Round(vec.Y)); var center = new Vector2Int((int)Math.Round(vec.X), (int)Math.Round(vec.Y));
@ -772,6 +770,9 @@ namespace Logic.AI
{ {
foreach (var cityId in CityBorder) foreach (var cityId in CityBorder)
{ {
if (!Map.GetPlayerIdByCityId(cityId, out var ownerId)) continue;
if(!Map.PlayerMap.GetPlayerDataByPlayerID(ownerId, out var owner)) continue;
if (!AttackPlayerSet.Contains(owner)) continue;
if (!LegionCanMoveCities[legionId].Contains(cityId)) continue; if (!LegionCanMoveCities[legionId].Contains(cityId)) continue;
if (!Map.GetGridDataByCityId(cityId, out var cityGrid)) continue; if (!Map.GetGridDataByCityId(cityId, out var cityGrid)) continue;
var dis = Map.GridMap.CalcDistance(cityGrid, LegionGrid[legionId]) + 1; var dis = Map.GridMap.CalcDistance(cityGrid, LegionGrid[legionId]) + 1;
@ -918,8 +919,8 @@ namespace Logic.AI
foreach (var aroundGrid in aroundGrids) foreach (var aroundGrid in aroundGrids)
{ {
if (!Map.GetUnitDataByGid(aroundGrid.Id, out var attacker)) continue; if (!Map.GetUnitDataByGid(aroundGrid.Id, out var attacker)) continue;
if (!selfUnit.Contains(attacker)) score += attacker.GetCost(); if (!selfUnit.Contains(attacker)) score += attacker.GetMilitary();
else score -= attacker.GetCost(); else score -= attacker.GetMilitary();
} }
if (score > 6f) if (score > 6f)
{ {
@ -1074,7 +1075,7 @@ namespace Logic.AI
var score = 0f; var score = 0f;
var unitList = new List<UnitData>(); var unitList = new List<UnitData>();
Map.GetUnitDataListByPlayerId(player.Id, unitList); Map.GetUnitDataListByPlayerId(player.Id, unitList);
foreach (var unit in unitList) score += unit.GetCost(); foreach (var unit in unitList) score += unit.GetMilitary();
return score; return score;
} }
@ -1143,140 +1144,92 @@ namespace Logic.AI
// 计算两个小兵的克制分 克制分表 克制表 // 计算两个小兵的克制分 克制分表 克制表
public float CalUnitCounterScore(UnitType self, UnitType target) public float CalUnitCounterScore(UnitType self, UnitType target)
{ {
if (self == UnitType.Warrior && target == UnitType.Warrior) return 0;
if (self == UnitType.Archer && target == UnitType.Warrior) return 1; if (self == UnitType.Archer && target == UnitType.Warrior) return 1;
if (self == UnitType.Defender && target == UnitType.Warrior) return -0.5f; if (self == UnitType.Swordsman && target == UnitType.Warrior) return 1f;
if (self == UnitType.Rider && target == UnitType.Warrior) return 0;
if (self == UnitType.Knights && target == UnitType.Warrior) return 3;
if (self == UnitType.Catapult && target == UnitType.Warrior) return 3;
if (self == UnitType.Swordsman && target == UnitType.Warrior) return 0.5f;
if (self == UnitType.Boat && target == UnitType.Warrior) return 0;
if (self == UnitType.RammerShip && target == UnitType.Warrior) return 0.5f;
if (self == UnitType.Ship && target == UnitType.Warrior) return 1f;
if (self == UnitType.BomberShip && target == UnitType.Warrior) return 1.5f;
if (self == UnitType.Warrior && target == UnitType.Archer) return 0.5f;
if (self == UnitType.Archer && target == UnitType.Archer) return 0;
if (self == UnitType.Defender && target == UnitType.Archer) return -0.5f;
if (self == UnitType.Rider && target == UnitType.Archer) return 1; if (self == UnitType.Rider && target == UnitType.Archer) return 1;
if (self == UnitType.Knights && target == UnitType.Archer) return 3; if (self == UnitType.Knights && target == UnitType.Archer) return 3;
if (self == UnitType.Catapult && target == UnitType.Archer) return 3;
if (self == UnitType.Swordsman && target == UnitType.Archer) return 3;
if (self == UnitType.Boat && target == UnitType.Archer) return 0;
if (self == UnitType.RammerShip && target == UnitType.Archer) return 3f;
if (self == UnitType.Ship && target == UnitType.Archer) return 0f;
if (self == UnitType.BomberShip && target == UnitType.Archer) return 3f;
if (self == UnitType.Warrior && target == UnitType.Defender) return -1f; if (self == UnitType.Archer && target == UnitType.Defender) return 1f;
if (self == UnitType.Archer && target == UnitType.Defender) return 0.5f;
if (self == UnitType.Defender && target == UnitType.Defender) return -2f;
if (self == UnitType.Rider && target == UnitType.Defender) return -1.5f;
if (self == UnitType.Knights && target == UnitType.Defender) return 0;
if (self == UnitType.Catapult && target == UnitType.Defender) return 2; if (self == UnitType.Catapult && target == UnitType.Defender) return 2;
if (self == UnitType.Swordsman && target == UnitType.Defender) return 0; if (self == UnitType.Swordsman && target == UnitType.Defender) return 1;
if (self == UnitType.Boat && target == UnitType.Defender) return 0; if (self == UnitType.RammerShip && target == UnitType.Defender) return 1;
if (self == UnitType.RammerShip && target == UnitType.Defender) return 0; if (self == UnitType.Ship && target == UnitType.Defender) return 1f;
if (self == UnitType.Ship && target == UnitType.Defender) return 0.5f; if (self == UnitType.BomberShip && target == UnitType.Defender) return 2f;
if (self == UnitType.BomberShip && target == UnitType.Defender) return 1.5f;
if (self == UnitType.Warrior && target == UnitType.Rider) return 0.5f; if (self == UnitType.Archer && target == UnitType.Rider) return 1f;
if (self == UnitType.Archer && target == UnitType.Rider) return 1.5f;
if (self == UnitType.Defender && target == UnitType.Rider) return 0;
if (self == UnitType.Rider && target == UnitType.Rider) return 0.5f;
if (self == UnitType.Knights && target == UnitType.Rider) return 3;
if (self == UnitType.Catapult && target == UnitType.Rider) return 3;
if (self == UnitType.Swordsman && target == UnitType.Rider) return 3;
if (self == UnitType.Boat && target == UnitType.Rider) return 0;
if (self == UnitType.RammerShip && target == UnitType.Rider) return 3f;
if (self == UnitType.Ship && target == UnitType.Rider) return 1.5f;
if (self == UnitType.BomberShip && target == UnitType.Rider) return 3f;
if (self == UnitType.Warrior && target == UnitType.Knights) return 0.5f; if (self == UnitType.Rider && target == UnitType.Catapult) return 1;
if (self == UnitType.Archer && target == UnitType.Knights) return 1.5f;
if (self == UnitType.Defender && target == UnitType.Knights) return 0f;
if (self == UnitType.Rider && target == UnitType.Knights) return 0.5f;
if (self == UnitType.Knights && target == UnitType.Knights) return 3f;
if (self == UnitType.Catapult && target == UnitType.Knights) return 3f;
if (self == UnitType.Swordsman && target == UnitType.Knights) return 3f;
if (self == UnitType.Boat && target == UnitType.Knights) return 0f;
if (self == UnitType.RammerShip && target == UnitType.Knights) return 3f;
if (self == UnitType.Ship && target == UnitType.Knights) return 1.5f;
if (self == UnitType.BomberShip && target == UnitType.Knights) return 3f;
if (self == UnitType.Warrior && target == UnitType.Catapult) return 2;
if (self == UnitType.Archer && target == UnitType.Catapult) return 2;
if (self == UnitType.Defender && target == UnitType.Catapult) return 1;
if (self == UnitType.Rider && target == UnitType.Catapult) return 2;
if (self == UnitType.Knights && target == UnitType.Catapult) return 3; if (self == UnitType.Knights && target == UnitType.Catapult) return 3;
if (self == UnitType.Catapult && target == UnitType.Catapult) return 3;
if (self == UnitType.Swordsman && target == UnitType.Catapult) return 3;
if (self == UnitType.Boat && target == UnitType.Catapult) return 0;
if (self == UnitType.RammerShip && target == UnitType.Catapult) return 3;
if (self == UnitType.Ship && target == UnitType.Catapult) return 2f;
if (self == UnitType.BomberShip && target == UnitType.Catapult) return 3f;
if (self == UnitType.Warrior && target == UnitType.Swordsman) return -1; if (self == UnitType.Catapult && target == UnitType.RammerShip) return 1;
if (self == UnitType.Archer && target == UnitType.Swordsman) return 0.5f; if (self == UnitType.Ship && target == UnitType.RammerShip) return 1f;
if (self == UnitType.Defender && target == UnitType.Swordsman) return -2;
if (self == UnitType.Rider && target == UnitType.Swordsman) return -2;
if (self == UnitType.Knights && target == UnitType.Swordsman) return 1;
if (self == UnitType.Catapult && target == UnitType.Swordsman) return 2;
if (self == UnitType.Swordsman && target == UnitType.Swordsman) return 0;
if (self == UnitType.Boat && target == UnitType.Swordsman) return 0;
if (self == UnitType.RammerShip && target == UnitType.Swordsman) return 0;
if (self == UnitType.Ship && target == UnitType.Swordsman) return 0.5f;
if (self == UnitType.BomberShip && target == UnitType.Swordsman) return 1.5f;
if (self == UnitType.Warrior && target == UnitType.Boat) return 1.5f; if (self == UnitType.Catapult && target == UnitType.Ship) return 1;
if (self == UnitType.Archer && target == UnitType.Boat) return 1.5f; if (self == UnitType.RammerShip && target == UnitType.Ship) return 1;
if (self == UnitType.Defender && target == UnitType.Boat) return 0.5f;
if (self == UnitType.Rider && target == UnitType.Boat) return 1.5f;
if (self == UnitType.Knights && target == UnitType.Boat) return 3;
if (self == UnitType.Catapult && target == UnitType.Boat) return 3;
if (self == UnitType.Swordsman && target == UnitType.Boat) return 3;
if (self == UnitType.Boat && target == UnitType.Boat) return 0;
if (self == UnitType.RammerShip && target == UnitType.Boat) return 3;
if (self == UnitType.Ship && target == UnitType.Boat) return 1.5f;
if (self == UnitType.BomberShip && target == UnitType.Boat) return 3f;
if (self == UnitType.Warrior && target == UnitType.RammerShip) return -1; if (self == UnitType.Catapult && target == UnitType.BomberShip) return 1;
if (self == UnitType.Archer && target == UnitType.RammerShip) return 0.5f;
if (self == UnitType.Defender && target == UnitType.RammerShip) return -2f;
if (self == UnitType.Rider && target == UnitType.RammerShip) return -2f;
if (self == UnitType.Knights && target == UnitType.RammerShip) return 1;
if (self == UnitType.Catapult && target == UnitType.RammerShip) return 3;
if (self == UnitType.Swordsman && target == UnitType.RammerShip) return 0;
if (self == UnitType.Boat && target == UnitType.RammerShip) return 0;
if (self == UnitType.RammerShip && target == UnitType.RammerShip) return 0;
if (self == UnitType.Ship && target == UnitType.RammerShip) return 0.5f;
if (self == UnitType.BomberShip && target == UnitType.RammerShip) return 1.5f;
if (self == UnitType.Warrior && target == UnitType.Ship) return 0.5f; if (self == UnitType.Catapult && (target == UnitType.Giant || target == UnitType.BigGuy)) return 2;
if (self == UnitType.Archer && target == UnitType.Ship) return 0; if (self == UnitType.Swordsman && (target == UnitType.Giant || target == UnitType.BigGuy)) return 1;
if (self == UnitType.Defender && target == UnitType.Ship) return -0.5f; if (self == UnitType.BomberShip && (target == UnitType.Giant || target == UnitType.BigGuy)) return 2;
if (self == UnitType.Rider && target == UnitType.Ship) return 0;
if (self == UnitType.Knights && target == UnitType.Ship) return 3;
if (self == UnitType.Catapult && target == UnitType.Ship) return 2;
if (self == UnitType.Swordsman && target == UnitType.Ship) return 3;
if (self == UnitType.Boat && target == UnitType.Ship) return 0;
if (self == UnitType.RammerShip && target == UnitType.Ship) return 3;
if (self == UnitType.Ship && target == UnitType.Ship) return 0;
if (self == UnitType.BomberShip && target == UnitType.Ship) return 3;
if (self == UnitType.Warrior && target == UnitType.BomberShip) return 1;
if (self == UnitType.Archer && target == UnitType.BomberShip) return 1;
if (self == UnitType.Defender && target == UnitType.BomberShip) return 0.5f;
if (self == UnitType.Rider && target == UnitType.BomberShip) return 1;
if (self == UnitType.Knights && target == UnitType.BomberShip) return 3;
if (self == UnitType.Catapult && target == UnitType.BomberShip) return 3;
if (self == UnitType.Swordsman && target == UnitType.BomberShip) return 3;
if (self == UnitType.Boat && target == UnitType.BomberShip) return 0;
if (self == UnitType.RammerShip && target == UnitType.BomberShip) return 3;
if (self == UnitType.Ship && target == UnitType.BomberShip) return 1f;
if (self == UnitType.BomberShip && target == UnitType.BomberShip) return 1.5f;
return 0; return 0;
} }
// 计算两个小兵的军事差距分
public float CalUnitMilitaryGapScore(MapData map, UnitData self, UnitData target)
{
var score = self.GetMilitary() - target.GetMilitary();
score += CalUnitCounterScore(self, target);
if (map.GetGridDataByUnitId(self.Id, out var selfGrid) &&
map.GetGridDataByUnitId(target.Id, out var targetGrid))
{
var selfMag = self.GetSkill(SkillType.DASH, out _)
? self.GetAttackRange() + self.MP
: self.GetAttackRange();
var dis = map.GridMap.CalcDistance(selfGrid, targetGrid);
if (dis <= selfMag)
{
var dmg = Table.Instance.CalcDamage(map, self, target);
var attackScore = (float)dmg / target.GetMaxHealth() * target.GetCost();
score += Mathf.Min(5, attackScore);
}
var targetMag = target.GetSkill(SkillType.DASH, out _)
? target.GetAttackRange() + self.MP
: target.GetAttackRange();
if (dis <= targetMag)
{
var dmg = Table.Instance.CalcDamage(map, target, self);
var attackScore = (float)dmg / self.GetMaxHealth() * self.GetCost();
score -= Mathf.Min(self.GetMilitary(), attackScore);
}
}
if (self.GetAttackRange() == 1 && map.GetPlayerIdByUnitId(self.Id, out var selfPlayerId))
{
var selfUnits = new HashSet<UnitData>();
Map.GetUnitDataListByPlayerId(selfPlayerId, selfUnits);
var count = 0;
foreach (var unit in selfUnits) if (unit.GetAttackRange() == 1) count++;
if (count / (float)selfUnits.Count < 0.6f)
score += 6 - count / (float)selfUnits.Count * 10;
}
if (self.UnitType == UnitType.Catapult && map.GetGridDataByUnitId(self.Id, out var grid))
{
var gridList = Map.GridMap.GetAroundGridData(1, 1, grid);
bool isSea = true;
foreach (var aroundGrid in gridList)
{
if (aroundGrid.Terrain == TerrainType.Land) isSea = false;
}
if (isSea) score++;
}
return score;
}
// 计算科技对应的兵种 // 计算科技对应的兵种
public void GetUnitTypeByTech(TechType techType, out UnitType unitType, out GiantType giantType) public void GetUnitTypeByTech(TechType techType, out UnitType unitType, out GiantType giantType)
{ {
@ -1355,7 +1308,6 @@ namespace Logic.AI
if (ThreatScore[target.Id] < 10) continue; if (ThreatScore[target.Id] < 10) continue;
if (MilitaryGapScore[target.Id] > -5) continue; if (MilitaryGapScore[target.Id] > -5) continue;
CountryStrategy = Strategy.Defend; CountryStrategy = Strategy.Defend;
DefendTarget = target;
return; return;
} }
@ -1393,63 +1345,87 @@ namespace Logic.AI
} }
} }
int maxScore = 0;
PlayerData maxScorePlayer = null;
foreach (var player in Map.PlayerMap.PlayerDataList)
{
if (player.PlayerScore <= maxScore) continue;
maxScore = player.PlayerScore;
maxScorePlayer = player;
}
//然后判断是否做进攻的国家战略 //然后判断是否做进攻的国家战略
foreach (var target in Map.PlayerMap.PlayerDataList) if (maxScorePlayer != null && maxScorePlayer != Player && AttackDistance[maxScorePlayer.Id] <= 5)
{ {
if (target == Player) continue;
if (ThreatScore[target.Id] < 10) continue;
if (MilitaryGapScore[target.Id] < 3) continue;
if (AttackDistance[target.Id] > 7) continue;
CountryStrategy = Strategy.Attack; CountryStrategy = Strategy.Attack;
AttackPlayer = target; AttackPlayerSet.Add(maxScorePlayer);
AttackType = CountryAttackType.CounterattackInvasions;
return;
} }
foreach (var target in Map.PlayerMap.PlayerDataList)
{
if (target == Player) continue;
if (MilitaryGapScore[target.Id] < 0) continue;
if (AttackDistance[target.Id] > 7) continue;
CountryStrategy = Strategy.Attack;
AttackPlayer = target;
AttackType = CountryAttackType.BullyWeak;
return;
}
foreach (var target in Map.PlayerMap.PlayerDataList)
{
if (target == Player) continue;
if (MilitaryGapScore[target.Id] < 3) continue;
bool hasCityCenter = false; if (maxScorePlayer != null && maxScorePlayer != Map.PlayerMap.SelfPlayerData)
foreach (var city in selfCity) {
foreach (var target in Map.PlayerMap.PlayerDataList)
{ {
if (hasCityCenter) break; if (target == Player) continue;
if(!Map.GetGridDataByCityId(city.Id,out var cityGrid)) continue; if (ThreatScore[target.Id] < 10) continue;
var gridList = Map.GridMap.GetAroundGridData(8, 8, cityGrid); if (MilitaryGapScore[target.Id] < 3) continue;
foreach (var grid in gridList) if (AttackDistance[target.Id] > 7) continue;
{
if (grid.Resource != ResourceType.CityCenter) continue; CountryStrategy = Strategy.Attack;
if (Map.GetCityDataByGid(grid.Id, out var _)) continue; AttackPlayerSet.Add(target);
var path = PathFinder.FindPath((int)Map.MapConfig.Width, (int)Map.MapConfig.Height,
new (grid.Pos.X, grid.Pos.Y), new (cityGrid.Pos.X, cityGrid.Pos.Y), Map, Player);
if (!path.found) continue;
if (path.length > 8) continue;
hasCityCenter = true;
break;
}
} }
if (!hasCityCenter) continue; foreach (var target in Map.PlayerMap.PlayerDataList)
CountryStrategy = Strategy.Attack; {
AttackPlayer = target; if (target == Player) continue;
AttackType = CountryAttackType.ForcedExpansion; if (MilitaryGapScore[target.Id] < 0) continue;
return; if (AttackDistance[target.Id] > 7) continue;
CountryStrategy = Strategy.Attack;
AttackPlayerSet.Add(target);
}
foreach (var target in Map.PlayerMap.PlayerDataList)
{
if (target == Player) continue;
if (MilitaryGapScore[target.Id] < 3) continue;
bool hasCityCenter = false;
foreach (var city in selfCity)
{
if (hasCityCenter) break;
if (!Map.GetGridDataByCityId(city.Id, out var cityGrid)) continue;
var gridList = Map.GridMap.GetAroundGridData(8, 8, cityGrid);
foreach (var grid in gridList)
{
if (grid.Resource != ResourceType.CityCenter) continue;
if (Map.GetCityDataByGid(grid.Id, out var _)) continue;
var path = PathFinder.FindPath((int)Map.MapConfig.Width, (int)Map.MapConfig.Height,
new(grid.Pos.X, grid.Pos.Y), new(cityGrid.Pos.X, cityGrid.Pos.Y), Map, Player);
if (!path.found) continue;
if (path.length > 8) continue;
hasCityCenter = true;
break;
}
}
if (!hasCityCenter) continue;
CountryStrategy = Strategy.Attack;
AttackPlayerSet.Add(target);
}
} }
CountryStrategy = Strategy.Development; if (maxScorePlayer != null && maxScorePlayer == Map.PlayerMap.SelfPlayerData)
{
foreach (var target in Map.PlayerMap.PlayerDataList)
{
if (target == Player) continue;
if (MilitaryGapScore[target.Id] < 0) continue;
if (AttackDistance[target.Id] > 5) continue;
CountryStrategy = Strategy.Attack;
AttackPlayerSet.Add(target);
}
}
if (CountryStrategy == Strategy.None) CountryStrategy = Strategy.Development;
} }
// 计算城市周围单位 // 计算城市周围单位
@ -1476,7 +1452,7 @@ namespace Logic.AI
private float CalCityDefendScore(CityData city) private float CalCityDefendScore(CityData city)
{ {
var score = 0f; var score = 0f;
foreach (var unit in CityDefendUnits[city.Id]) score += unit.GetCost(); foreach (var unit in CityDefendUnits[city.Id]) score += unit.GetMilitary();
if (city.CityWall) score += 2; if (city.CityWall) score += 2;
return score; return score;
} }
@ -1493,7 +1469,7 @@ namespace Logic.AI
{ {
if (!Map.GetGridDataByUnitId(unit.Id, out var unitGrid)) continue; if (!Map.GetGridDataByUnitId(unit.Id, out var unitGrid)) continue;
var distance = Map.GridMap.CalcDistance(cityGrid, unitGrid); var distance = Map.GridMap.CalcDistance(cityGrid, unitGrid);
score += unit.GetCost() / (distance + 1); score += unit.GetMilitary() / (distance + 1);
} }
return score; return score;
@ -1503,7 +1479,7 @@ namespace Logic.AI
private float CalCityEnemyScore(CityData city) private float CalCityEnemyScore(CityData city)
{ {
var score = 0f; var score = 0f;
foreach (var unit in CityEnemyUnits[city.Id]) score += unit.GetCost(); foreach (var unit in CityEnemyUnits[city.Id]) score += unit.GetMilitary();
return score; return score;
} }
@ -1542,7 +1518,7 @@ namespace Logic.AI
3 => 0, 3 => 0,
_ => ratio _ => ratio
}; };
score += unit.GetCost() * ratio; score += unit.GetMilitary() * ratio;
} }
foreach (var unit in CityDefendUnits[city.Id]) foreach (var unit in CityDefendUnits[city.Id])
{ {
@ -1559,7 +1535,7 @@ namespace Logic.AI
3 => 0, 3 => 0,
_ => ratio _ => ratio
}; };
score -= unit.GetCost() * ratio; score -= unit.GetMilitary() * ratio;
} }
return score; return score;
} }
@ -1701,7 +1677,7 @@ namespace Logic.AI
for (int i = selfCity.Count - 1; i >= 0; i--) for (int i = selfCity.Count - 1; i >= 0; i--)
{ {
var city = selfCity[i]; var city = selfCity[i];
if (PlayerBorderDistance[city.Id] < 5) continue; if (PlayerBorderDistance[city.Id] > 5) continue;
if (CityDangerScore[city.Id] <= 0) continue; if (CityDangerScore[city.Id] <= 0) continue;
CityStrategy[city] = Strategy.Military; CityStrategy[city] = Strategy.Military;
selfCity.RemoveAt(i); selfCity.RemoveAt(i);

View File

@ -6,11 +6,13 @@
*/ */
using System;
using RuntimeData; using RuntimeData;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Logic.Action; using Logic.Action;
using UnityEngine; using UnityEngine;
using Random = UnityEngine.Random;
namespace Logic.AI namespace Logic.AI
@ -707,6 +709,7 @@ namespace Logic.AI
if (type == CalculateType.CityTrainDefend) CalculateCityTrainDefend(data, param, result); if (type == CalculateType.CityTrainDefend) CalculateCityTrainDefend(data, param, result);
if (type == CalculateType.CityTrainAttack) CalculateCityTrainAttack(data, param, result); if (type == CalculateType.CityTrainAttack) CalculateCityTrainAttack(data, param, result);
if (type == CalculateType.CityTrainMilitary) CalculateCityTrainMilitary(data, param, result);
if (type == CalculateType.CityDevelopment) CalculateCityDevelopment(data, param, result); if (type == CalculateType.CityDevelopment) CalculateCityDevelopment(data, param, result);
if (type == CalculateType.CityTrainDefendAttack) CalculateCityTrainDefendAttack(data, param, result); if (type == CalculateType.CityTrainDefendAttack) CalculateCityTrainDefendAttack(data, param, result);
@ -806,7 +809,7 @@ namespace Logic.AI
foreach (var unit in param.MapData.UnitMap.UnitList) foreach (var unit in param.MapData.UnitMap.UnitList)
{ {
if (selfUnits.Contains(unit)) continue; if (selfUnits.Contains(unit)) continue;
score += unit.GetCost(); score += unit.GetMilitary();
} }
result.Score[CalculateType.LegionDevelopmentKill] = 1f / (score + 1); result.Score[CalculateType.LegionDevelopmentKill] = 1f / (score + 1);
} }
@ -889,7 +892,7 @@ namespace Logic.AI
{ {
if (!param.MapData.GetUnitDataByGid(aroundGrid.Id, out var aroundUnit)) continue; if (!param.MapData.GetUnitDataByGid(aroundGrid.Id, out var aroundUnit)) continue;
if (!selfUnits.Contains(aroundUnit)) continue; if (!selfUnits.Contains(aroundUnit)) continue;
score += aroundUnit.GetCost(); score += aroundUnit.GetMilitary();
} }
result.Score[CalculateType.LegionDefendMove] = 1 / ((float)path.length + 1) * 1000 + score / 1000f; result.Score[CalculateType.LegionDefendMove] = 1 / ((float)path.length + 1) * 1000 + score / 1000f;
} }
@ -925,98 +928,70 @@ namespace Logic.AI
private static void CalculateCityTrainAttack(AICalculatorData data, CommonActionParams param, CalculateResult result) private static void CalculateCityTrainAttack(AICalculatorData data, CommonActionParams param, CalculateResult result)
{ {
if (data.AttackPlayer == null) return; if (!param.MapData.GetGridDataByCityId(param.CityData.Id, out var cityGrid)) return;
if (!param.MapData.GetUnitDataByGid(cityGrid.Id, out var newUnit)) return;
var selfUnits = new HashSet<UnitData>(); var selfUnits = new HashSet<UnitData>();
param.MapData.GetUnitDataListByPlayerId(param.PlayerData.Id, selfUnits); param.MapData.GetUnitDataListByPlayerId(param.PlayerData.Id, selfUnits);
if (!param.MapData.GetGridDataByCityId(param.CityData.Id, out var cityGrid)) return;
var units = new HashSet<UnitData>(); var score = 0f;
var targets = new HashSet<UnitData>();
var selfScore = 0f;
var targetScore = 0f;
foreach (var unit in param.MapData.UnitMap.UnitList) foreach (var unit in param.MapData.UnitMap.UnitList)
{ {
if (!selfUnits.Contains(unit)) continue; if (selfUnits.Contains(unit)) continue;
if (selfUnits.Contains(unit)) if (!param.MapData.GetPlayerIdByUnitId(unit.Id, out var ownerId)) continue;
{ if (!param.PlayerData.LastAttackPlayers.Contains(ownerId) &&
units.Add(unit); !param.PlayerData.CurAttackPlayers.Contains(ownerId)) continue;
selfScore += unit.GetCost(); score += data.CalUnitMilitaryGapScore(param.MapData, newUnit, unit);
}
else
{
targets.Add(unit);
targetScore += unit.GetCost();
}
} }
float score = selfScore - targetScore;
foreach (var selfUnit in units)
{
foreach (var target in targets)
{
score += data.CalUnitCounterScore(selfUnit, target) * 2;
score -= data.CalUnitCounterScore(target, selfUnit);
}
}
result.Score[CalculateType.CityTrainAttack] = score; result.Score[CalculateType.CityTrainAttack] = score;
} }
private static void CalculateCityTrainMilitary(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
if (!param.MapData.GetGridDataByCityId(param.CityData.Id, out var cityGrid)) return;
if (!param.MapData.GetUnitDataByGid(cityGrid.Id, out var newUnit)) return;
var selfUnits = new HashSet<UnitData>();
param.MapData.GetUnitDataListByPlayerId(param.PlayerData.Id, selfUnits);
var score = 0f;
foreach (var unit in param.MapData.UnitMap.UnitList)
{
if (selfUnits.Contains(unit)) continue;
if (!param.MapData.GetGridDataByUnitId(unit.Id, out var unitGrid)) continue;
var dis = param.MapData.GridMap.CalcDistance(cityGrid, unitGrid);
if (dis > 3) continue;
score += data.CalUnitMilitaryGapScore(param.MapData, newUnit, unit);
}
result.Score[CalculateType.CityTrainMilitary] = score;
}
private static void CalculateCityTrainDefendAttack(AICalculatorData data, CommonActionParams param, CalculateResult result) private static void CalculateCityTrainDefendAttack(AICalculatorData data, CommonActionParams param, CalculateResult result)
{ {
if (data.AttackPlayer == null) return; if (!param.MapData.GetGridDataByCityId(param.CityData.Id, out var cityGrid)) return;
if (!param.MapData.GetUnitDataByGid(cityGrid.Id, out var newUnit)) return;
var selfUnits = new HashSet<UnitData>(); var selfUnits = new HashSet<UnitData>();
param.MapData.GetUnitDataListByPlayerId(param.PlayerData.Id, selfUnits); param.MapData.GetUnitDataListByPlayerId(param.PlayerData.Id, selfUnits);
if (!param.MapData.GetGridDataByCityId(param.CityData.Id, out var cityGrid)) return;
var units = new HashSet<UnitData>(); var score = 0f;
var targets = new HashSet<UnitData>();
var selfScore = 0f;
var targetScore = 0f;
foreach (var unit in param.MapData.UnitMap.UnitList) foreach (var unit in param.MapData.UnitMap.UnitList)
{ {
if (!param.MapData.GetGridDataByUnitId(unit.Id, out var unitGrid)) continue; if (selfUnits.Contains(unit)) continue;
var dis = param.MapData.GridMap.CalcDistance(cityGrid, unitGrid); if (!param.MapData.GetPlayerIdByUnitId(unit.Id, out var ownerId)) continue;
if (dis > 6) continue; if (!param.PlayerData.LastAttackPlayers.Contains(ownerId) &&
if (selfUnits.Contains(unit)) !param.PlayerData.CurAttackPlayers.Contains(ownerId)) continue;
{ score += data.CalUnitMilitaryGapScore(param.MapData, newUnit, unit);
units.Add(unit);
selfScore += unit.GetCost();
}
else
{
targets.Add(unit);
targetScore += unit.GetCost();
}
} }
float score = selfScore - targetScore;
foreach (var selfUnit in units)
{
foreach (var target in targets)
{
score += data.CalUnitCounterScore(selfUnit, target) * 2;
score -= data.CalUnitCounterScore(target, selfUnit);
}
}
result.Score[CalculateType.CityTrainDefendAttack] = score; result.Score[CalculateType.CityTrainDefendAttack] = score;
} }
private static void CalculateCityTrainDefend(AICalculatorData data, CommonActionParams param, CalculateResult result) private static void CalculateCityTrainDefend(AICalculatorData data, CommonActionParams param, CalculateResult result)
{ {
float score = 0;
var selfUnits = new HashSet<UnitData>();
param.MapData.GetUnitDataListByPlayerId(param.PlayerData.Id, selfUnits);
if (!param.MapData.GetGridDataByCityId(param.CityData.Id, out var cityGrid)) return; if (!param.MapData.GetGridDataByCityId(param.CityData.Id, out var cityGrid)) return;
if (!param.MapData.GetUnitDataByGid(cityGrid.Id, out var newUnit)) return;
foreach (var unit in selfUnits) result.Score[CalculateType.CityTrainDefend] = newUnit.GetDefenseValue(param.MapData) + newUnit.Health;
{
if (!param.MapData.GetGridDataByUnitId(unit.Id, out var unitGrid)) continue;
if (param.MapData.GridMap.CalcDistance(cityGrid, unitGrid) > 3) continue;
score += unit.GetDefenseValue(param.MapData) + unit.Health;
}
result.Score[CalculateType.CityTrainDefend] = score;
} }
private static void CalculatePlayerTechDefend(AICalculatorData data, CommonActionParams param, CalculateResult result) private static void CalculatePlayerTechDefend(AICalculatorData data, CommonActionParams param, CalculateResult result)

View File

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

View File

@ -113,14 +113,16 @@ namespace Logic.Editor
{ {
if (_main?.MapData == null || _player == null) return; if (_main?.MapData == null || _player == null) return;
MainEditor.Instance.GetPlayerStrategy(_player.Id, out var playerStrategy, out var targetId, out var attackType); MainEditor.Instance.GetPlayerStrategy(_player.Id, out var playerStrategy, out var targetIdSet);
EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginHorizontal();
if (playerStrategy != Strategy.Attack) if (playerStrategy != Strategy.Attack)
InspectorUtils.InspectorTextWidthRich($"<b>玩家ID: {_player.Id}, 国家战略为: {playerStrategy}</b>"); InspectorUtils.InspectorTextWidthRich($"<b>玩家ID: {_player.Id}, 国家战略为: {playerStrategy}</b>");
else else
{ {
var idStr = "";
foreach (var id in targetIdSet) idStr += $"{id}";
InspectorUtils.InspectorTextWidthRich( InspectorUtils.InspectorTextWidthRich(
$"<b>玩家ID: {_player.Id}, 国家战略为: {playerStrategy}, 目标:{targetId}, 类型:{attackType}</b>"); $"<b>玩家ID: {_player.Id}, 国家战略为: {playerStrategy}, 目标:{idStr}</b>");
} }
EditorGUILayout.EndHorizontal(); EditorGUILayout.EndHorizontal();

View File

@ -70,12 +70,16 @@ namespace RuntimeData
[System.Serializable] [System.Serializable]
public class GameRecord public class GameRecord
{ {
public GameMode Mode;
public AIDifficult AIDiff;
public uint Turn;
public uint PlayerCivId;
public uint PlayerForceId;
public float Score; public float Score;
public string Time; public string Time;
public uint CityCount; public uint CityCount;
public uint MapWidth; public uint MapWidth;
public uint MapHeight; public uint MapHeight;
public uint PlayerCount; public uint PlayerCount;
public AIDifficult AIDiff;
} }
} }

View File

@ -17,8 +17,7 @@ namespace Logic
public bool IsGo = false; public bool IsGo = false;
public AICalculatorData Data; public AICalculatorData Data;
private Dictionary<uint, Strategy> _playerStrategy; private Dictionary<uint, Strategy> _playerStrategy;
private Dictionary<uint, CountryAttackType> _playerAttackType; private Dictionary<uint, HashSet<uint>> _playerAttackTarget;
private Dictionary<uint, uint> _playerAttackTarget;
private Dictionary<uint, Strategy> _cityStrategy; private Dictionary<uint, Strategy> _cityStrategy;
private Dictionary<uint, Strategy> _unitStrategy; private Dictionary<uint, Strategy> _unitStrategy;
private Dictionary<uint, Strategy> _legionStrategy; private Dictionary<uint, Strategy> _legionStrategy;
@ -33,8 +32,7 @@ namespace Logic
public MainEditor() public MainEditor()
{ {
_playerStrategy = new Dictionary<uint, Strategy>(); _playerStrategy = new Dictionary<uint, Strategy>();
_playerAttackType = new Dictionary<uint, CountryAttackType>(); _playerAttackTarget = new Dictionary<uint, HashSet<uint>>();
_playerAttackTarget = new Dictionary<uint, uint>();
_cityStrategy = new Dictionary<uint, Strategy>(); _cityStrategy = new Dictionary<uint, Strategy>();
_unitStrategy = new Dictionary<uint, Strategy>(); _unitStrategy = new Dictionary<uint, Strategy>();
_legionStrategy = new Dictionary<uint, Strategy>(); _legionStrategy = new Dictionary<uint, Strategy>();
@ -62,14 +60,11 @@ namespace Logic
return _actionRecord.GetValueOrDefault(cid); return _actionRecord.GetValueOrDefault(cid);
} }
public void GetPlayerStrategy(uint pid, out Strategy strategy, out uint target, out CountryAttackType attackType) public void GetPlayerStrategy(uint pid, out Strategy strategy, out HashSet<uint> target)
{ {
strategy = Strategy.None; strategy = Strategy.None;
target = 0;
attackType = CountryAttackType.None;
_playerStrategy.TryGetValue(pid, out strategy); _playerStrategy.TryGetValue(pid, out strategy);
_playerAttackTarget.TryGetValue(pid, out target); _playerAttackTarget.TryGetValue(pid, out target);
_playerAttackType.TryGetValue(pid, out attackType);
} }
public void GetUnitStrategy(uint uid, uint legion, uint playerId, out Strategy strategy, out uint cityId, out LegionStrategyType type) public void GetUnitStrategy(uint uid, uint legion, uint playerId, out Strategy strategy, out uint cityId, out LegionStrategyType type)
@ -113,8 +108,10 @@ namespace Logic
if (Data == null) return; if (Data == null) return;
if (Data.Player == null) return; if (Data.Player == null) return;
_playerStrategy[Data.Player.Id] = Data.CountryStrategy; _playerStrategy[Data.Player.Id] = Data.CountryStrategy;
_playerAttackType[Data.Player.Id] = Data.AttackType; if (!_playerAttackTarget.ContainsKey(Data.Player.Id))
if (Data.AttackPlayer != null) _playerAttackTarget[Data.Player.Id] = Data.AttackPlayer.Id; _playerAttackTarget[Data.Player.Id] = new HashSet<uint>();
_playerAttackTarget[Data.Player.Id].Clear();
foreach (var player in Data.AttackPlayerSet) _playerAttackTarget[Data.Player.Id].Add(player.Id);
foreach (var kv in Data.CityStrategy) _cityStrategy[kv.Key.Id] = kv.Value; foreach (var kv in Data.CityStrategy) _cityStrategy[kv.Key.Id] = kv.Value;
foreach (var kv in Data.FreeUnitStrategy) _unitStrategy[kv.Key.Id] = kv.Value; foreach (var kv in Data.FreeUnitStrategy) _unitStrategy[kv.Key.Id] = kv.Value;
foreach (var kv in Data.LegionStrategy) _legionStrategy[kv.Key * 10000 + Data.Player.Id] = kv.Value; foreach (var kv in Data.LegionStrategy) _legionStrategy[kv.Key * 10000 + Data.Player.Id] = kv.Value;

View File

@ -874,7 +874,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.GetCityDataByUnitId(unit2.Id, out var city2)) return false;
if (!mapData.GetGridDataByUnitId(unit1.Id, out var grid1)) return false; if (!mapData.GetGridDataByUnitId(unit1.Id, out var grid1)) return false;
if (!mapData.GetGridDataByUnitId(unit2.Id, out var grid2)) return false; if (!mapData.GetGridDataByUnitId(unit2.Id, out var grid2)) return false;
Main.PlayerLogic.SetDiplomacyLeague(player1, player2, DiplomacyState.War);
player1.CurAttackPlayers.Add(player2.Id); player1.CurAttackPlayers.Add(player2.Id);
player2.CurAttackPlayers.Add(player1.Id); player2.CurAttackPlayers.Add(player1.Id);
player2.AddAttacker(player1.Id);
player1.TurnNoAttack = 0; player1.TurnNoAttack = 0;
// 计算攻击伤害 // 计算攻击伤害

View File

@ -7,6 +7,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using Logic.CrashSight;
using RuntimeData; using RuntimeData;
using UnityEngine; using UnityEngine;
@ -19,7 +20,7 @@ namespace Logic.Audio
private AudioPlayer _musicPlayer; private AudioPlayer _musicPlayer;
private List<AudioPlayer> _allPlayer; private List<AudioPlayer> _allPlayer;
private Dictionary<string, AudioClip> _clips; private Dictionary<string, List<AudioClip>> _clips;
private GameObject AudioRoot; private GameObject AudioRoot;
public float MusicVolume public float MusicVolume
@ -42,45 +43,74 @@ namespace Logic.Audio
public void Init() public void Init()
{ {
_allPlayer = new List<AudioPlayer>(); _allPlayer = new List<AudioPlayer>();
_clips = new Dictionary<string, AudioClip>(); _clips = new Dictionary<string, List<AudioClip>>();
_clips["Main"] = Resources.Load<AudioClip>("Audio/Main"); var path = new Dictionary<string, string>();
_clips["RemiliaEgyptian"] = Resources.Load<AudioClip>("Audio/RemiliaEgyptian"); path["Main"] = "Audio/Main";
_clips["SatoriIndian"] = Resources.Load<AudioClip>("Audio/SatoriIndian"); path["RemiliaEgyptian"] = "Audio/RemiliaEgyptian";
_clips["KanakoGermany"] = Resources.Load<AudioClip>("Audio/KanakoGermany"); path["SatoriIndian"] = "Audio/SatoriIndian";
_clips["KaguyaFrench"] = Resources.Load<AudioClip>("Audio/KaguyaFrench"); path["KanakoGermany"] = "Audio/KanakoGermany";
_clips["SFX/UI_buttonHover"] = Resources.Load<AudioClip>("Audio/SFX/UI_buttonHover"); path["KaguyaFrench"] = "Audio/KaguyaFrench";
_clips["SFX/UI_buttonClick"] = Resources.Load<AudioClip>("Audio/SFX/UI_buttonClick"); path["SFX/UI_buttonHover"] = "Audio/SFX/UI_buttonHover";
_clips["SFX/UNIT_click"] = Resources.Load<AudioClip>("Audio/SFX/UNIT_click"); path["SFX/UI_buttonClick"] = "Audio/SFX/UI_buttonClick";
_clips["SFX/UNIT_bomb"] = Resources.Load<AudioClip>("Audio/SFX/UNIT_bomb"); path["SFX/UNIT_click"] = "Audio/SFX/UNIT_click";
_clips["SFX/UNIT_archer"] = Resources.Load<AudioClip>("Audio/SFX/UNIT_archer"); path["SFX/UNIT_bomb"] = "Audio/SFX/UNIT_bomb";
_clips["SFX/UNIT_hurt"] = Resources.Load<AudioClip>("Audio/SFX/UNIT_hurt"); path["SFX/UNIT_archer"] = "Audio/SFX/UNIT_archer";
_clips["SFX/UNIT_move"] = Resources.Load<AudioClip>("Audio/SFX/UNIT_move"); path["SFX/UNIT_hurt"] = "Audio/SFX/UNIT_hurt";
_clips["SFX/UNIT_attack"] = Resources.Load<AudioClip>("Audio/SFX/UNIT_attack"); path["SFX/UNIT_move"] = "Audio/SFX/UNIT_move";
path["SFX/UNIT_attack"] = "Audio/SFX/UNIT_attack";
_clips["SFX/ENV_sea"] = Resources.Load<AudioClip>("Audio/SFX/ENV_sea"); path["SFX/ENV_sea"] = "Audio/SFX/ENV_sea";
_clips["SFX/ENV_forest"] = Resources.Load<AudioClip>("Audio/SFX/ENV_forest"); path["SFX/ENV_forest"] = "Audio/SFX/ENV_forest";
foreach (var kv in path)
{
_clips[kv.Key] = new List<AudioClip>();
_clips[kv.Key].Add(Resources.Load<AudioClip>(kv.Value));
}
if (!AudioRoot) AudioRoot = new GameObject(); if (!AudioRoot) AudioRoot = new GameObject();
AudioRoot.name = "AudioRoot"; AudioRoot.name = "AudioRoot";
var cfg = Object.FindObjectOfType<AudioClipConfig>(); var cfg = Object.FindObjectOfType<AudioClipConfig>();
if (cfg != null) foreach (var clip in cfg.Clips) _clips[clip.name] = clip; if (cfg != null)
{
foreach (var clip in cfg.Clips)
{
_clips[clip.name] = new List<AudioClip>();
_clips[clip.name].Add(clip);
}
}
} }
public void Update() public void Update()
{ {
foreach (var player in _allPlayer) player.Update(); foreach (var player in _allPlayer)
{
player.Update();
if (player.State != PlayerState.Finished) continue;
if (!player.Clip) continue;
if (!_clips.ContainsKey(player.MusicName)) _clips[player.MusicName] = new List<AudioClip>();
_clips[player.MusicName].Add(player.Clip);
player.Clip = null;
}
} }
public void PlayMusic(string musicName, float fadeIn, float fadeOut, bool isLoop) public void PlayMusic(string musicName, float fadeIn, float fadeOut, bool isLoop)
{ {
if (!_clips.ContainsKey(musicName)) return; if (!_clips.ContainsKey(musicName) || _clips[musicName].Count == 0) return;
if (_musicPlayer != null) _musicPlayer.Stop(); if (_musicPlayer != null) _musicPlayer.Stop();
_musicPlayer = GetPlayer(); else _musicPlayer = GetPlayer();
_musicPlayer.Clip = _clips[musicName]; if (_clips[musicName].Count > 1)
{
_musicPlayer.Clip = _clips[musicName][1];
_clips[musicName].RemoveAt(1);
}
else _musicPlayer.Clip = Object.Instantiate(_clips[musicName][0]);
if (!_musicPlayer.Clip)
{
LogSystem.LogError($"音乐资源 {musicName} 未找到或加载失败!");
return;
}
_musicPlayer.MusicName = musicName;
_musicPlayer.IsLoop = isLoop; _musicPlayer.IsLoop = isLoop;
_musicPlayer.Length = _clips[musicName].length; _musicPlayer.Length = _musicPlayer.Clip.length;
_musicPlayer.FadeInDuration = fadeIn; _musicPlayer.FadeInDuration = fadeIn;
_musicPlayer.FadeOutDuration = fadeOut; _musicPlayer.FadeOutDuration = fadeOut;
_musicPlayer.VolumeRatio = MusicVolume; _musicPlayer.VolumeRatio = MusicVolume;
@ -96,9 +126,20 @@ namespace Logic.Audio
{ {
if (!_clips.ContainsKey(musicName)) return; if (!_clips.ContainsKey(musicName)) return;
var player = GetPlayer(); var player = GetPlayer();
player.Clip = _clips[musicName]; if (_clips[musicName].Count > 1)
{
player.Clip = _clips[musicName][1];
_clips[musicName].RemoveAt(1);
}
else player.Clip = Object.Instantiate(_clips[musicName][0]);
if (!player.Clip)
{
LogSystem.LogError($"音频资源 {musicName} 未找到或加载失败!");
return;
}
player.MusicName = musicName;
player.IsLoop = isLoop; player.IsLoop = isLoop;
player.Length = _clips[musicName].length; player.Length = _musicPlayer.Clip.length;
player.FadeInDuration = fadeIn; player.FadeInDuration = fadeIn;
player.FadeOutDuration = fadeOut; player.FadeOutDuration = fadeOut;
_musicPlayer.VolumeRatio = AudioVolume; _musicPlayer.VolumeRatio = AudioVolume;
@ -175,6 +216,7 @@ namespace Logic.Audio
public class AudioPlayer public class AudioPlayer
{ {
public string MusicName;
public AudioSource Source; public AudioSource Source;
public AudioClip Clip; public AudioClip Clip;
public bool IsLoop; public bool IsLoop;
@ -200,6 +242,7 @@ namespace Logic.Audio
public void Play() public void Play()
{ {
StartTime = Time.time; StartTime = Time.time;
Source.time = 0;
Source.clip = Clip; Source.clip = Clip;
Source.volume = 0; Source.volume = 0;
Source.loop = IsLoop; Source.loop = IsLoop;
@ -209,7 +252,7 @@ namespace Logic.Audio
public void Update() public void Update()
{ {
if (Source == null || !Source.isPlaying) return; if (Source == null) return;
UpdateSourceVolume(); UpdateSourceVolume();
} }

View File

@ -85,9 +85,8 @@ namespace TH1Renderer
if (MainEditor.Instance.Data != null) if (MainEditor.Instance.Data != null)
{ {
MainEditor.Instance.GetCityStrategy(_cityId, out var cst); MainEditor.Instance.GetCityStrategy(_cityId, out var cst);
MainEditor.Instance.GetPlayerStrategy(_playerData.Id, out var pst, out var target, out var type); MainEditor.Instance.GetPlayerStrategy(_playerData.Id, out var pst, out var target);
_debugText.text += $"CST={cst} PST={pst}"; _debugText.text += $"CST={cst} PST={pst}";
if (pst == Strategy.Attack) _debugText.text += $"TAR={target} Type={type}";
_debugText.text += "\n"; _debugText.text += "\n";
} }