xingweishu

This commit is contained in:
wuwenbo 2025-06-18 20:22:27 +08:00
parent 3765320b92
commit 92cf2d28fa
49 changed files with 2413 additions and 125 deletions

File diff suppressed because one or more lines are too long

View File

@ -12,7 +12,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: ee4fa5caed5fb2b43b903ef7859c9bf9, type: 3}
m_Name: BlackBoard
m_EditorClassIdentifier:
_serializedBlackboard: '{"_variables":{"AIData":{"_value":{},"_name":"AIData","_id":"84e6dedf-2cb5-4be6-b2d0-55291cb78d97","$type":"NodeCanvas.Framework.Variable`1[[Logic.AI.AICalculatorData,
_serializedBlackboard: '{"_variables":{"Data":{"_value":{"StrategyCity":[{"Key":0,"Value":[]},{"Key":1,"Value":[]},{"Key":2,"Value":[]},{"Key":3,"Value":[]},{"Key":4,"Value":[]},{"Key":5,"Value":[]}],"StrategyUnit":[{"Key":0,"Value":[]},{"Key":1,"Value":[]},{"Key":2,"Value":[]},{"Key":3,"Value":[]},{"Key":4,"Value":[]},{"Key":5,"Value":[]}],"StrategyLegion":[{"Key":0,"Value":[]},{"Key":1,"Value":[]},{"Key":2,"Value":[]},{"Key":3,"Value":[]},{"Key":4,"Value":[]},{"Key":5,"Value":[]}],"MilitaryScore":{},"DevelopmentScore":{},"ThreatScore":{},"MilitaryGapScore":{},"GeographicalDistance":{},"CityDefendScore":{},"CityRescueScore":{},"CityEnemyScore":{},"CityDangerScore":{},"CityBorderDistance":{},"LegionUnits":{},"FreeUnits":[],"LegionScore":{},"LegionGrid":{},"LegionGapScore":{},"LegionUnstableScore":{},"AIActions":[],"TargetParam":{},"Marks":{}},"_name":"Data","_id":"84e6dedf-2cb5-4be6-b2d0-55291cb78d97","$type":"NodeCanvas.Framework.Variable`1[[Logic.AI.AICalculatorData,
Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]"}}}'
_objectReferences: []
_UID: 741313d3-f9b0-425b-9848-f246bb07a9cb

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 49c0da9bda6079b4084856c777257077
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,24 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7a686a47eee2fa44cb0a34b5d86e4d5e, type: 3}
m_Name: DefendTech
m_EditorClassIdentifier:
_serializedGraph: '{"type":"NodeCanvas.BehaviourTrees.BehaviourTree","nodes":[{"_tag":"","_position":{"x":-4057.796,"y":495.8528},"$type":"NodeCanvas.BehaviourTrees.Sequencer","$id":"0"},{"_action":{"MarkStr":"DefendTech","CheckNotHave":true,"$type":"NodeCanvas.Tasks.Actions.MarkAction"},"_position":{"x":-4346.097,"y":657.5146},"$type":"NodeCanvas.BehaviourTrees.ActionNode","$id":"1"},{"_tag":"","_position":{"x":-3859.394,"y":652.2503},"$type":"NodeCanvas.BehaviourTrees.Selector","$id":"2"},{"_tag":"","_position":{"x":-3980.755,"y":761.3211},"$type":"NodeCanvas.BehaviourTrees.Sequencer","$id":"3"},{"_condition":{"CountryStrategy":1,"$type":"NodeCanvas.Tasks.Actions.CountryStrategyCondition"},"_tag":"","_position":{"x":-4438.908,"y":944.6642},"$type":"NodeCanvas.BehaviourTrees.ConditionNode","$id":"4"},{"_action":{"Strategy":1,"IsPlayer":true,"$type":"NodeCanvas.Tasks.Actions.AIParamAction"},"_tag":"","_position":{"x":-4328.108,"y":1030.32},"$type":"NodeCanvas.BehaviourTrees.ActionNode","$id":"5"},{"_action":{"ActionTypes":[9],"$type":"NodeCanvas.Tasks.Actions.AIGeneratorAction"},"_tag":"","_position":{"x":-4135.428,"y":1028.843},"$type":"NodeCanvas.BehaviourTrees.ActionNode","$id":"6"},{"_action":{"CalculateTypes":[0],"$type":"NodeCanvas.Tasks.Actions.AICalculateAction"},"_tag":"","_position":{"x":-4088.958,"y":1108.48},"$type":"NodeCanvas.BehaviourTrees.ActionNode","$id":"7"},{"_action":{"$type":"NodeCanvas.Tasks.Actions.AIExecuteAction"},"_tag":"","_position":{"x":-3920.547,"y":1026.896},"$type":"NodeCanvas.BehaviourTrees.ActionNode","$id":"8"},{"_action":{"MarkStr":"DefendTech","$type":"NodeCanvas.Tasks.Actions.MarkAction"},"_position":{"x":-3854.173,"y":854.0856},"$type":"NodeCanvas.BehaviourTrees.ActionNode","$id":"9"}],"connections":[{"_sourceNode":{"$ref":"0"},"_targetNode":{"$ref":"1"},"$type":"NodeCanvas.BehaviourTrees.BTConnection"},{"_sourceNode":{"$ref":"0"},"_targetNode":{"$ref":"2"},"$type":"NodeCanvas.BehaviourTrees.BTConnection"},{"_sourceNode":{"$ref":"2"},"_targetNode":{"$ref":"3"},"$type":"NodeCanvas.BehaviourTrees.BTConnection"},{"_sourceNode":{"$ref":"2"},"_targetNode":{"$ref":"9"},"$type":"NodeCanvas.BehaviourTrees.BTConnection"},{"_sourceNode":{"$ref":"3"},"_targetNode":{"$ref":"4"},"$type":"NodeCanvas.BehaviourTrees.BTConnection"},{"_sourceNode":{"$ref":"3"},"_targetNode":{"$ref":"5"},"$type":"NodeCanvas.BehaviourTrees.BTConnection"},{"_sourceNode":{"$ref":"3"},"_targetNode":{"$ref":"6"},"$type":"NodeCanvas.BehaviourTrees.BTConnection"},{"_sourceNode":{"$ref":"3"},"_targetNode":{"$ref":"7"},"$type":"NodeCanvas.BehaviourTrees.BTConnection"},{"_sourceNode":{"$ref":"3"},"_targetNode":{"$ref":"8"},"$type":"NodeCanvas.BehaviourTrees.BTConnection"}],"canvasGroups":[],"localBlackboard":{"_variables":{}},"derivedData":{"repeat":true,"$type":"NodeCanvas.BehaviourTrees.BehaviourTree+DerivedSerializationData"}}'
_objectReferences: []
_graphSource:
_version: 3.33
_category:
_comments:
_translation: {x: 4836, y: -298}
_zoomFactor: 0.9998963
_haltSerialization: 0
_externalSerializationFile: {fileID: 0}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5c2d56f30f0281f4b89ebbbe5f2015e8
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cf5354f0e5a794a4688155a6dc1354f9
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6b1eeef55a4e73d42b4d25f9e0794b53
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c172427e55805814c9b171c0d4a91cb1
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -23,7 +23,7 @@ namespace NodeCanvas.Tasks.Actions
protected override string info
{
get { return string.Format($"Calculate Action"); }
get { return string.Format($"对行为评分,评分策略为 {CalculateTypes[0]}"); }
}
protected override void OnExecute()
@ -36,10 +36,13 @@ namespace NodeCanvas.Tasks.Actions
return;
}
foreach (var calculateType in CalculateTypes)
AIActionScoreCalculator.CalculateAIActionScore(data.value, calculateType);
data.value.AIActions = data.value.AIActions.OrderBy(a => a.Result.GetAllScore()).ToList();
data.value.MaxAiAction = AIActionScoreCalculator.CalculateAIActionScore(data.value, CalculateTypes);
if (data.value.MaxAiAction == null)
{
EndAction(false);
return;
}
EndAction(true);
}
}

View File

@ -18,21 +18,25 @@ namespace NodeCanvas.Tasks.Actions
{
protected override string info
{
get { return string.Format($"AI Execute Action"); }
get { return string.Format($"执行"); }
}
protected override void OnExecute()
{
// 直接从Blackboard获取AICalculatorData
var data = blackboard.GetVariable<AICalculatorData>("Data");
if (data?.value == null || data.value.AIActions.Count == 0)
if (data?.value == null)
{
EndAction(false);
return;
}
var aiAction = data.value.AIActions[0];
aiAction.ActionLogic.Execute(aiAction.Param);
data.value.MaxAiAction.Param.MapData = data.value.Map;
data.value.MaxAiAction.Param.RefreshParams();
data.value.MaxAiAction.ActionLogic.Execute(data.value.MaxAiAction.Param);
data.value.IsExcute = true;
data.value.MaxAiAction.CheckIsActionInPlayerSight();
data.value.IsInSight = data.value.MaxAiAction.IsInSight;
EndAction(true);
}
}

View File

@ -0,0 +1,38 @@
/*
* @Author:
* @Description:
* @Date: 20250606 19:06:16
* @Modify:
*/
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
namespace NodeCanvas.Tasks.Actions
{
[Category("AIAction")]
public class AIFinishAction : ActionTask
{
protected override string info
{
get { return string.Format($"行为树执行结束"); }
}
protected override void OnExecute()
{
// 直接从Blackboard获取AICalculatorData
var data = blackboard.GetVariable<AICalculatorData>("Data");
if (data?.value == null)
{
EndAction(false);
return;
}
data.value.IsFinish = true;
EndAction(true);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c80b7d7ac0524f64bb60c9aefbad0475
timeCreated: 1750228853

View File

@ -59,9 +59,9 @@ namespace NodeCanvas.Tasks.Actions
if (data.value.StrategyLegion.ContainsKey(strategy) && data.value.StrategyLegion[strategy].Count != 0)
{
var legion = data.value.StrategyLegion[strategy][0];
if (data.value.LegionUnit.ContainsKey(legion) && data.value.LegionUnit[legion].Count != 0)
if (data.value.LegionUnits.ContainsKey(legion) && data.value.LegionUnits[legion].Count != 0)
{
data.value.LegionUnit[legion].RemoveAt(0);
data.value.LegionUnits[legion].RemoveAt(0);
}
else
{

View File

@ -46,6 +46,7 @@ namespace NodeCanvas.Tasks.Actions
data.value.TargetParam.MapData = data.value.Map;
data.value.TargetParam.PlayerData = data.value.Player;
data.value.TargetParam.OnParamChanged();
if (IsPlayer) EndAction(true);
if (IsCity)
@ -53,6 +54,7 @@ namespace NodeCanvas.Tasks.Actions
if (data.value.StrategyCity.ContainsKey(Strategy) && data.value.StrategyCity[Strategy].Count != 0)
{
data.value.TargetParam.CityData = data.value.StrategyCity[Strategy][0];
data.value.TargetParam.OnParamChanged();
EndAction(true);
return;
}
@ -63,6 +65,7 @@ namespace NodeCanvas.Tasks.Actions
if (data.value.StrategyUnit.ContainsKey(Strategy) && data.value.StrategyUnit[Strategy].Count != 0)
{
data.value.TargetParam.UnitData = data.value.StrategyUnit[Strategy][0];
data.value.TargetParam.OnParamChanged();
EndAction(true);
return;
}
@ -73,9 +76,10 @@ namespace NodeCanvas.Tasks.Actions
if (data.value.StrategyLegion.ContainsKey(Strategy) && data.value.StrategyLegion[Strategy].Count != 0)
{
var legion = data.value.StrategyLegion[Strategy][0];
if (data.value.LegionUnit.ContainsKey(legion) && data.value.LegionUnit[legion].Count != 0)
if (data.value.LegionUnits.ContainsKey(legion) && data.value.LegionUnits[legion].Count != 0)
{
data.value.TargetParam.UnitData = data.value.LegionUnit[legion][0];
data.value.TargetParam.UnitData = data.value.LegionUnits[legion][0];
data.value.TargetParam.OnParamChanged();
EndAction(true);
return;
}

View File

@ -0,0 +1,67 @@
/*
* @Author:
* @Description:
* @Date: 20250606 19:06:16
* @Modify:
*/
using System.Collections.Generic;
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using RuntimeData;
namespace NodeCanvas.Tasks.Actions
{
[Category("AIAction")]
public class AIParamAroundNoUnitCity : ActionTask
{
public int Offset;
protected override string info
{
get
{
return string.Format($"周围 {Offset} 格内存在无人驻守的我方城市");
}
}
protected override void OnExecute()
{
// 直接从Blackboard获取AICalculatorData
var data = blackboard.GetVariable<AICalculatorData>("Data");
if (data?.value?.TargetParam.UnitData == null)
{
EndAction(false);
return;
}
var param = data.value.TargetParam;
var unit = data.value.TargetParam.UnitData;
if (!param.MapData.GetGridDataByUnitId(unit.Id, out var unitGrid))
{
EndAction(false);
return;
}
var selfCity = new HashSet<CityData>();
param.MapData.GetCityDataListByPlayerId(param.PlayerData.Id, selfCity);
var aroundGrid = param.MapData.GridMap.GetAroundGridData(Offset, Offset, unitGrid);
foreach (var grid in aroundGrid)
{
if (!param.MapData.GetCityDataByGid(grid.Id, out var city)) continue;
if (param.MapData.GetUnitDataByGid(grid.Id, out var cityUnit)) continue;
if (!selfCity.Contains(city)) continue;
data.value.TargetParam.GridData = grid;
data.value.TargetParam.CityData = city;
data.value.TargetParam.OnParamChanged();
EndAction(true);
return;
}
EndAction(false);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e16fa8844b264801bb7df58c5be25c29
timeCreated: 1750218255

View File

@ -0,0 +1,71 @@
/*
* @Author:
* @Description:
* @Date: 20250606 19:06:16
* @Modify:
*/
using System.Collections.Generic;
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using RuntimeData;
namespace NodeCanvas.Tasks.Actions
{
[Category("AIAction")]
public class AIParamEnemyCityTarget : ActionTask
{
protected override string info
{
get { return string.Format($"将可达最近的地方城市中心设为目标点"); }
}
protected override void OnExecute()
{
// 直接从Blackboard获取AICalculatorData
var data = blackboard.GetVariable<AICalculatorData>("Data");
if (data?.value?.TargetParam.UnitData == null)
{
EndAction(false);
return;
}
var param = data.value.TargetParam;
if (!param.MapData.GetGridDataByUnitId(param.UnitData.Id, out var unitGrid))
{
EndAction(false);
return;
}
var selfCity = new HashSet<CityData>();
param.MapData.GetCityDataListByPlayerId(param.PlayerData.Id, selfCity);
var length = int.MaxValue;
GridData minTarget = null;
foreach (var city in param.MapData.CityMap.CityList)
{
if (selfCity.Contains(city)) continue;
if (!param.MapData.GetGridDataByCityId(city.Id, out var cityGrid)) continue;
var path = PathFinder.FindPath((int)param.MapData.MapConfig.Width, (int)param.MapData.MapConfig.Height,
new (unitGrid.Pos.X, unitGrid.Pos.Y), new (cityGrid.Pos.X, cityGrid.Pos.Y), param.MapData, param.PlayerData);
if (!path.found) continue;
if (path.length < length)
{
length = path.length;
minTarget = cityGrid;
}
}
if (minTarget != null)
{
data.value.TargetParam.GridData = minTarget;
data.value.TargetParam.OnParamChanged();
EndAction(true);
return;
}
EndAction(false);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: db30b01ea10348129709429852518f2b
timeCreated: 1750244250

View File

@ -0,0 +1,98 @@
/*
* @Author:
* @Description:
* @Date: 20250606 19:06:16
* @Modify:
*/
using System.Collections.Generic;
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
using RuntimeData;
namespace NodeCanvas.Tasks.Actions
{
[Category("AIAction")]
public class AIParamExplore : ActionTask
{
public int Offset = 1;
protected override string info
{
get { return string.Format($"查找周围 {Offset} 格内可抵达的探索目标"); }
}
protected override void OnExecute()
{
// 直接从Blackboard获取AICalculatorData
var data = blackboard.GetVariable<AICalculatorData>("Data");
if (data?.value?.TargetParam.UnitData == null)
{
EndAction(false);
return;
}
var param = data.value.TargetParam;
if (!param.MapData.GetGridDataByUnitId(param.UnitData.Id, out var unitGrid))
{
EndAction(false);
return;
}
var selfCity = new HashSet<CityData>();
param.MapData.GetCityDataListByPlayerId(param.PlayerData.Id, selfCity);
var aroundGrids = param.MapData.GridMap.GetAroundGridData(Offset, Offset, unitGrid);
var targetGrids = new List<List<GridData>>();
for (int i = 0; i < 3; i++) targetGrids.Add(new List<GridData>());
foreach (var grid in aroundGrids)
{
if (grid == unitGrid) continue;
if (grid.Resource == ResourceType.CityCenter && !param.MapData.GetCityDataByGid(grid.Id, out _))
{
targetGrids[0].Add(grid);
continue;
}
if (grid.Resource == ResourceType.Treasure)
{
targetGrids[1].Add(grid);
continue;
}
if (grid.Resource == ResourceType.Starfish)
{
targetGrids[2].Add(grid);
continue;
}
}
var length = int.MaxValue;
GridData minTarget = null;
foreach (var list in targetGrids)
{
foreach (var target in list)
{
var path = PathFinder.FindPath((int)param.MapData.MapConfig.Width, (int)param.MapData.MapConfig.Height,
new (unitGrid.Pos.X, unitGrid.Pos.Y), new (target.Pos.X, target.Pos.Y), param.MapData, param.PlayerData);
if (!path.found) continue;
if (path.length < length)
{
length = path.length;
minTarget = target;
}
}
}
if (minTarget != null)
{
data.value.TargetParam.GridData = minTarget;
data.value.TargetParam.OnParamChanged();
EndAction(true);
return;
}
EndAction(false);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f94528ce5a934593b2d384da2aa304fd
timeCreated: 1750156321

View File

@ -0,0 +1,54 @@
/*
* @Author:
* @Description:
* @Date: 20250606 19:06:16
* @Modify:
*/
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
namespace NodeCanvas.Tasks.Actions
{
[Category("AIAction")]
public class AIParamHealth : ActionTask
{
public bool GreaterThan = true;
public float Ratio = 0.6f;
protected override string info
{
get
{
if (GreaterThan) return string.Format($"血量大于等于");
return string.Format($"血量小于等于");
}
}
protected override void OnExecute()
{
// 直接从Blackboard获取AICalculatorData
var data = blackboard.GetVariable<AICalculatorData>("Data");
if (data?.value?.TargetParam.UnitData == null)
{
EndAction(false);
return;
}
var unit = data.value.TargetParam.UnitData;
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unit.UnitType, unit.GiantType, out var info))
{
EndAction(false);
return;
}
if (GreaterThan && unit.Health >= info.MaxHealth * Ratio) EndAction(true);
else if (!GreaterThan && unit.Health <= info.MaxHealth * Ratio) EndAction(true);
else EndAction(false);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 912e15e2957c415a83cf667e56b94677
timeCreated: 1750216920

View File

@ -0,0 +1,61 @@
/*
* @Author:
* @Description:
* @Date: 20250606 19:06:16
* @Modify:
*/
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
namespace NodeCanvas.Tasks.Actions
{
[Category("AIAction")]
public class AIParamSelfTerritory : ActionTask
{
public bool IsIn = true;
protected override string info
{
get
{
if (IsIn) return string.Format($"在我方领土");
return string.Format($"不在我方领土");
}
}
protected override void OnExecute()
{
// 直接从Blackboard获取AICalculatorData
var data = blackboard.GetVariable<AICalculatorData>("Data");
if (data?.value?.TargetParam.UnitData == null)
{
EndAction(false);
return;
}
var unit = data.value.TargetParam.UnitData;
var set = data.value.TargetParam.MapData.GetPlayerTerritoryGridIdSet(data.value.TargetParam.PlayerData.Id);
if (data.value.TargetParam.MapData.GetGridDataByUnitId(unit.Id, out var grid))
{
if (IsIn && set.Contains(grid.Id))
{
EndAction(true);
return;
}
if (!IsIn && !set.Contains(grid.Id))
{
EndAction(true);
return;
}
}
EndAction(false);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 36fb2cbde3fa43b888e9b54a6c53d832
timeCreated: 1750217595

View File

@ -0,0 +1,62 @@
/*
* @Author:
* @Description:
* @Date: 20250606 19:06:16
* @Modify:
*/
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
namespace NodeCanvas.Tasks.Actions
{
[Category("AIAction")]
public class AIParamTargetCityHaveUnit : ActionTask
{
public bool CheckHaveUnit = true;
protected override string info
{
get
{
if (CheckHaveUnit) return string.Format($"目标城市上有单位");
return string.Format($"目标城市上没有单位");
}
}
protected override void OnExecute()
{
// 直接从Blackboard获取AICalculatorData
var data = blackboard.GetVariable<AICalculatorData>("Data");
if (data?.value?.TargetParam.UnitData == null)
{
EndAction(false);
return;
}
var param = data.value.TargetParam;
var legion = param.UnitData.LegionId;
var targetCity = data.value.LegionTargetCity[legion];
if (targetCity == 0)
{
EndAction(false);
return;
}
if (param.MapData.GetGridDataByCityId(targetCity, out var gridData))
{
if (param.MapData.GetUnitDataByGid(gridData.Id, out var unitData))
{
EndAction(CheckHaveUnit);
return;
}
}
EndAction(!CheckHaveUnit);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5e827c6d3dd340c28c48224d17641397
timeCreated: 1750164921

View File

@ -0,0 +1,49 @@
/*
* @Author:
* @Description:
* @Date: 20250606 19:06:16
* @Modify:
*/
using Logic.AI;
using NodeCanvas.Framework;
using ParadoxNotion.Design;
namespace NodeCanvas.Tasks.Actions
{
[Category("AIAction")]
public class AIParamTrainUnitType : ActionTask
{
public UnitType UnitType;
protected override string info
{
get
{
return string.Format($"只能建 {UnitType} 小兵");
}
}
protected override void OnExecute()
{
// 直接从Blackboard获取AICalculatorData
var data = blackboard.GetVariable<AICalculatorData>("Data");
if (data?.value?.TargetParam == null)
{
EndAction(false);
return;
}
for (int i = data.value.AIActions.Count - 1; i >= 0; i--)
{
if (data.value.AIActions[i].ActionLogic.ActionId.UnitType == UnitType) continue;
data.value.AIActions.RemoveAt(i);
}
EndAction(data.value.AIActions.Count > 0);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bf0540198eaf409094a3eb834578f27b
timeCreated: 1750246584

View File

@ -18,6 +18,10 @@ namespace NodeCanvas.Tasks.Actions
{
public Strategy CountryStrategy;
protected override string info
{
get { return string.Format($"当国家战略为: {CountryStrategy} 时"); }
}
protected override bool OnCheck()
{

View File

@ -17,18 +17,35 @@ namespace NodeCanvas.Tasks.Actions
public class MarkAction : ActionTask
{
public string MarkStr;
public bool CheckNotHave;
protected override string info
{
get { return string.Format($"Add {MarkStr}"); }
get
{
if (CheckNotHave) return string.Format($"检查没有 {MarkStr} 标记");
else return string.Format($"添加 {MarkStr} 标记");
}
}
protected override void OnExecute()
{
// 直接从Blackboard获取AICalculatorData
var data = blackboard.GetVariable<AICalculatorData>("Data");
if (data?.value != null) data.value.Marks.Add(MarkStr);
if (data?.value == null)
{
EndAction(false);
return;
}
if (CheckNotHave)
{
EndAction(!data.value.Marks.Contains(MarkStr));
return;
}
data.value.Marks.Add(MarkStr);
EndAction(true);
}
}

View File

@ -142,6 +142,18 @@ namespace RuntimeData
}
}
// 通过玩家 ID 找城市Set
public void GetCityDataListByPlayerId(uint pid, HashSet<CityData> cityDataList=null)
{
if (cityDataList == null) cityDataList = new HashSet<CityData>();
foreach (var kv in CityToPlayerDict)
{
if (kv.Value != pid) continue;
if (!CityMap.GetCityById(kv.Key, out var cityData)) continue;
cityDataList.Add(cityData);
}
}
// 通过玩家 ID 找城市 Set
public HashSet<CityData> GetCityDataSetByPlayerId(uint pid)
{

View File

@ -184,7 +184,6 @@ namespace RuntimeData
public uint PlayerCivId;
public uint PlayerForceId;
public uint CradleCityId;
// 是否存活
public bool Alive;
@ -205,7 +204,9 @@ namespace RuntimeData
// 用于记录累计击杀多少 unit
public int TotalKill;
public List<uint> MeetPlayerSet;
public List<uint> MeetPlayers;
public List<uint> CurAttackPlayers;
public List<uint> LastAttackPlayers;
//用于记录是否死亡是否要centMessage来宣告死亡信息
public bool DieMark = false;
@ -216,7 +217,9 @@ namespace RuntimeData
Sight = new MapSightData();
TechTree = new TechTreeData();
Wonder = new WonderData();
MeetPlayerSet = new List<uint>();
MeetPlayers = new List<uint>();
CurAttackPlayers = new List<uint>();
LastAttackPlayers = new List<uint>();
//InitData();
}
@ -232,8 +235,10 @@ namespace RuntimeData
Sight = new MapSightData();
Wonder = new WonderData();
MeetPlayerSet = new List<uint>();
MeetPlayerSet.Add(Id);
CurAttackPlayers = new List<uint>();
LastAttackPlayers = new List<uint>();
MeetPlayers = new List<uint>();
MeetPlayers.Add(Id);
InitData(civId,forceId);
}
@ -251,6 +256,14 @@ namespace RuntimeData
TechTree = new TechTreeData(copyData.TechTree);
Wonder = new WonderData(copyData.Wonder);
CurAttackPlayers = new List<uint>();
foreach (var id in copyData.CurAttackPlayers) CurAttackPlayers.Add(id);
LastAttackPlayers = new List<uint>();
foreach (var id in copyData.LastAttackPlayers) LastAttackPlayers.Add(id);
MeetPlayers = new List<uint>();
foreach (var id in copyData.MeetPlayers) MeetPlayers.Add(id);
TurnNoAttack = copyData.TurnNoAttack;
TotalKill = copyData.TotalKill;
@ -270,6 +283,13 @@ namespace RuntimeData
Sight.DeepCopy(copyData.Sight);
TechTree.DeepCopy(copyData.TechTree);
Wonder.DeepCopy(copyData.Wonder);
CurAttackPlayers.Clear();
foreach (var id in copyData.CurAttackPlayers) CurAttackPlayers.Add(id);
LastAttackPlayers.Clear();
foreach (var id in copyData.LastAttackPlayers) LastAttackPlayers.Add(id);
MeetPlayers.Clear();
foreach (var id in copyData.MeetPlayers) MeetPlayers.Add(id);
TurnNoAttack = copyData.TurnNoAttack;
TotalKill = copyData.TotalKill;
@ -302,6 +322,9 @@ namespace RuntimeData
public void OnTurnStart(MapData map)
{
Turn++;
LastAttackPlayers.Clear();
foreach (var id in CurAttackPlayers)LastAttackPlayers.Add(id);
CurAttackPlayers.Clear();
for (int i = Skills.Count - 1; i >= 0; i--)
{
var skill = Skills[i];

View File

@ -289,6 +289,7 @@ namespace RuntimeData
//MoveRange = Table.Instance.QueryUnitMoveRange(UnitType);
Exp = 0;
Veteran = false;
LegionId = 0;
}
// 全局通知调用

View File

@ -9,14 +9,14 @@ public enum UnitType
{
None,
Warrior, // 战士
Rider, // 骑兵
Archer, // 弓箭手
Defender, // 盾兵
Knights, // 骑士们
Catapult, // 投石车
Swordsman, // 剑士
Cloak, // 间谍
Minder, // 脑控
Rider, // 骑兵 Riding
Archer, // 弓箭手 Archery
Defender, // 盾兵 Strategy
Knights, // 重骑兵 Chivalry
Catapult, // 投石车 Mathematics
Swordsman, // 剑士 Smithery
Cloak, // 间谍 Diplomacy
Minder, // 脑控 Philosophy
Boat, // 小艇
Ship, // 远战船
RammerShip, // 近战船

File diff suppressed because it is too large Load Diff

View File

@ -557,31 +557,523 @@ namespace Logic.AI
}
// 这里的得分是和当前情况的差值
public static void CalculateAIActionScore(AICalculatorData data, CalculateType type)
public static AIActionBase CalculateAIActionScore(AICalculatorData data, List<CalculateType> types)
{
if (type == CalculateType.PlayerTechDefend)
AIActionBase maxAction = null;
var calMap = data.Map.GetDeepCopyMapData();
var startResult = CalculateAIActionScore(data, data.TargetParam, types);
var maxScoreOffset = 0f;
foreach (var aiAction in data.AIActions)
{
foreach (var aiAction in data.AIActions)
if (CalculateAIActionIsTrue(data, aiAction, types)) return aiAction;
calMap.DeepCopy(data.Map);
aiAction.Param.MapData = calMap;
aiAction.Param.RefreshParams();
aiAction.ActionLogic.Execute(aiAction.Param);
aiAction.Result = CalculateAIActionScore(data, aiAction.Param, types);
var scoreOffset = aiAction.Result.GetAllScore() - startResult.GetAllScore();
if (scoreOffset <= 0) continue;
if (maxAction == null || scoreOffset > maxScoreOffset)
{
aiAction.Result = new CalculateResult();
if (aiAction.ActionLogic.ActionId.ActionType == CommonActionType.LearnTech &&
aiAction.ActionLogic.ActionId.TechType == TechType.Strategy)
{
aiAction.Result.Score[type] = 2;
}
if (aiAction.ActionLogic.ActionId.ActionType == CommonActionType.LearnTech &&
aiAction.ActionLogic.ActionId.TechType == TechType.Organization)
{
aiAction.Result.Score[type] = 1;
}
maxAction = aiAction;
maxScoreOffset = scoreOffset;
}
}
return maxAction;
}
private static bool CalculateAIActionIsTrue(AICalculatorData data, AIActionBase aiAction, List<CalculateType> types)
{
foreach (var type in types)
{
if (type == CalculateType.UnitCollect)
{
if (aiAction.ActionLogic.ActionId.UnitActionType == UnitActionType.Capture) return true;
if (aiAction.ActionLogic.ActionId.UnitActionType == UnitActionType.Gather) return true;
if (aiAction.ActionLogic.ActionId.UnitActionType == UnitActionType.Examine) return true;
}
if (type == CalculateType.UnitUpgrade)
{
if (aiAction.ActionLogic.ActionId.UnitActionType == UnitActionType.Upgrade) return true;
}
if (type == CalculateType.LegionDefendAttack)
{
if (aiAction.Param.UnitData == null || aiAction.Param.TargetUnitData == null) return false;
if (aiAction.Param.UnitData.GetAttackValue(aiAction.Param.MapData) <
aiAction.Param.TargetUnitData.GetAttackValue(aiAction.Param.MapData)) return false;
return true;
}
if (type == CalculateType.UnitAttack)
{
if (aiAction.Param.UnitData == null || aiAction.Param.TargetUnitData == null) return false;
if (aiAction.Param.UnitData.GetAttackValue(aiAction.Param.MapData) <
aiAction.Param.TargetUnitData.GetAttackValue(aiAction.Param.MapData)) return false;
return true;
}
if (type == CalculateType.UnitAuto)
{
return Random.value > 0.5f;
}
if (type == CalculateType.CityOK)
{
return true;
}
if (type == CalculateType.LegionAttackCityUnit)
{
if (aiAction.Param.UnitData == null || aiAction.Param.TargetUnitData == null) return false;
if (aiAction.Param.UnitData.LegionId == 0) return false;
if (!data.LegionTargetCity.TryGetValue(aiAction.Param.UnitData.LegionId, out var cityId)) return false;
if (!aiAction.Param.MapData.GetGridDataByCityId(cityId, out var cityGrid)) return false;
if (!aiAction.Param.MapData.GetGridDataByUnitId(aiAction.Param.TargetUnitData.Id, out var unitGrid)) return false;
if (unitGrid != cityGrid) return false;
var selfDmg = Table.Instance.CalcDamage(aiAction.Param.MapData, aiAction.Param.UnitData, aiAction.Param.TargetUnitData);
var otherDmg = Table.Instance.CalcDamage(aiAction.Param.MapData, aiAction.Param.TargetUnitData, aiAction.Param.UnitData);
if (selfDmg * 3 < otherDmg) return false;
return true;
}
if (type == CalculateType.LegionAttackCityTerritoryUnit)
{
if (aiAction.Param.UnitData == null || aiAction.Param.TargetUnitData == null) return false;
if (aiAction.Param.UnitData.LegionId == 0) return false;
if (!data.LegionTargetCity.TryGetValue(aiAction.Param.UnitData.LegionId, out var cityId)) return false;
if (!aiAction.Param.MapData.GetGridDataByCityId(cityId, out var cityGrid)) return false;
if (!aiAction.Param.MapData.GetUnitDataByGid(cityGrid.Id, out var unitData)) return false;
var selfDmg = Table.Instance.CalcDamage(aiAction.Param.MapData, aiAction.Param.UnitData, aiAction.Param.TargetUnitData);
var otherDmg = Table.Instance.CalcDamage(aiAction.Param.MapData, aiAction.Param.TargetUnitData, aiAction.Param.UnitData);
if (selfDmg * 3 < otherDmg) return false;
return true;
}
if (type == CalculateType.LegionAttackUnit)
{
if (aiAction.Param.UnitData == null || aiAction.Param.TargetUnitData == null) return false;
var selfDmg = Table.Instance.CalcDamage(aiAction.Param.MapData, aiAction.Param.UnitData, aiAction.Param.TargetUnitData);
var otherDmg = Table.Instance.CalcDamage(aiAction.Param.MapData, aiAction.Param.TargetUnitData, aiAction.Param.UnitData);
if (selfDmg * 3 < otherDmg) return false;
return true;
}
if (type == CalculateType.LegionDevelopmentAttackUnit)
{
if (aiAction.Param.UnitData == null || aiAction.Param.TargetUnitData == null) return false;
var selfDmg = Table.Instance.CalcDamage(aiAction.Param.MapData, aiAction.Param.UnitData, aiAction.Param.TargetUnitData);
var otherDmg = Table.Instance.CalcDamage(aiAction.Param.MapData, aiAction.Param.TargetUnitData, aiAction.Param.UnitData);
if (selfDmg * 2 < otherDmg) return false;
return true;
}
}
return false;
}
private static CalculateResult CalculateAIActionScore(AICalculatorData data, CommonActionParams param, List<CalculateType> types)
{
var result = new CalculateResult();
foreach (var type in types)
{
if (type == CalculateType.PlayerTechDefend) CalculatePlayerTechDefend(data, data.TargetParam, result);
if (type == CalculateType.PlayerTechAttack) CalculatePlayerTechDefend(data, data.TargetParam, result);
if (type == CalculateType.PlayerTechScore) CalculatePlayerTechDefend(data, data.TargetParam, result);
if (type == CalculateType.CityLevelUpDefend) CalculateCityLevelUpDefend(data, data.TargetParam, result);
if (type == CalculateType.CityTrainDefend) CalculateCityTrainDefend(data, data.TargetParam, result);
if (type == CalculateType.CityTrainAttack) CalculateCityTrainAttack(data, data.TargetParam, result);
if (type == CalculateType.CityDevelopment) CalculateCityDevelopment(data, data.TargetParam, result);
if (type == CalculateType.LegionDefendKill) CalculateLegionDefendKill(data, data.TargetParam, result);
if (type == CalculateType.LegionDefendMove) CalculateLegionDefendMove(data, data.TargetParam, result);
if (type == CalculateType.LegionDefendAttack) CalculateLegionDefendAttack(data, data.TargetParam, result);
if (type == CalculateType.LegionDefendMoveForTrain) CalculateLegionDefendMoveForTrain(data, data.TargetParam, result);
if (type == CalculateType.UnitRecovery) CalculateUnitRecovery(data, data.TargetParam, result);
if (type == CalculateType.UnitAttackCityCenter) CalculateUnitAttackCityCenter(data, data.TargetParam, result);
if (type == CalculateType.UnitExplore) CalculateExplore(data, data.TargetParam, result);
if (type == CalculateType.UnitRetreat) CalculateUnitRetreat(data, data.TargetParam, result);
if (type == CalculateType.UnitMoveToTargetGrid) CalculateUnitMoveToTargetGrid(data, data.TargetParam, result);
if (type == CalculateType.LegionAttackMoveInCity) CalculateLegionAttackMoveInCity(data, data.TargetParam, result);
if (type == CalculateType.LegionAttackMoveToCity) CalculateLegionAttackMoveToCity(data, data.TargetParam, result);
if (type == CalculateType.LegionAttackCityUnit) CalculateLegionAttackCityUnit(data, data.TargetParam, result);
if (type == CalculateType.LegionAttackCityTerritoryUnit) CalculateLegionAttackCityTerritoryUnit(data, data.TargetParam, result);
if (type == CalculateType.LegionAttackUnit) CalculateLegionAttackUnit(data, data.TargetParam, result);
if (type == CalculateType.LegionDevelopmentMoveToCityTerritory) CalculateLegionDevelopmentMoveToCityTerritory(data, data.TargetParam, result);
if (type == CalculateType.LegionDevelopmentKill) CalculateLegionDevelopmentKill(data, data.TargetParam, result);
if (type == CalculateType.LegionDevelopmentMoveToCity) CalculateLegionDevelopmentMoveToCity(data, data.TargetParam, result);
if (type == CalculateType.LegionDevelopmentAttackUnit) CalculateLegionDevelopmentAttackUnit(data, data.TargetParam, result);
if (type == CalculateType.LegionDevelopmentMoveToOtherCity) CalculateLegionDevelopmentMoveToOtherCity(data, data.TargetParam, result);
}
return result;
}
private static void CalculateUnitAttackCityCenter(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
if (param.UnitData == null || param.TargetUnitData == null) return;
if (!param.MapData.GetGridDataByUnitId(param.TargetUnitData.Id, out var unitGrid)) return;
if (!param.MapData.GetCityDataByGid(unitGrid.Id, out var cityData)) return;
var selfCities = param.MapData.GetCityDataSetByPlayerId(param.PlayerData.Id);
if (!selfCities.Contains(cityData)) return;
result.Score[CalculateType.UnitAttackCityCenter] = 1f;
}
private static void CalculateLegionDevelopmentMoveToOtherCity(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
if (param.UnitData == null || param.GridData == null) return;
if (!param.MapData.GetGridDataByUnitId(param.UnitData.Id, out var unitGrid)) return;
var distance = param.MapData.GridMap.CalcDistance(param.GridData, unitGrid);
result.Score[CalculateType.LegionDevelopmentMoveToOtherCity] = 1f / (distance + 1);
}
private static void CalculateUnitRetreat(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
if (param.UnitData == null) return;
if (!data.UnitTargetGrid.ContainsKey(param.UnitData.Id)) return;
if (!param.MapData.GetGridDataByUnitId(param.UnitData.Id, out var unitGrid)) return;
var distance = param.MapData.GridMap.CalcDistance(data.UnitTargetGrid[param.UnitData.Id], unitGrid);
result.Score[CalculateType.UnitRetreat] = 1f / (distance + 1);
}
private static void CalculateExplore(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
if (param.UnitData == null || param.GridData == null) return;
if (!param.MapData.GetGridDataByUnitId(param.UnitData.Id, out var unitGrid)) return;
var distance = param.MapData.GridMap.CalcDistance(param.GridData, unitGrid);
result.Score[CalculateType.UnitExplore] = 1f / (distance + 1);
}
private static void CalculateUnitMoveToTargetGrid(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
if (param.UnitData == null || param.GridData == null) return;
if (!param.MapData.GetGridDataByUnitId(param.UnitData.Id, out var unitGrid)) return;
var distance = param.MapData.GridMap.CalcDistance(param.GridData, unitGrid);
result.Score[CalculateType.UnitMoveToTargetGrid] = 1f / (distance + 1);
}
private static void CalculateLegionDevelopmentAttackUnit(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
}
private static void CalculateLegionDevelopmentMoveToCity(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
if (param.UnitData == null || param.CityData == null) return;
if (!param.MapData.GetGridDataByUnitId(param.UnitData.Id, out var unitGrid)) return;
if (!param.MapData.GetGridDataByCityId(param.CityData.Id, out var cityGrid)) return;
var distance = param.MapData.GridMap.CalcDistance(cityGrid, unitGrid);
result.Score[CalculateType.LegionDevelopmentMoveToCity] = 1f / (distance + 1);
}
private static void CalculateLegionDevelopmentKill(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
var score = 0;
var selfUnits = new HashSet<UnitData>();
param.MapData.GetUnitDataListByPlayerId(param.PlayerData.Id, selfUnits);
foreach (var unit in param.MapData.UnitMap.UnitList)
{
if (selfUnits.Contains(unit)) continue;
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unit.UnitType, unit.GiantType, out var info)) continue;
score += info.Cost;
}
result.Score[CalculateType.LegionDevelopmentKill] = 1f / (score + 1);
}
private static void CalculateLegionDevelopmentMoveToCityTerritory(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
if (param.UnitData == null) return;
if (!param.MapData.GetGridDataByUnitId(param.UnitData.Id, out var grid)) return;
var set = param.MapData.GetPlayerTerritoryGridIdSet(param.PlayerData.Id);
if (!set.Contains(grid.Id)) return;
result.Score[CalculateType.LegionDevelopmentMoveToCityTerritory] = 1f;
}
private static void CalculateLegionAttackUnit(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
}
private static void CalculateLegionAttackOccupation(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
}
private static void CalculateLegionAttackCityTerritoryUnit(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
}
private static void CalculateLegionAttackCityUnit(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
}
private static void CalculateLegionAttackMoveToCity(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
if (param.UnitData.LegionId == 0) return;
if (!data.LegionTargetCity.TryGetValue(param.UnitData.LegionId, out var cityId)) return;
if (!param.MapData.GetGridDataByCityId(cityId, out var cityGrid)) return;
if (!param.MapData.GetGridDataByUnitId(param.UnitData.Id, out var unitGrid)) return;
if (param.MapData.GetUnitDataByGid(cityGrid.Id, out var cityUnit)) return;
var path = PathFinder.FindPath((int)param.MapData.MapConfig.Width, (int)param.MapData.MapConfig.Height,
new (unitGrid.Pos.X, unitGrid.Pos.Y), new (cityGrid.Pos.X, cityGrid.Pos.Y), param.MapData, param.PlayerData);
result.Score[CalculateType.UnitRecovery] = 1f / (path.length + 1);
}
private static void CalculateLegionAttackMoveInCity(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
if (param.UnitData.LegionId == 0) return;
if (!data.LegionTargetCity.TryGetValue(param.UnitData.LegionId, out var cityId)) return;
if (!param.MapData.GetGridDataByCityId(cityId, out var cityGrid)) return;
if (!param.MapData.GetGridDataByUnitId(param.UnitData.Id, out var unitGrid)) return;
if (cityGrid != unitGrid) return;
result.Score[CalculateType.LegionAttackMoveInCity] = 1f;
// if (param.UnitData.LegionId == 0) return;
// if (!data.LegionTargetCity.TryGetValue(param.UnitData.LegionId, out var cityId)) return;
// if (!param.MapData.CityMap.GetCityById(cityId, out var city)) return;
// if (!param.MapData.GetGridDataByUnitId(param.UnitData.Id, out var unitGrid)) return;
// if (!param.MapData.GetGridDataByUnitId(city.Id, out var cityGrid)) return;
// var path = PathFinder.FindPath((int)param.MapData.MapConfig.Width, (int)param.MapData.MapConfig.Height,
// new (unitGrid.Pos.X, unitGrid.Pos.Y), new (cityGrid.Pos.X, cityGrid.Pos.Y), param.MapData, param.PlayerData);
// result.Score[CalculateType.UnitRecovery] = 1f / (path.length + 1);
}
private static void CalculateLegionDefendMoveForTrain(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
if (param.UnitData.LegionId == 0) return;
if (!data.LegionTargetCity.TryGetValue(param.UnitData.LegionId, out var cityId)) return;
if (!param.MapData.CityMap.GetCityById(cityId, out var city)) return;
if (!param.MapData.GetGridDataByCityId(cityId, out var grid)) return;
if (param.MapData.GetUnitDataByGid(grid.Id, out var unit))
{
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unit.UnitType, unit.GiantType, out var info)) return;
result.Score[CalculateType.UnitRecovery] = info.Defense;
}
else
{
var actions = ActionLogicFactory.GetActionLogicByType(CommonActionType.TrainUnit);
if (actions == null || actions.Count == 0) return;
var newParam = new CommonActionParams();
newParam.MainObjectType = MainObjectType.City;
newParam.MapData = param.MapData;
newParam.PlayerData = param.PlayerData;
newParam.CityData = city;
foreach (var action in actions)
{
if (!action.CheckCan(newParam)) continue;
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(action.ActionId.UnitType, action.ActionId.GiantType, out var info)) return;
if (info.Defense > result.Score[CalculateType.UnitRecovery])
result.Score[CalculateType.UnitRecovery] = info.Defense;
}
}
}
private static void CalculateUnitRecovery(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
result.Score[CalculateType.UnitRecovery] = param.UnitData.Health;
}
private static void CalculateLegionDefendAttack(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
}
private static void CalculateLegionDefendMove(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
if (param.UnitData.LegionId == 0) return;
var targetCityId = data.LegionTargetCity[param.UnitData.LegionId];
if (!param.MapData.GetGridDataByCityId(targetCityId, out var targetGrid)) return;
if (!param.MapData.GetGridDataByUnitId(param.UnitData.Id, out var unitGrid)) return;
var path = PathFinder.FindPath((int)param.MapData.MapConfig.Width, (int)param.MapData.MapConfig.Height,
new (unitGrid.Pos.X, unitGrid.Pos.Y), new (targetGrid.Pos.X, targetGrid.Pos.Y), param.MapData, param.PlayerData);
var selfUnits = new HashSet<UnitData>();
param.MapData.GetUnitDataListByPlayerId(param.PlayerData.Id, selfUnits);
var around = param.MapData.GridMap.GetAroundGridData(1, 1, unitGrid);
var score = 0;
foreach (var aroundGrid in around)
{
if (!param.MapData.GetUnitDataByGid(aroundGrid.Id, out var aroundUnit)) continue;
if (!selfUnits.Contains(aroundUnit)) continue;
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(aroundUnit.UnitType, aroundUnit.GiantType, out var info)) continue;
score += info.Cost;
}
result.Score[CalculateType.LegionDefendMove] = 1 / ((float)path.length + 1) * 1000 + score / 1000f;
}
private static void CalculateLegionDefendKill(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
if (param.TargetUnitData == null) return;
if (param.TargetUnitData.Alive) return;
result.Score[CalculateType.LegionDefendKill] = 1;
}
private static void CalculateUnitCollect(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
}
private static void CalculateCityDevelopment(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
result.Score[CalculateType.CityDevelopment] = param.CityData.Level * 50 + param.CityData.LevelExp * 5 - param.PlayerData.PlayerWealth * 5;
}
private static void CalculateCityTrainAttack(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;
var units = new HashSet<UnitData>();
var targets = new HashSet<UnitData>();
var selfScore = 0;
var targetScore = 0;
foreach (var unit in param.MapData.UnitMap.UnitList)
{
if (!param.MapData.GetGridDataByUnitId(unit.Id, out var unitGrid)) continue;
if (param.MapData.GridMap.CalcDistance(cityGrid, unitGrid) > 3) continue;
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unit.UnitType, unit.GiantType, out var info))
continue;
if (selfUnits.Contains(unit))
{
units.Add(unit);
selfScore += info.Cost;
}
else
{
targets.Add(unit);
targetScore += info.Cost;
}
}
score = selfScore - targetScore;
foreach (var selfUnit in units)
{
foreach (var target in targets)
{
score += data.CalUnitCounterScore(selfUnit, target);
score -= data.CalUnitCounterScore(target, selfUnit);
}
}
result.Score[CalculateType.CityTrainAttack] = score;
}
public static void CalculatePlayerTechDefend(AICalculatorData data)
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;
foreach (var unit in selfUnits)
{
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 CalculateCityLevelUpDefend(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
result.Score[CalculateType.CityLevelUpDefend] = 0;
if (param.CityData.CityWall) result.Score[CalculateType.CityLevelUpDefend]++;
result.Score[CalculateType.CityLevelUpDefend]+= param.CityData.ParkCount;
}
private static void CalculatePlayerTechDefend(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
result.Score[CalculateType.PlayerTechDefend] = 0;
if (param.PlayerData.TechTree.CheckIfHasTech(TechType.Strategy))
result.Score[CalculateType.PlayerTechDefend] += 2;
if (param.PlayerData.TechTree.CheckIfHasTech(TechType.Organization))
result.Score[CalculateType.PlayerTechDefend] += 1;
}
private static void CalculatePlayerTechAttack(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
var score = 0;
foreach (var playerId in param.PlayerData.LastAttackPlayers)
{
if (!param.MapData.PlayerMap.GetPlayerDataByPlayerID(playerId, out var target)) continue;
score += CalculatePlayerTechGapScore(param.PlayerData, target);
}
result.Score[CalculateType.PlayerTechAttack] = score;
}
private static void CalculatePlayerTechScore(AICalculatorData data, CommonActionParams param, CalculateResult result)
{
var score = 0;
foreach (var target in param.MapData.PlayerMap.PlayerDataList)
{
if (target == param.PlayerData) continue;
score += CalculatePlayerTechGapScore(param.PlayerData, target);
}
result.Score[CalculateType.PlayerTechScore] = score;
}
private static int CalculatePlayerTechGapScore(PlayerData player1, PlayerData player2)
{
int player1Score = 0;
int player2Score = 0;
int gapScore = 0;
foreach (var tech in player1.TechTree.TechList)
{
var techInfo = Table.Instance.TechDataAssets.GetTechInfo(tech);
player1Score += techInfo.CostLevel;
}
foreach (var tech in player2.TechTree.TechList)
{
var techInfo = Table.Instance.TechDataAssets.GetTechInfo(tech);
player2Score += techInfo.CostLevel;
}
foreach (var tech1 in player1.TechTree.TechList)
{
foreach (var tech2 in player2.TechTree.TechList)
{
gapScore += CalculatePlayerTechGapScore(tech1, tech2);
gapScore -= CalculatePlayerTechGapScore(tech2, tech1);
}
}
return player1Score - player2Score + gapScore;
}
private static int CalculatePlayerTechGapScore(TechType techType1, TechType techType2)
{
if (techType1 == TechType.Archery && techType2 == TechType.Smithery) return 1;
if (techType1 == TechType.Strategy && techType2 == TechType.Archery) return 1;
if (techType1 == TechType.Strategy && techType2 == TechType.Riding) return 1;
if (techType1 == TechType.Strategy && techType2 == TechType.Chivalry) return 1;
if (techType1 == TechType.Riding && techType2 == TechType.Archery) return 1;
if (techType1 == TechType.Riding && techType2 == TechType.Mathematics) return 2;
if (techType1 == TechType.Riding && techType2 == TechType.Philosophy) return 2;
if (techType1 == TechType.Chivalry && techType2 == TechType.Archery) return 3;
if (techType1 == TechType.Chivalry && techType2 == TechType.Mathematics) return 3;
if (techType1 == TechType.Chivalry && techType2 == TechType.Philosophy) return 3;
if (techType1 == TechType.Mathematics && techType2 == TechType.Strategy) return 1;
if (techType1 == TechType.Mathematics && techType2 == TechType.Smithery) return 2;
if (techType1 == TechType.Smithery && techType2 == TechType.Riding) return 2;
if (techType1 == TechType.Smithery && techType2 == TechType.Chivalry) return 2;
if (techType1 == TechType.Philosophy && techType2 == TechType.Strategy) return 2;
return 0;
}
}
@ -595,7 +1087,8 @@ namespace Logic.AI
public int Distance;
}
public static (bool found, Vector2Int[] path, int length) FindPath(int width, int height, Vector2Int start, Vector2Int end, MapData mapData, PlayerData playerData)
public static (bool found, Vector2Int[] path, int length) FindPath(int width, int height, Vector2Int start, Vector2Int end,
MapData mapData, PlayerData playerData)
{
if (start == end) return(true, null, 0);
if (!IsValidPosition(width, height, start) || !IsValidPosition(width, height , end))

View File

@ -9,6 +9,8 @@
using System.Collections.Generic;
using System.Diagnostics;
using Logic.Action;
using NodeCanvas.BehaviourTrees;
using NodeCanvas.Framework;
using UnityEngine;
using RuntimeData;
using Debug = UnityEngine.Debug;
@ -37,29 +39,38 @@ namespace Logic.AI
public class AILogic
{
public AILogicState AILogicState;
private float _recordTime;
private AIActionScoreCalculator _scoreCalculator;
private AIActionGenerator _generator;
private List<AIActionBase> RecordActions;
private AIActionBase MaxScoreAction;
private MapData _mapData;
private PlayerData _playerData;
private AIConfigAsset _cfg;
private GameObject _logicObject;
private BehaviourTreeOwner _btOwner;
private AICalculatorData _data;
public AILogic()
{
AILogicState = AILogicState.Prepare;
RecordActions = new List<AIActionBase>();
_scoreCalculator = new AIActionScoreCalculator();
_cfg = Resources.Load<AIConfigAsset>("DataAssets/AIConfig");
_generator = new AIActionGenerator();
_logicObject = GameObject.Find("AIBT");
_btOwner = _logicObject.GetComponent<BehaviourTreeOwner>();
_data = new AICalculatorData();
var data = _btOwner.blackboard.GetVariable<AICalculatorData>("Data");
data.value = _data;
}
// 开始 AI 逻辑
public void StartAILogic(MapData mapData, PlayerData playerData)
{
@ -67,8 +78,9 @@ namespace Logic.AI
_mapData = mapData;
_playerData = playerData;
_generator.Init(_mapData, _playerData);
_data.Refresh(mapData, playerData);
}
// 结束 AI 逻辑
public void FinishAILogic()
{
@ -81,69 +93,97 @@ namespace Logic.AI
if (AILogicState == AILogicState.Finished || AILogicState == AILogicState.Prepare) return;
if (AILogicState == AILogicState.Pausing)
{
if (Time.time - _recordTime > DebugCenter.Instance.DebugAIActionTime) AILogicState = AILogicState.Playing;
if (Time.time - _recordTime > DebugCenter.Instance.DebugAIActionTime)
AILogicState = AILogicState.Playing;
}
if (AILogicState == AILogicState.Playing)
{
Stopwatch sw = new Stopwatch();
sw.Start();
RecordActions.Clear();
_generator.GeneratorOneStepActions(_mapData, _playerData, RecordActions);
CalculateMaxScoreAction();
if (sw.Elapsed.TotalMilliseconds > 20) Debug.Log($"{_generator.ActionType} 耗时:{sw.Elapsed.TotalMilliseconds} ms");
// AI 执行已结束
if (!GetAILogicPermission())
var index = 0;
while (true)
{
AILogicState = AILogicState.Finished;
}
// 无行动可执行
else if(MaxScoreAction == null)
{
AILogicState = AILogicState.Finished;
FinishAILogic();
}
// 执行行动
else
{
MaxScoreAction.Param.MapData = _mapData;
MaxScoreAction.Param.RefreshParams();
if (MainEditor.Instance.IsEditor && !MainEditor.Instance.IsGo) return;
index++;
_data.ClearCache();
sw.Start();
_btOwner.UpdateBehaviour();
if (sw.Elapsed.TotalMilliseconds > 20)
Debug.Log($"{_generator.ActionType} 耗时:{sw.Elapsed.TotalMilliseconds} ms");
MainEditor.Instance.IsGo = false;
if (MaxScoreAction.ActionLogic is UnitAttackAction)
{
_mapData.GetGridDataByUnitId(MaxScoreAction.Param.UnitData.Id, out var grid);
_mapData.GetGridDataByUnitId(MaxScoreAction.Param.TargetUnitData.Id, out var target);
Debug.Log($"小兵攻击 {MaxScoreAction.Param.UnitData.Id}, " +
$"位置{grid.Pos.X}, {grid.Pos.Y}, " +
$"目标 {MaxScoreAction.Param.TargetUnitData.Id}, " +
$"目标位置{target.Pos.X}, {target.Pos.Y}");
}
if (MaxScoreAction.ActionLogic is UnitMoveAction)
{
_mapData.GetGridDataByUnitId(MaxScoreAction.Param.UnitData.Id, out var grid);
Debug.Log($"小兵移动 {MaxScoreAction.Param.UnitData.Id}, " +
$"位置{grid.Pos.X}, {grid.Pos.Y}, " +
$"目标 {MaxScoreAction.Param.GridData.Pos.X}, {MaxScoreAction.Param.GridData.Pos.Y}");
}
MaxScoreAction.CheckIsActionInPlayerSight();
MaxScoreAction.ActionLogic.Execute(MaxScoreAction.Param);
AILogicState = AILogicState.Pausing;
_recordTime = Time.time;
if (!MaxScoreAction.IsInSight) _recordTime -= DebugCenter.Instance.DebugAIActionTime - 0.04f;
MaxScoreAction = null;
if (index > 20 || _data.IsExcute || _data.IsFinish) break;
}
if (_data.IsExcute)
{
AILogicState = AILogicState.Pausing;
if (!_data.IsInSight) _recordTime -= DebugCenter.Instance.DebugAIActionTime - 0.04f;
else _recordTime = Time.time;
}
else AILogicState = AILogicState.Finished;
}
// if (AILogicState == AILogicState.Playing)
// {
// Stopwatch sw = new Stopwatch();
// sw.Start();
// RecordActions.Clear();
// _generator.GeneratorOneStepActions(_mapData, _playerData, RecordActions);
// CalculateMaxScoreAction();
// if (sw.Elapsed.TotalMilliseconds > 20) Debug.Log($"{_generator.ActionType} 耗时:{sw.Elapsed.TotalMilliseconds} ms");
//
// // AI 执行已结束
// if (!GetAILogicPermission())
// {
// AILogicState = AILogicState.Finished;
// }
// // 无行动可执行
// else if(MaxScoreAction == null)
// {
// AILogicState = AILogicState.Finished;
// FinishAILogic();
// }
// // 执行行动
// else
// {
// MaxScoreAction.Param.MapData = _mapData;
// MaxScoreAction.Param.RefreshParams();
//
// if (MaxScoreAction.ActionLogic is UnitAttackAction)
// {
// _mapData.GetGridDataByUnitId(MaxScoreAction.Param.UnitData.Id, out var grid);
// _mapData.GetGridDataByUnitId(MaxScoreAction.Param.TargetUnitData.Id, out var target);
// Debug.Log($"小兵攻击 {MaxScoreAction.Param.UnitData.Id}, " +
// $"位置{grid.Pos.X}, {grid.Pos.Y}, " +
// $"目标 {MaxScoreAction.Param.TargetUnitData.Id}, " +
// $"目标位置{target.Pos.X}, {target.Pos.Y}");
// }
//
// if (MaxScoreAction.ActionLogic is UnitMoveAction)
// {
// _mapData.GetGridDataByUnitId(MaxScoreAction.Param.UnitData.Id, out var grid);
// Debug.Log($"小兵移动 {MaxScoreAction.Param.UnitData.Id}, " +
// $"位置{grid.Pos.X}, {grid.Pos.Y}, " +
// $"目标 {MaxScoreAction.Param.GridData.Pos.X}, {MaxScoreAction.Param.GridData.Pos.Y}");
// }
//
// MaxScoreAction.CheckIsActionInPlayerSight();
// MaxScoreAction.ActionLogic.Execute(MaxScoreAction.Param);
// AILogicState = AILogicState.Pausing;
// _recordTime = Time.time;
// if (!MaxScoreAction.IsInSight) _recordTime -= DebugCenter.Instance.DebugAIActionTime - 0.04f;
// MaxScoreAction = null;
// }
// }
}
// 获取 AI 执行权限
private bool GetAILogicPermission()
{
return true;
}
// 选取最高得分行动
private void CalculateMaxScoreAction()
{

View File

@ -101,6 +101,14 @@ namespace Logic.Action
TargetUnitData = target;
}
}
public void OnParamChanged()
{
if (PlayerData != null) PlayerId = PlayerData.Id;
if (UnitData != null) UnitId = UnitData.Id;
if (CityData != null) CityId = CityData.Id;
if (GridData != null) GridId = GridData.Id;
if (TargetUnitData != null) TargetUnitId = TargetUnitData.Id;
}
public CommonActionParams GetCopyParam()
{
@ -228,10 +236,10 @@ namespace Logic.Action
ActionLogicDict = new Dictionary<CommonActionId, ActionLogicBase>();
CommonActionId commonActionId;
// commonActionId = new CommonActionId { ActionType = CommonActionType.UnitMove };
// ActionLogicDict[commonActionId] = new UnitMoveAction(commonActionId);
// commonActionId = new CommonActionId { ActionType = CommonActionType.UnitAttack };
// ActionLogicDict[commonActionId] = new UnitAttackAction(commonActionId);
commonActionId = new CommonActionId { ActionType = CommonActionType.UnitMove };
ActionLogicDict[commonActionId] = new UnitMoveAction(commonActionId);
commonActionId = new CommonActionId { ActionType = CommonActionType.UnitAttack };
ActionLogicDict[commonActionId] = new UnitAttackAction(commonActionId);
foreach (ResourceType resourceType in System.Enum.GetValues(typeof(ResourceType)))
{

View File

@ -149,6 +149,7 @@ namespace Logic
// Update is called once per frame
void Update()
{
MainEditor.Instance.Update();
GameLogic.Update();
PlayerLogic.Update(MapData);
//先处理玩家输入

View File

@ -0,0 +1,38 @@
/*
* @Author:
* @Description:
* @Date: 20250618 15:06:25
* @Modify:
*/
using UnityEngine;
namespace Logic
{
public class MainEditor
{
public bool IsEditor = false;
public bool IsGo = false;
public static MainEditor Instance = new MainEditor();
public void Update()
{
if (Input.GetKeyDown(KeyCode.F10))
{
IsEditor = true;
Debug.LogWarning("F10");
}
if (Input.GetKeyDown(KeyCode.F11))
{
IsEditor = false;
Debug.LogWarning("F11");
}
if (Input.GetKeyDown(KeyCode.Space))
{
IsGo = true;
Debug.LogWarning("Space");
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ade4142431a947a385ca79f56e997f1e
timeCreated: 1750231509

View File

@ -30,7 +30,6 @@ namespace Logic.Multilingual
private void OnEnable()
{
if (Ban) return;
OnMultilingualChanged();

View File

@ -169,9 +169,9 @@ namespace Logic
continue;
if (!mapData.GetPlayerDataByUnitId(unit.Id, out var player1))
continue;
if (curPlayer.Sight.CheckIsInSight(grid.Id) && !curPlayer.MeetPlayerSet.Contains(player1.Id))
if (curPlayer.Sight.CheckIsInSight(grid.Id) && !curPlayer.MeetPlayers.Contains(player1.Id))
{
curPlayer.MeetPlayerSet.Add(player1.Id);
curPlayer.MeetPlayers.Add(player1.Id);
//如果是玩家出发ui提示。加钱的操作要在ui提示关闭的时候由ui来出发
if (curPlayer == mapData.PlayerMap.SelfPlayerData)
{

View File

@ -85,6 +85,8 @@ namespace Logic
if (!mapData.GetCityDataByUnitId(unit2.Id, out var city2)) return false;
if (!mapData.GetGridDataByUnitId(unit1.Id, out var grid1)) return false;
if (!mapData.GetGridDataByUnitId(unit2.Id, out var grid2)) return false;
player1.CurAttackPlayers.Add(player2.Id);
player2.CurAttackPlayers.Add(player1.Id);
mapData.OnUnitAttack(unit1, unit2);
unit1.BeforeAttack(mapData, unit2);
player1.TurnNoAttack = 0;

View File

@ -51,7 +51,7 @@ public class RankingUI
row.Find("Head3/Text").GetComponent<TextMeshProUGUI>().text = player.PlayerScore.ToString();
row.Find("Head1/TextGroup/Text1").GetComponent<TextMeshProUGUI>().color = Color.black;
if (!selfPlayer.MeetPlayerSet.Contains(player.Id))
if (!selfPlayer.MeetPlayers.Contains(player.Id))
{
forceName = "未知领袖";
civName = "带领 <未知文明>";