Convert AIHarvesterManager into *Module
This commit is contained in:
@@ -1,94 +0,0 @@
|
|||||||
#region Copyright & License Information
|
|
||||||
/*
|
|
||||||
* Copyright 2007-2018 The OpenRA Developers (see AUTHORS)
|
|
||||||
* This file is part of OpenRA, which is free software. It is made
|
|
||||||
* available to you under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version. For more
|
|
||||||
* information, see COPYING.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using OpenRA.Mods.Common.Activities;
|
|
||||||
using OpenRA.Mods.Common.Pathfinder;
|
|
||||||
using OpenRA.Mods.Common.Traits;
|
|
||||||
using OpenRA.Support;
|
|
||||||
using OpenRA.Traits;
|
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.AI
|
|
||||||
{
|
|
||||||
class AIHarvesterManager
|
|
||||||
{
|
|
||||||
readonly HackyAI ai;
|
|
||||||
readonly World world;
|
|
||||||
readonly IPathFinder pathfinder;
|
|
||||||
readonly DomainIndex domainIndex;
|
|
||||||
readonly ResourceLayer resLayer;
|
|
||||||
readonly ResourceClaimLayer claimLayer;
|
|
||||||
|
|
||||||
public AIHarvesterManager(HackyAI ai, Player p)
|
|
||||||
{
|
|
||||||
this.ai = ai;
|
|
||||||
world = p.World;
|
|
||||||
pathfinder = world.WorldActor.Trait<IPathFinder>();
|
|
||||||
domainIndex = world.WorldActor.Trait<DomainIndex>();
|
|
||||||
resLayer = world.WorldActor.TraitOrDefault<ResourceLayer>();
|
|
||||||
claimLayer = world.WorldActor.TraitOrDefault<ResourceClaimLayer>();
|
|
||||||
}
|
|
||||||
|
|
||||||
CPos FindNextResource(Actor actor, Harvester harv)
|
|
||||||
{
|
|
||||||
var locomotorInfo = actor.Info.TraitInfo<MobileInfo>().LocomotorInfo;
|
|
||||||
|
|
||||||
Func<CPos, bool> isValidResource = cell =>
|
|
||||||
domainIndex.IsPassable(actor.Location, cell, locomotorInfo) &&
|
|
||||||
harv.CanHarvestCell(actor, cell) &&
|
|
||||||
claimLayer.CanClaimCell(actor, cell);
|
|
||||||
|
|
||||||
var path = pathfinder.FindPath(
|
|
||||||
PathSearch.Search(world, locomotorInfo, actor, true, isValidResource)
|
|
||||||
.WithCustomCost(loc => world.FindActorsInCircle(world.Map.CenterOfCell(loc), ai.Info.HarvesterEnemyAvoidanceRadius)
|
|
||||||
.Where(u => !u.IsDead && actor.Owner.Stances[u.Owner] == Stance.Enemy)
|
|
||||||
.Sum(u => Math.Max(WDist.Zero.Length, ai.Info.HarvesterEnemyAvoidanceRadius.Length - (world.Map.CenterOfCell(loc) - u.CenterPosition).Length)))
|
|
||||||
.FromPoint(actor.Location));
|
|
||||||
|
|
||||||
if (path.Count == 0)
|
|
||||||
return CPos.Zero;
|
|
||||||
|
|
||||||
return path[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Tick(List<Actor> harvesters)
|
|
||||||
{
|
|
||||||
if (resLayer == null || resLayer.IsResourceLayerEmpty)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Find idle harvesters and give them orders:
|
|
||||||
foreach (var harvester in harvesters)
|
|
||||||
{
|
|
||||||
var harv = harvester.Trait<Harvester>();
|
|
||||||
if (!harv.IsEmpty)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!harvester.IsIdle)
|
|
||||||
{
|
|
||||||
var act = harvester.CurrentActivity;
|
|
||||||
if (!harv.LastSearchFailed || act.NextActivity == null || act.NextActivity.GetType() != typeof(FindResources))
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var para = harvester.TraitOrDefault<Parachutable>();
|
|
||||||
if (para != null && para.IsInAir)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Tell the idle harvester to quit slacking:
|
|
||||||
var newSafeResourcePatch = FindNextResource(harvester, harv);
|
|
||||||
HackyAI.BotDebug("AI: Harvester {0} is idle. Ordering to {1} in search for new resources.".F(harvester, newSafeResourcePatch));
|
|
||||||
ai.QueueOrder(new Order("Harvest", harvester, Target.FromCell(world, newSafeResourcePatch), false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -281,7 +281,6 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
|
|
||||||
BitArray resourceTypeIndices;
|
BitArray resourceTypeIndices;
|
||||||
|
|
||||||
AIHarvesterManager harvManager;
|
|
||||||
AISupportPowerManager supportPowerManager;
|
AISupportPowerManager supportPowerManager;
|
||||||
|
|
||||||
List<BaseBuilder> builders = new List<BaseBuilder>();
|
List<BaseBuilder> builders = new List<BaseBuilder>();
|
||||||
@@ -291,10 +290,6 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
// Units that the ai already knows about. Any unit not on this list needs to be given a role.
|
// Units that the ai already knows about. Any unit not on this list needs to be given a role.
|
||||||
List<Actor> activeUnits = new List<Actor>();
|
List<Actor> activeUnits = new List<Actor>();
|
||||||
|
|
||||||
// Harvesters are usually listed under ExcludeFromSquads, so they're not included in the activeUnits list, but still needed in AIHarvesterManager.
|
|
||||||
// TODO: Consider adding an explicit UnitsCommonNames.Harvester category.
|
|
||||||
List<Actor> harvesters = new List<Actor>();
|
|
||||||
|
|
||||||
public const int FeedbackTime = 30; // ticks; = a bit over 1s. must be >= netlag.
|
public const int FeedbackTime = 30; // ticks; = a bit over 1s. must be >= netlag.
|
||||||
|
|
||||||
public readonly World World;
|
public readonly World World;
|
||||||
@@ -335,7 +330,6 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
playerResource = p.PlayerActor.Trait<PlayerResources>();
|
playerResource = p.PlayerActor.Trait<PlayerResources>();
|
||||||
botOrderManager = p.PlayerActor.Trait<BotOrderManager>();
|
botOrderManager = p.PlayerActor.Trait<BotOrderManager>();
|
||||||
|
|
||||||
harvManager = new AIHarvesterManager(this, p);
|
|
||||||
supportPowerManager = new AISupportPowerManager(this, p);
|
supportPowerManager = new AISupportPowerManager(this, p);
|
||||||
|
|
||||||
foreach (var building in Info.BuildingQueues)
|
foreach (var building in Info.BuildingQueues)
|
||||||
@@ -600,7 +594,6 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
|
|
||||||
activeUnits.RemoveAll(unitCannotBeOrdered);
|
activeUnits.RemoveAll(unitCannotBeOrdered);
|
||||||
unitsHangingAroundTheBase.RemoveAll(unitCannotBeOrdered);
|
unitsHangingAroundTheBase.RemoveAll(unitCannotBeOrdered);
|
||||||
harvesters.RemoveAll(unitCannotBeOrdered);
|
|
||||||
|
|
||||||
if (--rushTicks <= 0)
|
if (--rushTicks <= 0)
|
||||||
{
|
{
|
||||||
@@ -619,7 +612,6 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
{
|
{
|
||||||
assignRolesTicks = Info.AssignRolesInterval;
|
assignRolesTicks = Info.AssignRolesInterval;
|
||||||
FindNewUnits(self);
|
FindNewUnits(self);
|
||||||
harvManager.Tick(harvesters);
|
|
||||||
InitializeBase(self, true);
|
InitializeBase(self, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -723,13 +715,10 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
void FindNewUnits(Actor self)
|
void FindNewUnits(Actor self)
|
||||||
{
|
{
|
||||||
var newUnits = self.World.ActorsHavingTrait<IPositionable>()
|
var newUnits = self.World.ActorsHavingTrait<IPositionable>()
|
||||||
.Where(a => a.Owner == Player && !activeUnits.Contains(a) && !harvesters.Contains(a));
|
.Where(a => a.Owner == Player && !activeUnits.Contains(a));
|
||||||
|
|
||||||
foreach (var a in newUnits)
|
foreach (var a in newUnits)
|
||||||
{
|
{
|
||||||
if (a.Info.HasTraitInfo<HarvesterInfo>())
|
|
||||||
harvesters.Add(a);
|
|
||||||
|
|
||||||
if (Info.UnitsCommonNames.Mcv.Contains(a.Info.Name) || Info.UnitsCommonNames.ExcludeFromSquads.Contains(a.Info.Name))
|
if (Info.UnitsCommonNames.Mcv.Contains(a.Info.Name) || Info.UnitsCommonNames.ExcludeFromSquads.Contains(a.Info.Name))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
<Compile Include="AI\BaseBuilder.cs" />
|
<Compile Include="AI\BaseBuilder.cs" />
|
||||||
<Compile Include="AI\BotOrderManager.cs" />
|
<Compile Include="AI\BotOrderManager.cs" />
|
||||||
<Compile Include="AI\HackyAI.cs" />
|
<Compile Include="AI\HackyAI.cs" />
|
||||||
<Compile Include="AI\AIHarvesterManager.cs" />
|
<Compile Include="Traits\BotModules\HarvesterBotModule.cs" />
|
||||||
<Compile Include="AI\AISupportPowerManager.cs" />
|
<Compile Include="AI\AISupportPowerManager.cs" />
|
||||||
<Compile Include="AI\Squad.cs" />
|
<Compile Include="AI\Squad.cs" />
|
||||||
<Compile Include="AI\StateMachine.cs" />
|
<Compile Include="AI\StateMachine.cs" />
|
||||||
@@ -937,6 +937,7 @@
|
|||||||
<Compile Include="UpdateRules\Rules\20180923\AddRearmable.cs" />
|
<Compile Include="UpdateRules\Rules\20180923\AddRearmable.cs" />
|
||||||
<Compile Include="UpdateRules\Rules\20180923\MergeAttackPlaneAndHeli.cs" />
|
<Compile Include="UpdateRules\Rules\20180923\MergeAttackPlaneAndHeli.cs" />
|
||||||
<Compile Include="UpdateRules\Rules\20180923\AddBotOrderManager.cs" />
|
<Compile Include="UpdateRules\Rules\20180923\AddBotOrderManager.cs" />
|
||||||
|
<Compile Include="UpdateRules\Rules\20180923\AddHarvesterBotModule.cs" />
|
||||||
<Compile Include="Traits\Player\PlayerResources.cs" />
|
<Compile Include="Traits\Player\PlayerResources.cs" />
|
||||||
<Compile Include="UtilityCommands\DumpSequenceSheetsCommand.cs" />
|
<Compile Include="UtilityCommands\DumpSequenceSheetsCommand.cs" />
|
||||||
<Compile Include="Traits\Render\WithBuildingRepairDecoration.cs" />
|
<Compile Include="Traits\Render\WithBuildingRepairDecoration.cs" />
|
||||||
|
|||||||
133
OpenRA.Mods.Common/Traits/BotModules/HarvesterBotModule.cs
Normal file
133
OpenRA.Mods.Common/Traits/BotModules/HarvesterBotModule.cs
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
#region Copyright & License Information
|
||||||
|
/*
|
||||||
|
* Copyright 2007-2018 The OpenRA Developers (see AUTHORS)
|
||||||
|
* This file is part of OpenRA, which is free software. It is made
|
||||||
|
* available to you under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version. For more
|
||||||
|
* information, see COPYING.
|
||||||
|
*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using OpenRA.Mods.Common.Activities;
|
||||||
|
using OpenRA.Mods.Common.AI;
|
||||||
|
using OpenRA.Mods.Common.Pathfinder;
|
||||||
|
using OpenRA.Support;
|
||||||
|
using OpenRA.Traits;
|
||||||
|
|
||||||
|
namespace OpenRA.Mods.Common.Traits
|
||||||
|
{
|
||||||
|
[Desc("Put this on the Player actor. Manages bot harvesters to ensure they always continue harvesting as long as there are resources on the map.")]
|
||||||
|
public class HarvesterBotModuleInfo : ConditionalTraitInfo, Requires<BotOrderManagerInfo>
|
||||||
|
{
|
||||||
|
[Desc("Interval (in ticks) between giving out orders to idle harvesters.")]
|
||||||
|
public readonly int ScanForIdleHarvestersInterval = 20;
|
||||||
|
|
||||||
|
[Desc("Avoid enemy actors nearby when searching for a new resource patch. Should be somewhere near the max weapon range.")]
|
||||||
|
public readonly WDist HarvesterEnemyAvoidanceRadius = WDist.FromCells(8);
|
||||||
|
|
||||||
|
public override object Create(ActorInitializer init) { return new HarvesterBotModule(init.Self, this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HarvesterBotModule : ConditionalTrait<HarvesterBotModuleInfo>, ITick
|
||||||
|
{
|
||||||
|
readonly World world;
|
||||||
|
readonly Player player;
|
||||||
|
readonly Predicate<Actor> unitCannotBeOrdered;
|
||||||
|
IPathFinder pathfinder;
|
||||||
|
DomainIndex domainIndex;
|
||||||
|
ResourceLayer resLayer;
|
||||||
|
ResourceClaimLayer claimLayer;
|
||||||
|
BotOrderManager botOrderManager;
|
||||||
|
List<Actor> harvesters = new List<Actor>();
|
||||||
|
int scanForIdleHarvestersTicks;
|
||||||
|
|
||||||
|
public HarvesterBotModule(Actor self, HarvesterBotModuleInfo info)
|
||||||
|
: base(info)
|
||||||
|
{
|
||||||
|
world = self.World;
|
||||||
|
player = self.Owner;
|
||||||
|
unitCannotBeOrdered = a => a.Owner != self.Owner || a.IsDead || !a.IsInWorld;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void TraitEnabled(Actor self)
|
||||||
|
{
|
||||||
|
pathfinder = world.WorldActor.Trait<IPathFinder>();
|
||||||
|
domainIndex = world.WorldActor.Trait<DomainIndex>();
|
||||||
|
resLayer = world.WorldActor.TraitOrDefault<ResourceLayer>();
|
||||||
|
claimLayer = world.WorldActor.TraitOrDefault<ResourceClaimLayer>();
|
||||||
|
botOrderManager = self.Owner.PlayerActor.Trait<BotOrderManager>();
|
||||||
|
scanForIdleHarvestersTicks = Info.ScanForIdleHarvestersInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ITick.Tick(Actor self)
|
||||||
|
{
|
||||||
|
if (IsTraitDisabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (resLayer == null || resLayer.IsResourceLayerEmpty)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (--scanForIdleHarvestersTicks > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
harvesters.RemoveAll(unitCannotBeOrdered);
|
||||||
|
scanForIdleHarvestersTicks = Info.ScanForIdleHarvestersInterval;
|
||||||
|
|
||||||
|
// Find new harvesters
|
||||||
|
// TODO: Look for a more performance-friendly way to update this list
|
||||||
|
var newHarvesters = world.ActorsHavingTrait<Harvester>().Where(a => a.Owner == player && !harvesters.Contains(a));
|
||||||
|
foreach (var a in newHarvesters)
|
||||||
|
harvesters.Add(a);
|
||||||
|
|
||||||
|
// Find idle harvesters and give them orders:
|
||||||
|
foreach (var harvester in harvesters)
|
||||||
|
{
|
||||||
|
var harv = harvester.Trait<Harvester>();
|
||||||
|
if (!harv.IsEmpty)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!harvester.IsIdle)
|
||||||
|
{
|
||||||
|
var act = harvester.CurrentActivity;
|
||||||
|
if (!harv.LastSearchFailed || act.NextActivity == null || act.NextActivity.GetType() != typeof(FindResources))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var para = harvester.TraitOrDefault<Parachutable>();
|
||||||
|
if (para != null && para.IsInAir)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Tell the idle harvester to quit slacking:
|
||||||
|
var newSafeResourcePatch = FindNextResource(harvester, harv);
|
||||||
|
AIUtils.BotDebug("AI: Harvester {0} is idle. Ordering to {1} in search for new resources.".F(harvester, newSafeResourcePatch));
|
||||||
|
botOrderManager.QueueOrder(new Order("Harvest", harvester, Target.FromCell(world, newSafeResourcePatch), false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CPos FindNextResource(Actor actor, Harvester harv)
|
||||||
|
{
|
||||||
|
var locomotorInfo = actor.Info.TraitInfo<MobileInfo>().LocomotorInfo;
|
||||||
|
|
||||||
|
Func<CPos, bool> isValidResource = cell =>
|
||||||
|
domainIndex.IsPassable(actor.Location, cell, locomotorInfo) &&
|
||||||
|
harv.CanHarvestCell(actor, cell) &&
|
||||||
|
claimLayer.CanClaimCell(actor, cell);
|
||||||
|
|
||||||
|
var path = pathfinder.FindPath(
|
||||||
|
PathSearch.Search(world, locomotorInfo, actor, true, isValidResource)
|
||||||
|
.WithCustomCost(loc => world.FindActorsInCircle(world.Map.CenterOfCell(loc), Info.HarvesterEnemyAvoidanceRadius)
|
||||||
|
.Where(u => !u.IsDead && actor.Owner.Stances[u.Owner] == Stance.Enemy)
|
||||||
|
.Sum(u => Math.Max(WDist.Zero.Length, Info.HarvesterEnemyAvoidanceRadius.Length - (world.Map.CenterOfCell(loc) - u.CenterPosition).Length)))
|
||||||
|
.FromPoint(actor.Location));
|
||||||
|
|
||||||
|
if (path.Count == 0)
|
||||||
|
return CPos.Zero;
|
||||||
|
|
||||||
|
return path[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
#region Copyright & License Information
|
||||||
|
/*
|
||||||
|
* Copyright 2007-2018 The OpenRA Developers (see AUTHORS)
|
||||||
|
* This file is part of OpenRA, which is free software. It is made
|
||||||
|
* available to you under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version. For more
|
||||||
|
* information, see COPYING.
|
||||||
|
*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace OpenRA.Mods.Common.UpdateRules.Rules
|
||||||
|
{
|
||||||
|
public class AddHarvesterBotModule : UpdateRule
|
||||||
|
{
|
||||||
|
public override string Name { get { return "Split HackyAI harvester handling to HarvesterBotModule"; } }
|
||||||
|
public override string Description
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return "Some properties and all harvester handling have been moved from HackyAI\n" +
|
||||||
|
"to the new HarvesterBotModule.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool messageShown;
|
||||||
|
|
||||||
|
readonly string[] harvesterFields =
|
||||||
|
{
|
||||||
|
"HarvesterEnemyAvoidanceRadius", "AssignRolesInterval"
|
||||||
|
};
|
||||||
|
|
||||||
|
public override IEnumerable<string> AfterUpdate(ModData modData)
|
||||||
|
{
|
||||||
|
var message = "You may want to check your AI yamls for possible redundant module entries.\n" +
|
||||||
|
"Additionally, make sure the Player actor has the ConditionManager trait and add it manually if it doesn't.";
|
||||||
|
|
||||||
|
if (!messageShown)
|
||||||
|
yield return message;
|
||||||
|
|
||||||
|
messageShown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
|
||||||
|
{
|
||||||
|
if (actorNode.Key != "Player")
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
var hackyAIs = actorNode.ChildrenMatching("HackyAI");
|
||||||
|
if (!hackyAIs.Any())
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
var addNodes = new List<MiniYamlNode>();
|
||||||
|
|
||||||
|
// We add a 'default' HarvesterBotModule in any case,
|
||||||
|
// and only add more for AIs that define custom values for one of its fields.
|
||||||
|
var defaultHarvNode = new MiniYamlNode("HarvesterBotModule", "");
|
||||||
|
|
||||||
|
foreach (var hackyAINode in hackyAIs)
|
||||||
|
{
|
||||||
|
// HackyAIInfo.Name might contain spaces, so Type is better suited to be used as condition name
|
||||||
|
var aiType = hackyAINode.LastChildMatching("Type").NodeValue<string>();
|
||||||
|
var conditionString = "enable-" + aiType + "-ai";
|
||||||
|
var requiresCondition = new MiniYamlNode("RequiresCondition", conditionString);
|
||||||
|
var conditionNode = hackyAINode.LastChildMatching("Condition");
|
||||||
|
if (conditionNode == null)
|
||||||
|
{
|
||||||
|
var enableModule = new MiniYamlNode("Condition", conditionString);
|
||||||
|
hackyAINode.AddNode(enableModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (harvesterFields.Any(f => hackyAINode.ChildrenMatching(f).Any()))
|
||||||
|
{
|
||||||
|
var harvNode = new MiniYamlNode("HarvesterBotModule@" + aiType, "");
|
||||||
|
harvNode.AddNode(requiresCondition);
|
||||||
|
|
||||||
|
foreach (var hf in harvesterFields)
|
||||||
|
{
|
||||||
|
var fieldNode = hackyAINode.LastChildMatching(hf);
|
||||||
|
if (fieldNode != null)
|
||||||
|
{
|
||||||
|
if (hf == "AssignRolesInterval")
|
||||||
|
fieldNode.MoveAndRenameNode(hackyAINode, harvNode, "ScanForIdleHarvestersInterval");
|
||||||
|
else
|
||||||
|
fieldNode.MoveNode(hackyAINode, harvNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addNodes.Add(harvNode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We want the default module to be enabled for every AI that didn't customise one of its fields,
|
||||||
|
// so we need to update RequiresCondition to be enabled on any of the conditions granted by these AIs,
|
||||||
|
// but only if the condition hasn't been added yet.
|
||||||
|
var requiresConditionNode = defaultHarvNode.LastChildMatching("RequiresCondition");
|
||||||
|
if (requiresConditionNode == null)
|
||||||
|
defaultHarvNode.AddNode(requiresCondition);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var oldValue = requiresConditionNode.NodeValue<string>();
|
||||||
|
if (oldValue.Contains(conditionString))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
requiresConditionNode.ReplaceValue(oldValue + " || " + conditionString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addNodes.Add(defaultHarvNode);
|
||||||
|
|
||||||
|
foreach (var node in addNodes)
|
||||||
|
actorNode.AddNode(node);
|
||||||
|
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -106,6 +106,7 @@ namespace OpenRA.Mods.Common.UpdateRules
|
|||||||
new RequireProductionType(),
|
new RequireProductionType(),
|
||||||
new CloakRequiresConditionToPause(),
|
new CloakRequiresConditionToPause(),
|
||||||
new AddBotOrderManager(),
|
new AddBotOrderManager(),
|
||||||
|
new AddHarvesterBotModule(),
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ Player:
|
|||||||
Attractiveness: -10
|
Attractiveness: -10
|
||||||
TargetMetric: Value
|
TargetMetric: Value
|
||||||
CheckRadius: 7c0
|
CheckRadius: 7c0
|
||||||
|
Condition: enable-cabal-ai
|
||||||
HackyAI@Watson:
|
HackyAI@Watson:
|
||||||
Name: Watson
|
Name: Watson
|
||||||
Type: watson
|
Type: watson
|
||||||
@@ -262,6 +263,7 @@ Player:
|
|||||||
Attractiveness: -10
|
Attractiveness: -10
|
||||||
TargetMetric: Value
|
TargetMetric: Value
|
||||||
CheckRadius: 7c0
|
CheckRadius: 7c0
|
||||||
|
Condition: enable-watson-ai
|
||||||
HackyAI@HAL9001:
|
HackyAI@HAL9001:
|
||||||
Name: HAL 9001
|
Name: HAL 9001
|
||||||
Type: hal9001
|
Type: hal9001
|
||||||
@@ -395,3 +397,6 @@ Player:
|
|||||||
Attractiveness: -10
|
Attractiveness: -10
|
||||||
TargetMetric: Value
|
TargetMetric: Value
|
||||||
CheckRadius: 7c0
|
CheckRadius: 7c0
|
||||||
|
Condition: enable-hal9001-ai
|
||||||
|
HarvesterBotModule:
|
||||||
|
RequiresCondition: enable-cabal-ai || enable-watson-ai || enable-hal9001-ai
|
||||||
|
|||||||
@@ -58,3 +58,4 @@ Player:
|
|||||||
GrantConditionOnPrerequisiteManager:
|
GrantConditionOnPrerequisiteManager:
|
||||||
ResourceStorageWarning:
|
ResourceStorageWarning:
|
||||||
PlayerExperience:
|
PlayerExperience:
|
||||||
|
ConditionManager:
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ Player:
|
|||||||
OrderName: ProduceActorPower.Fremen
|
OrderName: ProduceActorPower.Fremen
|
||||||
Consideration@1:
|
Consideration@1:
|
||||||
Against: Ally
|
Against: Ally
|
||||||
|
Condition: enable-omnius-ai
|
||||||
HackyAI@Vidius:
|
HackyAI@Vidius:
|
||||||
Name: Vidious
|
Name: Vidious
|
||||||
Type: vidious
|
Type: vidious
|
||||||
@@ -250,6 +251,7 @@ Player:
|
|||||||
OrderName: ProduceActorPower.Fremen
|
OrderName: ProduceActorPower.Fremen
|
||||||
Consideration@1:
|
Consideration@1:
|
||||||
Against: Ally
|
Against: Ally
|
||||||
|
Condition: enable-vidious-ai
|
||||||
HackyAI@Gladius:
|
HackyAI@Gladius:
|
||||||
Name: Gladius
|
Name: Gladius
|
||||||
Type: gladius
|
Type: gladius
|
||||||
@@ -374,3 +376,6 @@ Player:
|
|||||||
OrderName: ProduceActorPower.Fremen
|
OrderName: ProduceActorPower.Fremen
|
||||||
Consideration@1:
|
Consideration@1:
|
||||||
Against: Ally
|
Against: Ally
|
||||||
|
Condition: enable-gladius-ai
|
||||||
|
HarvesterBotModule:
|
||||||
|
RequiresCondition: enable-omnius-ai || enable-vidious-ai || enable-gladius-ai
|
||||||
|
|||||||
@@ -144,3 +144,4 @@ Player:
|
|||||||
ResourceStorageWarning:
|
ResourceStorageWarning:
|
||||||
AdviceInterval: 26
|
AdviceInterval: 26
|
||||||
PlayerExperience:
|
PlayerExperience:
|
||||||
|
ConditionManager:
|
||||||
|
|||||||
@@ -105,6 +105,8 @@ Player:
|
|||||||
DefaultCashDropdownLocked: True
|
DefaultCashDropdownLocked: True
|
||||||
DefaultCashDropdownVisible: False
|
DefaultCashDropdownVisible: False
|
||||||
DefaultCash: 50
|
DefaultCash: 50
|
||||||
|
-ConditionManager:
|
||||||
|
-HarvesterBotModule:
|
||||||
-HackyAI@RushAI:
|
-HackyAI@RushAI:
|
||||||
-HackyAI@NormalAI:
|
-HackyAI@NormalAI:
|
||||||
-HackyAI@NavalAI:
|
-HackyAI@NavalAI:
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ Player:
|
|||||||
Attractiveness: -10
|
Attractiveness: -10
|
||||||
TargetMetric: Value
|
TargetMetric: Value
|
||||||
CheckRadius: 7c0
|
CheckRadius: 7c0
|
||||||
|
Condition: enable-rush-ai
|
||||||
HackyAI@NormalAI:
|
HackyAI@NormalAI:
|
||||||
Name: Normal AI
|
Name: Normal AI
|
||||||
Type: normal
|
Type: normal
|
||||||
@@ -261,6 +262,7 @@ Player:
|
|||||||
Attractiveness: -10
|
Attractiveness: -10
|
||||||
TargetMetric: Value
|
TargetMetric: Value
|
||||||
CheckRadius: 7c0
|
CheckRadius: 7c0
|
||||||
|
Condition: enable-normal-ai
|
||||||
HackyAI@TurtleAI:
|
HackyAI@TurtleAI:
|
||||||
Name: Turtle AI
|
Name: Turtle AI
|
||||||
Type: turtle
|
Type: turtle
|
||||||
@@ -402,6 +404,7 @@ Player:
|
|||||||
Attractiveness: -10
|
Attractiveness: -10
|
||||||
TargetMetric: Value
|
TargetMetric: Value
|
||||||
CheckRadius: 7c0
|
CheckRadius: 7c0
|
||||||
|
Condition: enable-turtle-ai
|
||||||
HackyAI@NavalAI:
|
HackyAI@NavalAI:
|
||||||
Name: Naval AI
|
Name: Naval AI
|
||||||
Type: naval
|
Type: naval
|
||||||
@@ -518,3 +521,6 @@ Player:
|
|||||||
Attractiveness: -10
|
Attractiveness: -10
|
||||||
TargetMetric: Value
|
TargetMetric: Value
|
||||||
CheckRadius: 7c0
|
CheckRadius: 7c0
|
||||||
|
Condition: enable-naval-ai
|
||||||
|
HarvesterBotModule:
|
||||||
|
RequiresCondition: enable-rush-ai || enable-normal-ai || enable-turtle-ai || enable-naval-ai
|
||||||
|
|||||||
@@ -143,3 +143,4 @@ Player:
|
|||||||
Sequence: veteran
|
Sequence: veteran
|
||||||
ResourceStorageWarning:
|
ResourceStorageWarning:
|
||||||
PlayerExperience:
|
PlayerExperience:
|
||||||
|
ConditionManager:
|
||||||
|
|||||||
@@ -78,3 +78,6 @@ Player:
|
|||||||
medic: 3
|
medic: 3
|
||||||
repair: 3
|
repair: 3
|
||||||
SquadSize: 20
|
SquadSize: 20
|
||||||
|
Condition: enable-test-ai
|
||||||
|
HarvesterBotModule:
|
||||||
|
RequiresCondition: enable-test-ai
|
||||||
|
|||||||
@@ -114,3 +114,4 @@ Player:
|
|||||||
Id: unrestricted
|
Id: unrestricted
|
||||||
ResourceStorageWarning:
|
ResourceStorageWarning:
|
||||||
PlayerExperience:
|
PlayerExperience:
|
||||||
|
ConditionManager:
|
||||||
|
|||||||
Reference in New Issue
Block a user