TH1/My project/Assets/Scripts/Logic/AI/AIActionBase.cs
2025-06-24 18:16:05 +08:00

1311 lines
51 KiB
C#

/*
* @Author: 白哉
* @Description: AI 行为类
* @Date: 2025年04月01日 星期二 14:04:35
* @Modify:
*/
using System;
using System.Collections.Generic;
using System.Linq;
using Logic.Action;
using RuntimeData;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.SocialPlatforms.Impl;
using Vector2 = System.Numerics.Vector2;
namespace Logic.AI
{
public enum Strategy
{
Attack,
Defend,
Development,
Military,
EmergencyDefend,
Retreat,
Common,
None,
}
public enum CalculateType
{
PlayerTechDefend,
PlayerTechAttack,
PlayerTechScore,
CityLevelUpDefend,
CityTrainDefend,
CityTrainAttack,
CityDevelopment,
CityOk,
UnitCollect,
UnitUpgrade,
UnitRecovery,
UnitAttackCityCenter,
UnitExplore,
UnitRetreat,
UnitMoveToTargetGrid,
UnitAuto,
UnitAttack,
LegionDefendKill,
LegionDefendMove,
LegionDefendMoveForTrain,
LegionDefendAttack,
LegionAttackMoveInCity,
LegionAttackMoveToCity,
LegionAttackCityUnit,
LegionAttackUnit,
LegionDevelopmentMoveToCityTerritory,
LegionDevelopmentKill,
LegionDevelopmentMoveToCity,
LegionDevelopmentAttackUnit,
LegionDevelopmentMoveToOtherCity,
UnitAttackSelfCityEnemy,
UnitKill,
CityLevelUp,
TechGap,
TechResource,
}
public class AICalculatorData
{
// 核心数据
public MapData Map;
public PlayerData Player;
public Strategy CountryStrategy;
public PlayerData AttackPlayer;
// 策略集合
public Dictionary<CityData, Strategy> CityStrategy;
public Dictionary<UnitData, Strategy> FreeUnitStrategy;
public Dictionary<uint, Strategy> LegionStrategy;
// 玩家的军事分
public Dictionary<uint, int> MilitaryScore;
// 玩家的发展分
public Dictionary<uint, int> DevelopmentScore;
// 玩家对我的威胁分
public Dictionary<uint, int> ThreatScore;
// 我减去其他玩家的军事差距
public Dictionary<uint, int> MilitaryGapScore;
// 玩家对我的地缘距离
public Dictionary<uint, int> GeographicalDistance;
// 城市3格内我方单位
public Dictionary<uint, List<UnitData>> CityDefendUnits;
// 城市3格内敌方单位
public Dictionary<uint, List<UnitData>> CityEnemyUnits;
// 城市守备分
public Dictionary<uint, int> CityDefendScore;
// 城市救援分
public Dictionary<uint, int> CityRescueScore;
// 城市敌军分
public Dictionary<uint, int> CityEnemyScore;
// 城市危险分
public Dictionary<uint, int> CityDangerScore;
// 别国对我城市的最近边境距离
public Dictionary<uint, int> CityBorderDistance;
// 军团单位
public Dictionary<uint, List<UnitData>> LegionUnits;
// 自由人单位
public List<UnitData> FreeUnits;
// 军团分
public Dictionary<uint, int> LegionScore;
// 军团坐标
public Dictionary<uint, GridData> LegionGrid;
// 军团战力缺口
public Dictionary<uint, int> LegionGapScore;
// 军团不稳定值
public Dictionary<uint, float> LegionUnstableScore;
// 军团目标城市
public Dictionary<uint, uint> LegionTargetCity;
// 小兵目标格子
public Dictionary<uint, GridData> UnitTargetGrid;
// 小兵目标城市
public Dictionary<uint, uint> UnitTargetCity;
// 小兵攻击系数
public Dictionary<uint, int> UnitAttackRatio;
// 军团可达城市
public Dictionary<uint, HashSet<uint>> LegionCanMoveCities;
// 科技克制分缓存
public Dictionary<TechType, int> TechGapScore;
// 科技资源分缓存
public Dictionary<TechType, int> TechResourceScore;
public List<AIActionBase> AIActions;
public AIActionBase MaxAiAction;
public CommonActionParams TargetParam;
public HashSet<string> Marks;
// 迭代器
public List<UnitData> ForeachUnit;
public List<uint> ForeachLegion;
public List<CityData> ForeachCity;
public bool IsExcute;
public bool IsFinish;
public bool IsInSight;
public float Time;
public AICalculatorData()
{
CityStrategy = new Dictionary<CityData, Strategy>();
FreeUnitStrategy = new Dictionary<UnitData, Strategy>();
LegionStrategy = new Dictionary<uint, Strategy>();
AIActions = new List<AIActionBase>();
TargetParam = new CommonActionParams();
Marks = new HashSet<string>();
ForeachUnit = new List<UnitData>();
ForeachLegion = new List<uint>();
ForeachCity = new List<CityData>();
MilitaryScore = new Dictionary<uint, int>();
DevelopmentScore = new Dictionary<uint, int>();
ThreatScore = new Dictionary<uint, int>();
MilitaryGapScore = new Dictionary<uint, int>();
GeographicalDistance = new Dictionary<uint, int>();
CityDefendUnits = new Dictionary<uint, List<UnitData>>();
CityEnemyUnits = new Dictionary<uint, List<UnitData>>();
CityDefendScore = new Dictionary<uint, int>();
CityRescueScore = new Dictionary<uint, int>();
CityEnemyScore = new Dictionary<uint, int>();
CityDangerScore = new Dictionary<uint, int>();
CityBorderDistance = new Dictionary<uint, int>();
LegionUnits = new Dictionary<uint, List<UnitData>>();
FreeUnits = new List<UnitData>();
LegionScore = new Dictionary<uint, int>();
LegionGrid = new Dictionary<uint, GridData>();
LegionGapScore = new Dictionary<uint, int>();
LegionUnstableScore = new Dictionary<uint, float>();
LegionTargetCity = new Dictionary<uint, uint>();
UnitTargetGrid = new Dictionary<uint, GridData>();
UnitTargetCity = new Dictionary<uint, uint>();
UnitAttackRatio = new Dictionary<uint, int>();
LegionCanMoveCities = new Dictionary<uint, HashSet<uint>>();
TechGapScore = new Dictionary<TechType, int>();
TechResourceScore = new Dictionary<TechType, int>();
IsFinish = false;
IsInSight = false;
Time = 0.1f;
}
public void Refresh(MapData map, PlayerData player)
{
Map = map;
Player = player;
IsFinish = false;
IsInSight = false;
IsExcute = false;
Time = 0.1f;
AIActions.Clear();
TargetParam = new CommonActionParams();
Marks.Clear();
ForeachUnit.Clear();
ForeachLegion.Clear();
ForeachCity.Clear();
CityStrategy.Clear();
FreeUnitStrategy.Clear();
LegionStrategy.Clear();
MilitaryScore.Clear();
DevelopmentScore.Clear();
ThreatScore.Clear();
MilitaryGapScore.Clear();
GeographicalDistance.Clear();
CityDefendUnits.Clear();
CityEnemyUnits.Clear();
CityDefendScore.Clear();
CityRescueScore.Clear();
CityEnemyScore.Clear();
CityDangerScore.Clear();
CityBorderDistance.Clear();
LegionUnits.Clear();
FreeUnits.Clear();
LegionScore.Clear();
LegionGrid.Clear();
LegionGapScore.Clear();
LegionUnstableScore.Clear();
LegionTargetCity.Clear();
UnitTargetGrid.Clear();
UnitTargetCity.Clear();
UnitAttackRatio.Clear();
LegionCanMoveCities.Clear();
TechGapScore.Clear();
TechResourceScore.Clear();
// 国家策略
foreach (var playerData in map.PlayerMap.PlayerDataList)
{
MilitaryScore[playerData.Id] = CalMilitaryScore(playerData);
DevelopmentScore[playerData.Id] = playerData.PlayerScore;
}
foreach (var target in map.PlayerMap.PlayerDataList)
{
if (target == player) continue;
ThreatScore[target.Id] = CalThreatScore(target);
MilitaryGapScore[target.Id] = CalMilitaryGapScore(target);
GeographicalDistance[target.Id] = CalPlayerMinDistance(target);
}
CalculateCountryStrategy();
// 城市策略
var selfCity = new List<CityData>();
map.GetCityDataListByPlayerId(Player.Id, selfCity);
foreach (var city in Map.CityMap.CityList)
{
CalCityUnits(city);
CityDefendScore[city.Id] = CalCityDefendScore(city);
CityRescueScore[city.Id] = CalCityRescueScore(city);
CityEnemyScore[city.Id] = CalCityEnemyScore(city);
CityDangerScore[city.Id] = CalCityDangerScore(city);
CityBorderDistance[city.Id] = CalCityBorderDistance(city);
}
CalculateCityStrategy();
var count = 0;
while (true)
{
if (CalculateLegionStrategy()) if (CalculateFreeUnitStrategy()) break;
count++;
if (count > 20)
{
Debug.LogError($"死循环了");
break;
}
}
CalculateUnitAttackRatio();
CalculateTechGapScore();
CalculateTechResourceScore();
CalculateUnitTargetCity();
TargetParam.OnParamChanged();
}
public void ClearCache()
{
IsFinish = false;
IsInSight = false;
IsExcute = false;
Time = 0.1f;
AIActions.Clear();
MaxAiAction = null;
TargetParam = new CommonActionParams();
TargetParam.MapData = Map;
TargetParam.PlayerData = Player;
TargetParam.OnParamChanged();
}
// 小兵目标城市统计
public void CalculateUnitTargetCity()
{
var selfUnitList = new List<UnitData>();
Map.GetUnitDataListByPlayerId(Player.Id, selfUnitList);
foreach (var unit in selfUnitList)
{
if (unit.LegionId == 0) continue;
if (!LegionTargetCity.TryGetValue(unit.LegionId, out var value)) continue;
UnitTargetCity[unit.Id] = value;
}
}
// 科技资源分计算
public void CalculateTechResourceScore()
{
for (int i = (int)TechType.Climbing; i <= (int)TechType.EgyptSakuya; i++)
{
var techType = (TechType)i;
var techSet = Table.Instance.TechDataAssets.GetNextTechs(techType);
var score = 0f;
score += CalCountryTechResourceScore(techType);
foreach (var tech in techSet)
{
score += CalCountryTechResourceScore(tech) * 0.5f;
}
TechResourceScore[techType] = Mathf.RoundToInt(score);
}
}
// 计算科技对国家的资源分
public int CalCountryTechResourceScore(TechType techType)
{
var gridSet = Map.GetPlayerTerritoryGridIdSet(Player.Id);
int score = 0;
foreach(var gid in gridSet)
{
if (!Map.GridMap.GetGridDataByGid(gid, out var grid)) continue;
//计算每一种techActions在grid下的资源分
score += CalcGridTechResourceScore(techType, grid);
}
return score;
}
// 计算科技对一个格子的资源分。该函数主要用于计算科技对于一个国家的资源分
public int CalcGridTechResourceScore(TechType techType, GridData gridData)
{
int score = 0;
var techInfo = Table.Instance.TechDataAssets.GetTechInfo(techType);
foreach (var t in techInfo.techActions)
{
if (t.ActionType == CommonActionType.Gain)
{
if (!Table.Instance.GridAndResourceDataAssets.GetResourceInfo(t.ResourceType, out var resourceInfo))
continue;
score += resourceInfo.Exp;
}
if (t.ActionType == CommonActionType.Build)
{
if (!Table.Instance.GridAndResourceDataAssets.GetResourceInfo(t.ResourceType, out var resourceInfo))
continue;
score += t.ResourceType switch
{
ResourceType.LumberHut => gridData.Vegetation == Vegetation.Trees ? 1 : 0,
ResourceType.Farm => gridData.Resource == ResourceType.Crop ? 2 : 0,
ResourceType.Mine => gridData.Resource == ResourceType.Metal ? 2 : 0,
ResourceType.Sawmill => gridData.Vegetation == Vegetation.Trees ? 1 : 0,
ResourceType.Windmill => gridData.Resource == ResourceType.Crop || gridData.Resource == ResourceType.Farm ? 1 : 0,
ResourceType.Forge => gridData.Resource == ResourceType.Metal || gridData.Resource == ResourceType.Mine ? 2 : 0,
ResourceType.Port => gridData.Terrain == TerrainType.ShallowSea ? 1 : 0,
_ => 0
};
}
}
return score;
}
// 科技克制分计算
public void CalculateTechGapScore()
{
for (int i = (int)TechType.Climbing; i <= (int)TechType.EgyptSakuya; i++)
{
var techType = (TechType)i;
var techSet = Table.Instance.TechDataAssets.GetNextTechs(techType);
var score = 0f;
foreach (var id in Player.LastAttackPlayers)
{
score += CalCountryTechGapScore(techType, id);
}
foreach (var tech in techSet)
{
foreach (var id in Player.LastAttackPlayers)
{
score += CalCountryTechGapScore(tech, id) * 0.5f;
}
}
TechGapScore[techType] = Mathf.RoundToInt(score);
}
}
// 计算科技对某个国家的克制分
public int CalCountryTechGapScore(TechType techType, uint playerId)
{
GetUnitTypeByTech(techType, out var unitType, out var giantType);
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unitType, giantType, out var info))
return 0;
var unitList = new List<UnitData>();
Map.GetUnitDataListByPlayerId(playerId, unitList);
var score = 0;
foreach (var targetUnit in unitList)
{
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(targetUnit.UnitType, targetUnit.GiantType, out var targetInfo))
continue;
score += CalUnitCounterScore(info.UnitType, targetInfo.UnitType);
score -= CalUnitCounterScore(targetInfo.UnitType, info.UnitType);
}
return score;
}
// 计算军团战略
public bool CalculateLegionStrategy()
{
LegionStrategy.Clear();
LegionUnits.Clear();
FreeUnits.Clear();
LegionScore.Clear();
LegionGrid.Clear();
LegionGapScore.Clear();
LegionUnstableScore.Clear();
LegionTargetCity.Clear();
UnitTargetGrid.Clear();
UnitTargetCity.Clear();
UnitAttackRatio.Clear();
LegionCanMoveCities.Clear();
var selfUnitList = new List<UnitData>();
Map.GetUnitDataListByPlayerId(Player.Id, selfUnitList);
foreach (var unit in selfUnitList)
{
if (unit.LegionId == 0) FreeUnits.Add(unit);
else
{
if (!LegionUnits.ContainsKey(unit.LegionId)) LegionUnits[unit.LegionId] = new List<UnitData>();
LegionUnits[unit.LegionId].Add(unit);
}
}
foreach (var kv in LegionUnits)
{
var score = 0;
foreach (var unit in kv.Value)
{
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unit.UnitType, unit.GiantType, out var info))
continue;
score += info.Cost;
}
LegionScore[kv.Key] = score;
}
foreach (var kv in LegionUnits)
{
var score = LegionScore[kv.Key];
var vec = new Vector2();
foreach (var unit in kv.Value)
{
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unit.UnitType, unit.GiantType, out var info))
continue;
if (!Map.GetGridDataByUnitId(unit.Id, out var grid)) continue;
var ratio = info.Cost / (float)score;
vec += new Vector2(grid.Pos.X, grid.Pos.Y) * ratio;
}
var center = new Vector2Int((int)Math.Round(vec.X), (int)Math.Round(vec.Y));
if (!Map.GridMap.GetGridDataByPos(center.x, center.y, out var centerGrid)) continue;
LegionGrid[kv.Key] = centerGrid;
}
foreach (var kv in LegionUnits)
{
LegionCanMoveCities[kv.Key] = new HashSet<uint>();
foreach (var city in Map.CityMap.CityList)
{
if (!Map.GetGridDataByCityId(city.Id, out var cityGrid)) continue;
var path = PathFinder.FindPath((int)Map.MapConfig.Width, (int)Map.MapConfig.Height,
new (cityGrid.Pos.X, cityGrid.Pos.Y),
new (LegionGrid[kv.Key].Pos.X, LegionGrid[kv.Key].Pos.Y), Map, Player);
if (!path.found) continue;
LegionCanMoveCities[kv.Key].Add(city.Id);
}
}
foreach (var kv in LegionUnits)
{
var sunDis = 0;
foreach (var unit in kv.Value)
{
if (!Map.GetGridDataByUnitId(unit.Id, out var grid)) continue;
sunDis += Map.GridMap.CalcDistance(grid, LegionGrid[kv.Key]);
}
LegionUnstableScore[kv.Key] = sunDis * kv.Value.Count / 5f;
}
// 自主战略
var selfCity = new HashSet<CityData>();
Map.GetCityDataListByPlayerId(Player.Id, selfCity);
foreach (var kv in LegionUnits)
{
var citySet = new HashSet<CityData>();
var centerGrid = LegionGrid[kv.Key];
var aroundGrids = Map.GridMap.GetAroundGridData(3, 3, centerGrid);
foreach (var grid in aroundGrids)
{
if (!Map.GetCityDataByGid(grid.Id, out var cityData)) continue;
citySet.Add(cityData);
}
// 防守-城市协防
bool isFinish = false;
foreach (var cityPair in CityStrategy)
{
if (cityPair.Value != Strategy.EmergencyDefend) continue;
if (!LegionCanMoveCities[kv.Key].Contains(cityPair.Key.Id)) continue;
LegionTargetCity[kv.Key] = cityPair.Key.Id;
LegionStrategy[kv.Key] = Strategy.Defend;
isFinish = true;
break;
}
if (isFinish) continue;
// 进攻-城市进攻
foreach (var targetCity in citySet)
{
if (selfCity.Contains(targetCity)) continue;
if (!LegionCanMoveCities[kv.Key].Contains(targetCity.Id)) continue;
var score = LegionScore[kv.Key] - CityDefendScore[targetCity.Id];
foreach (var unit in kv.Value)
{
foreach (var targetUnit in CityDefendUnits[targetCity.Id])
{
score += CalUnitCounterScore(unit, targetUnit);
score -= CalUnitCounterScore(targetUnit, unit);
}
}
if (score >= 5)
{
LegionTargetCity[kv.Key] = targetCity.Id;
LegionStrategy[kv.Key] = Strategy.Attack;
break;
}
}
}
// 遵循国家战略
if (LegionTargetCity.Count == LegionUnits.Count) return true;
var waitLegions = new List<uint>();
foreach (var kv in LegionUnits)
{
if (LegionTargetCity.ContainsKey(kv.Key)) continue;
waitLegions.Add(kv.Key);
}
// 国家防守
var results = new List<MatchResult>();
if (CountryStrategy == Strategy.Defend)
{
foreach (var legionId in waitLegions)
{
foreach (var city in selfCity)
{
if (!LegionCanMoveCities[legionId].Contains(city.Id)) continue;
if (!Map.GetGridDataByCityId(city.Id, out var cityGrid)) continue;
var score = (LegionScore[legionId] - CityDangerScore[city.Id]) /
(Map.GridMap.CalcDistance(cityGrid, LegionGrid[legionId]) + 1);
results.Add(new MatchResult(legionId, city.Id, score));
}
}
results = results.OrderByDescending(r => r.Score).ToList();
foreach (var result in results)
{
if (LegionTargetCity.ContainsKey(result.LegionId)) continue;
if (LegionTargetCity.ContainsValue(result.CityId)) continue;
LegionTargetCity[result.LegionId] = result.CityId;
LegionStrategy[result.LegionId] = Strategy.Defend;
}
if (LegionTargetCity.Count == LegionUnits.Count) return true;
uint targetId = 0;
foreach (var city in selfCity)
{
if (targetId == 0 || CityDangerScore[city.Id] > CityDangerScore[targetId]) targetId = city.Id;
}
foreach (var legionId in waitLegions)
{
if (!LegionTargetCity.TryAdd(legionId, targetId)) continue;
LegionStrategy[legionId] = Strategy.Defend;
}
}
// 国家进攻
if (CountryStrategy == Strategy.Attack)
{
foreach (var legionId in waitLegions)
{
foreach (var city in Map.CityMap.CityList)
{
if (!LegionCanMoveCities[legionId].Contains(city.Id)) continue;
if (selfCity.Contains(city)) continue;
if (!Map.GetGridDataByCityId(city.Id, out var cityGrid)) continue;
var score = (2 * LegionScore[legionId] - CityDefendScore[city.Id] - CityRescueScore[city.Id]) /
(Map.GridMap.CalcDistance(cityGrid, LegionGrid[legionId]) + 1);
results.Add(new MatchResult(legionId, city.Id, score));
}
}
results = results.OrderByDescending(r => r.Score).ToList();
foreach (var result in results)
{
if (LegionTargetCity.ContainsKey(result.LegionId)) continue;
if (LegionTargetCity.ContainsValue(result.CityId)) continue;
LegionTargetCity[result.LegionId] = result.CityId;
LegionStrategy[result.LegionId] = Strategy.Attack;
}
if (LegionTargetCity.Count == LegionUnits.Count) return true;
uint targetId = 0;
foreach (var city in Map.CityMap.CityList)
{
if (selfCity.Contains(city)) continue;
if (targetId == 0 || CityDefendScore[city.Id] + CityRescueScore[city.Id] <
CityDefendScore[targetId] + CityRescueScore[targetId]) targetId = city.Id;
}
foreach (var legionId in waitLegions)
{
if (!LegionTargetCity.TryAdd(legionId, targetId)) continue;
LegionStrategy[legionId] = Strategy.Attack;
}
}
// 计算战力缺口
foreach (var kv in LegionUnits)
{
var combatGap = 0;
if (LegionTargetCity.ContainsKey(kv.Key))
{
if (LegionStrategy[kv.Key] == Strategy.Attack)
{
combatGap = LegionScore[kv.Key] - CityDefendScore[LegionTargetCity[kv.Key]];
}
if (LegionStrategy[kv.Key] == Strategy.Defend)
{
combatGap = LegionScore[kv.Key] - CityEnemyScore[LegionTargetCity[kv.Key]];
}
}
else
{
combatGap = Mathf.Max(5 - kv.Value.Count, 15 - LegionScore[kv.Key]);
combatGap = Mathf.Max(0, combatGap);
}
LegionGapScore[kv.Key] = combatGap;
}
if (CountryStrategy == Strategy.Development)
{
foreach (var legionId in waitLegions)
{
if (LegionUnits[legionId].Count == 1)
{
LegionUnits[legionId][0].LegionId = 0;
FreeUnits.Add(LegionUnits[legionId][0]);
LegionUnits.Remove(legionId);
Debug.Log($"解散军团{legionId}");
return false;
}
LegionStrategy[legionId] = Strategy.Development;
if (LegionUnstableScore[legionId] >= 3)
{
var maxDis = 0;
UnitData maxUnit = null;
foreach (var unit in LegionUnits[legionId])
{
if (!Map.GetGridDataByUnitId(unit.Id, out var unitGrid)) continue;
var dis = Map.GridMap.CalcDistance(unitGrid, LegionGrid[legionId]);
if (dis > maxDis)
{
maxDis = dis;
maxUnit = unit;
}
}
maxUnit.LegionId = 0;
FreeUnits.Add(maxUnit);
LegionUnits[legionId].Remove(maxUnit);
Debug.Log($"{legionId} 军团抛弃小兵 {maxUnit.Id}");
return false;
}
}
}
return true;
}
// 计算自由人策略
public bool CalculateFreeUnitStrategy()
{
if (FreeUnits.Count == 0) return true;
var selfCity = new HashSet<CityData>();
var selfUnit = new List<UnitData>();
Map.GetUnitDataListByPlayerId(Player.Id, selfUnit);
Map.GetCityDataListByPlayerId(Player.Id, selfCity);
var actionId = new CommonActionId();
actionId.ActionType = CommonActionType.UnitAction;
actionId.UnitActionType = UnitActionType.Upgrade;
var action = ActionLogicFactory.GetActionLogic(actionId);
var param = new CommonActionParams();
param.MapData = Map;
param.PlayerData = Player;
param.MainObjectType = MainObjectType.Unit;
foreach (var unit in FreeUnits)
{
bool isFinish = false;
if (action != null)
{
param.UnitData = unit;
if (action.CheckCan(param))
{
FreeUnitStrategy[unit] = Strategy.Defend;
isFinish = true;
}
}
if (isFinish) continue;
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unit.UnitType, unit.GiantType, out var info))
continue;
if (!Map.GetGridDataByUnitId(unit.Id, out var unitGrid)) continue;
var aroundGrids = Map.GridMap.GetAroundGridData(info.AttackRange, info.AttackRange, unitGrid);
foreach (var aroundGrid in aroundGrids)
{
if (!Map.GetCityDataByGid(aroundGrid.Id, out var city)) continue;
if (!selfCity.Contains(city)) continue;
if (!Map.GetUnitDataByGid(aroundGrid.Id, out var cityUnit)) continue;
if (selfUnit.Contains(cityUnit)) continue;
FreeUnitStrategy[unit] = Strategy.Defend;
isFinish = true;
break;
}
if (isFinish) continue;
if (unitGrid.Resource == ResourceType.Treasure)
{
isFinish = true;
FreeUnitStrategy[unit] = Strategy.Defend;
}
if (unitGrid.Resource == ResourceType.CityCenter)
{
if (!Map.GetCityDataByGid(unitGrid.Id, out var city))
{
isFinish = true;
FreeUnitStrategy[unit] = Strategy.Development;
}
else
{
if (!selfCity.Contains(city))
{
isFinish = true;
FreeUnitStrategy[unit] = Strategy.Development;
}
}
}
if (unitGrid.Resource == ResourceType.Starfish)
{
isFinish = true;
FreeUnitStrategy[unit] = Strategy.Development;
}
if (isFinish) continue;
uint legion = 0;
int dis = int.MaxValue;
foreach (var kv in LegionGapScore)
{
if (kv.Value <= 0) continue;
if (Map.GridMap.CalcDistance(LegionGrid[kv.Key], unitGrid) > 4) continue;
var path = PathFinder.FindPath((int)Map.MapConfig.Width, (int)Map.MapConfig.Height,
new (unitGrid.Pos.X, unitGrid.Pos.Y),
new (LegionGrid[kv.Key].Pos.X, LegionGrid[kv.Key].Pos.Y), Map, Player);
if (!path.found) continue;
if (path.length > 3) continue;
if (path.length < dis)
{
legion = kv.Key;
dis = path.length;
}
}
if (legion != 0)
{
Debug.Log($"加入军团{legion}");
unit.LegionId = legion;
return false;
}
aroundGrids = Map.GridMap.GetAroundGridData(2, 2, unitGrid);
var score = 0;
foreach (var aroundGrid in aroundGrids)
{
if (!Map.GetUnitDataByGid(aroundGrid.Id, out var attacker)) continue;
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(attacker.UnitType, attacker.GiantType, out var attackInfo))
continue;
if (selfUnit.Contains(attacker)) score += attackInfo.Cost;
else score -= attackInfo.Cost;
}
if (score > 6)
{
CityData target = null;
int minDis = int.MaxValue;
GridData targetGrid = null;
foreach (var city in selfCity)
{
if (!Map.GetGridDataByCityId(city.Id, out var cityGrid)) continue;
var distance = Map.GridMap.CalcDistance(cityGrid, unitGrid);
if (distance <= 2 || distance >= minDis) continue;
minDis = distance;
target = city;
targetGrid = cityGrid;
}
if (target != null)
{
isFinish = true;
FreeUnitStrategy[unit] = Strategy.Retreat;
UnitTargetGrid[unit.Id] = targetGrid;
UnitTargetCity[unit.Id] = target.Id;
}
}
if (isFinish) continue;
if (CountryStrategy == Strategy.Attack || CountryStrategy == Strategy.Defend)
{
uint legionId = 1;
foreach (var kv in LegionUnits)
{
if (legionId <= kv.Key) legionId = kv.Key + 1;
}
unit.LegionId = legionId;
Debug.Log($"成立");
return false;
}
FreeUnitStrategy[unit] = Strategy.Development;
}
return true;
}
// 刷新小兵攻击系数
public void CalculateUnitAttackRatio()
{
foreach (var unit in Map.UnitMap.UnitList)
{
var ratio = 1;
if (unit.LegionId != 0)
{
if (LegionStrategy.TryGetValue(unit.LegionId, out var value))
{
if (value == Strategy.Attack) ratio = 3;
if (value == Strategy.Development) ratio = 2;
}
}
else
{
if (FreeUnitStrategy.TryGetValue(unit, out var value))
{
if (value == Strategy.Retreat) ratio = 1;
else ratio = 2;
}
}
UnitAttackRatio[unit.Id] = ratio;
}
}
// 计算玩家的军事分
private int CalMilitaryScore(PlayerData player)
{
int score = 0;
var unitList = new List<UnitData>();
Map.GetUnitDataListByPlayerId(player.Id, unitList);
foreach (var unit in unitList)
{
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unit.UnitType, unit.GiantType, out var info))
continue;
score += info.Cost;
}
return score;
}
// 计算威胁分
private int CalThreatScore(PlayerData target)
{
int score = 0;
if (Player.LastAttackPlayers.Contains(target.Id)) score = 10;
var gridSet = Map.GetPlayerTerritoryGridIdSet(Player.Id);
var unitList = new List<UnitData>();
Map.GetUnitDataListByPlayerId(target.Id, unitList);
foreach (var gridId in gridSet)
{
if (!Map.GridMap.GetGridDataByGid(gridId, out var grid)) continue;
foreach (var unit in unitList)
{
if (!Map.GetGridDataByUnitId(unit.Id, out var unitGrid)) continue;
if (gridSet.Contains(unitGrid.Id))
{
score += 2;
continue;
}
var distance = Map.GridMap.CalcDistance(grid, unitGrid);
if (distance == 1) score += 1;
}
}
return score;
}
// 计算军事差距
private int CalMilitaryGapScore(PlayerData target)
{
var selfUnitList = new List<UnitData>();
var targetUnitList = new List<UnitData>();
Map.GetUnitDataListByPlayerId(Player.Id, selfUnitList);
Map.GetUnitDataListByPlayerId(target.Id, targetUnitList);
int score = MilitaryScore[Player.Id] - MilitaryScore[target.Id];
foreach (var selfUnit in selfUnitList)
{
foreach (var targetUnit in targetUnitList)
{
score += CalUnitCounterScore(selfUnit, targetUnit);
score -= CalUnitCounterScore(targetUnit, selfUnit);
}
}
return score;
}
// 计算两个小兵的克制分
public int CalUnitCounterScore(UnitData self, UnitData target)
{
return CalUnitCounterScore(self.UnitType, target.UnitType);
}
// 计算两个小兵的克制分 克制分表 克制表
public int CalUnitCounterScore(UnitType self, UnitType target)
{
if (self == UnitType.Archer && target == UnitType.Warrior) return 1;
if (self == UnitType.Catapult && target == UnitType.Warrior) return 3;
if (self == UnitType.BomberShip && target == UnitType.Warrior) return 1;
if (self == UnitType.Defender && 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.Catapult && target == UnitType.Defender) return 1;
if (self == UnitType.BomberShip && target == UnitType.Defender) return 1;
if (self == UnitType.Minder && target == UnitType.Defender) return 3;
if (self == UnitType.Defender && target == UnitType.Rider) return 1;
if (self == UnitType.Swordsman && target == UnitType.Rider) return 2;
if (self == UnitType.Defender && target == UnitType.Knights) return 1;
if (self == UnitType.Swordsman && target == UnitType.Knights) return 2;
if (self == UnitType.Rider && target == UnitType.Catapult) return 2;
if (self == UnitType.Knights && target == UnitType.Catapult) return 3;
if (self == UnitType.Archer && target == UnitType.Swordsman) return 1;
if (self == UnitType.Catapult && target == UnitType.Swordsman) return 2;
if (self == UnitType.BomberShip && target == UnitType.Swordsman) return 1;
if (self == UnitType.Minder && target == UnitType.BomberShip) return 2;
if (self == UnitType.Rider && target == UnitType.Minder) return 2;
if (self == UnitType.Knights && target == UnitType.Minder) return 3;
return 0;
}
// 计算科技对应的兵种
public void GetUnitTypeByTech(TechType techType, out UnitType unitType, out GiantType giantType)
{
unitType = UnitType.None;
giantType = GiantType.None;
if (techType == TechType.Archery) unitType = UnitType.Archer;
if (techType == TechType.Strategy) unitType = UnitType.Defender;
if (techType == TechType.Chivalry) unitType = UnitType.Knights;
if (techType == TechType.Mathematics) unitType = UnitType.Catapult;
if (techType == TechType.Smithery) unitType = UnitType.Swordsman;
if (techType == TechType.Diplomacy) unitType = UnitType.Cloak;
if (techType == TechType.Philosophy) unitType = UnitType.Minder;
}
// 计算最短地缘距离
private int CalPlayerMinDistance(PlayerData target)
{
var selfGridSet = Map.GetPlayerTerritoryGridIdSet(Player.Id);
var targetGridSet = Map.GetPlayerTerritoryGridIdSet(target.Id);
var minDistance = int.MaxValue;
foreach (var selfGridId in selfGridSet)
{
if (!Map.GridMap.GetGridDataByGid(selfGridId, out var selfGrid)) continue;
foreach (var targetGridId in targetGridSet)
{
if (!Map.GridMap.GetGridDataByGid(targetGridId, out var targetGrid)) continue;
var distance = Map.GridMap.CalcDistance(selfGrid, targetGrid);
if (distance < minDistance) minDistance = distance;
}
}
return minDistance;
}
// 计算国家战略
private void CalculateCountryStrategy()
{
foreach (var target in Map.PlayerMap.PlayerDataList)
{
if (target == Player) continue;
if (ThreatScore[target.Id] < 10) continue;
if (MilitaryGapScore[target.Id] < 2) continue;
CountryStrategy = Strategy.Defend;
return;
}
var threatScore = 0;
var militaryScore = 0;
foreach (var target in Map.PlayerMap.PlayerDataList)
{
if (target == Player) continue;
threatScore += ThreatScore[target.Id];
militaryScore += MilitaryGapScore[target.Id];
}
if (threatScore >= 20 && militaryScore >= 10)
{
CountryStrategy = Strategy.Defend;
return;
}
foreach (var target in Map.PlayerMap.PlayerDataList)
{
if (target == Player) continue;
if (ThreatScore[target.Id] < 2) continue;
if (MilitaryGapScore[target.Id] < -5) continue;
CountryStrategy = Strategy.Attack;
AttackPlayer = target;
return;
}
foreach (var target in Map.PlayerMap.PlayerDataList)
{
if (target == Player) continue;
if (MilitaryGapScore[target.Id] < 0) continue;
CountryStrategy = Strategy.Attack;
AttackPlayer = target;
return;
}
CountryStrategy = Strategy.Development;
}
// 计算城市周围单位
private void CalCityUnits(CityData city)
{
if (!Map.GetGridDataByCityId(city.Id, out var cityGrid)) return;
var selfUnits = new HashSet<UnitData>();
Map.GetPlayerDataByCityId(city.Id, out var player);
Map.GetUnitDataListByPlayerId(player.Id, selfUnits);
CityDefendUnits[city.Id] = new List<UnitData>();
CityEnemyUnits[city.Id] = new List<UnitData>();
foreach (var unit in Map.UnitMap.UnitList)
{
if (!Map.GetGridDataByUnitId(unit.Id, out var unitGrid)) continue;
if (Map.GridMap.CalcDistance(cityGrid, unitGrid) > 3) continue;
if (selfUnits.Contains(unit)) CityDefendUnits[city.Id].Add(unit);
else CityEnemyUnits[city.Id].Add(unit);
}
}
// 计算城市守备分
private int CalCityDefendScore(CityData city)
{
var score = 0;
foreach (var unit in CityDefendUnits[city.Id])
{
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unit.UnitType, unit.GiantType, out var info))
continue;
score += info.Cost;
}
if (city.CityWall) score += 2;
return score;
}
// 计算城市救援分
private int CalCityRescueScore(CityData city)
{
var score = 0;
var selfUnits = new HashSet<UnitData>();
Map.GetPlayerDataByCityId(city.Id, out var player);
Map.GetUnitDataListByPlayerId(player.Id, selfUnits);
if (!Map.GetGridDataByCityId(city.Id, out var cityGrid)) return 0;
foreach (var unit in selfUnits)
{
if (!Map.GetGridDataByUnitId(unit.Id, out var unitGrid)) continue;
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unit.UnitType, unit.GiantType, out var info))
continue;
var distance = Map.GridMap.CalcDistance(cityGrid, unitGrid);
score += info.Cost / (distance + 1);
}
return score;
}
// 计算城市敌军分
private int CalCityEnemyScore(CityData city)
{
var score = 0;
foreach (var unit in CityEnemyUnits[city.Id])
{
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unit.UnitType, unit.GiantType, out var info))
continue;
score += info.Cost;
}
return score;
}
// 计算城市危险分
private int CalCityDangerScore(CityData city)
{
var score = CityEnemyScore[city.Id] - CityDefendScore[city.Id];
foreach (var attackUnit in CityEnemyUnits[city.Id])
{
foreach (var defendUnit in CityDefendUnits[city.Id])
{
score += CalUnitCounterScore(attackUnit, defendUnit);
score -= CalUnitCounterScore(defendUnit, attackUnit);
}
}
return score;
}
// 计算城市最近边境距离
private int CalCityBorderDistance(CityData city)
{
if (!Map.GetGridDataByCityId(city.Id, out var selfGrid)) return 0;
var selfCity = new HashSet<CityData>();
Map.GetPlayerDataByCityId(city.Id, out var player);
Map.GetCityDataListByPlayerId(player.Id, selfCity);
var targetGridList = new List<GridData>();
foreach (var target in Map.CityMap.CityList)
{
if (selfCity.Contains(target)) continue;
foreach (var gridId in target.Territory.TerritoryArea)
{
if (!Map.GridMap.GetGridDataByGid(gridId, out var targetGrid)) continue;
targetGridList.Add(targetGrid);
}
}
var minDistance = int.MaxValue;
foreach (var target in targetGridList)
{
var distance = Map.GridMap.CalcDistance(selfGrid, target);
if (distance < minDistance) minDistance = distance;
}
return minDistance;
}
// 计算城市战略
private void CalculateCityStrategy()
{
var selfCity = new List<CityData>();
Map.GetCityDataListByPlayerId(Player.Id, selfCity);
for (int i = selfCity.Count - 1; i >= 0; i--)
{
var city = selfCity[i];
if (CityDangerScore[city.Id] < 2) continue;
if (CityEnemyScore[city.Id] < 10) continue;
CityStrategy[city] = Strategy.EmergencyDefend;
selfCity.RemoveAt(i);
}
// for (int i = selfCity.Count - 1; i >= 0; i--)
// {
// var city = selfCity[i];
// if (CityEnemyScore[city.Id] > 0) continue;
// if (CityBorderDistance[city.Id] < 5) continue;
// StrategyCity[Strategy.Development].Add(city);
// selfCity.RemoveAt(i);
// }
for (int i = selfCity.Count - 1; i >= 0; i--)
{
var city = selfCity[i];
if (CityBorderDistance[city.Id] < 5) continue;
if (CityDangerScore[city.Id] <= 0) continue;
CityStrategy[city] = Strategy.Military;
selfCity.RemoveAt(i);
}
for (int i = selfCity.Count - 1; i >= 0; i--)
{
var city = selfCity[i];
if (CityDangerScore[city.Id] < 5) continue;
CityStrategy[city] = Strategy.Military;
selfCity.RemoveAt(i);
}
for (int i = selfCity.Count - 1; i >= 0; i--)
{
var city = selfCity[i];
if (CityEnemyScore[city.Id] <= 0) continue;
if (CityDefendScore[city.Id] > 0) continue;
CityStrategy[city] = Strategy.Military;
selfCity.RemoveAt(i);
}
foreach (var city in selfCity) CityStrategy.TryAdd(city, CountryStrategy);
}
}
public class MatchResult
{
public uint LegionId;
public uint CityId;
public float Score;
public MatchResult(uint legionId, uint cityId, float score)
{
LegionId = legionId;
CityId = cityId;
Score = score;
}
}
public enum AIDifficult
{
Simple,
Normal,
Hard,
}
public class AIActionBase
{
public CalculateResult Result;
public CommonActionParams Param;
public ActionLogicBase ActionLogic;
public bool IsInSight;
public AIActionBase(CommonActionParams param, ActionLogicBase action)
{
Param = param;
ActionLogic = action;
IsInSight = false;
}
public void CheckIsActionInPlayerSight()
{
IsInSight = true;
var player = Param.MapData.PlayerMap.SelfPlayerData;
if (Param.MapData.GridMap.GetGridDataByGid(Param.GridId, out var grid)
&& player.Sight.CheckIsInSight(grid.Id)) return;
if (Param.MapData.GetGridDataByUnitId(Param.UnitId, out var unitGrid)
&& player.Sight.CheckIsInSight(unitGrid.Id)) return;
if (Param.MapData.GetGridDataByUnitId(Param.TargetUnitId, out var targetUnit)
&& player.Sight.CheckIsInSight(targetUnit.Id)) return;
if (Param.MapData.GetGridDataByCityId(Param.CityId, out var cityGrid)
&& player.Sight.CheckIsInSight(cityGrid.Id)) return;
IsInSight = false;
}
}
}