Improved naval placement check

Moved water checks before --waitTicks.
Use Water enum instead of multiple booleans.
Check for BaseProvider rather than BaseBuilding.
Move expensive ClosEnoughToWater check to last position for naval
production override.
This commit is contained in:
reaperrr
2015-07-26 13:45:20 +02:00
parent 76f2145db2
commit 91178d6f62
2 changed files with 47 additions and 28 deletions

View File

@@ -28,13 +28,21 @@ namespace OpenRA.Mods.Common.AI
int waitTicks; int waitTicks;
Actor[] playerBuildings; Actor[] playerBuildings;
bool waterAvailable;
bool checkedWater;
int failCount; int failCount;
int failRetryTicks; int failRetryTicks;
int checkForBasesTicks;
int cachedBases; int cachedBases;
int cachedBuildings; int cachedBuildings;
enum Water
{
NotChecked,
EnoughWater,
NotEnoughWater
}
Water waterState = Water.NotChecked;
public BaseBuilder(HackyAI ai, string category, Player p, PowerManager pm, PlayerResources pr) public BaseBuilder(HackyAI ai, string category, Player p, PowerManager pm, PlayerResources pr)
{ {
this.ai = ai; this.ai = ai;
@@ -55,7 +63,7 @@ namespace OpenRA.Mods.Common.AI
.Where(a => a.Actor.Owner == player) .Where(a => a.Actor.Owner == player)
.Count(); .Count();
var baseProviders = world.ActorsWithTrait<BaseBuilding>() var baseProviders = world.ActorsWithTrait<BaseProvider>()
.Where(a => a.Actor.Owner == player) .Where(a => a.Actor.Owner == player)
.Count(); .Count();
@@ -68,29 +76,34 @@ namespace OpenRA.Mods.Common.AI
failRetryTicks = ai.Info.StructureProductionResumeDelay; failRetryTicks = ai.Info.StructureProductionResumeDelay;
} }
// Only update once per second or so if (waterState == Water.NotChecked)
if (--waitTicks > 0)
return;
if (!checkedWater)
{ {
waterAvailable = ai.EnoughWaterToBuildNaval(); if (ai.EnoughWaterToBuildNaval())
checkedWater = true; waterState = Water.EnoughWater;
else
{
waterState = Water.NotEnoughWater;
checkForBasesTicks = ai.Info.CheckForNewBasesDelay;
}
} }
if (!waterAvailable) if (waterState == Water.NotEnoughWater && --checkForBasesTicks <= 0)
{ {
var currentBases = world.ActorsWithTrait<BaseBuilding>() var currentBases = world.ActorsWithTrait<BaseProvider>()
.Where(a => a.Actor.Owner == player) .Where(a => a.Actor.Owner == player)
.Count(); .Count();
if (currentBases > cachedBases) if (currentBases > cachedBases)
{ {
cachedBases = currentBases; cachedBases = currentBases;
checkedWater = false; waterState = Water.NotChecked;
} }
} }
// Only update once per second or so
if (--waitTicks > 0)
return;
playerBuildings = world.ActorsWithTrait<Building>() playerBuildings = world.ActorsWithTrait<Building>()
.Where(a => a.Actor.Owner == player) .Where(a => a.Actor.Owner == player)
.Select(a => a.Actor) .Select(a => a.Actor)
@@ -148,7 +161,7 @@ namespace OpenRA.Mods.Common.AI
.Where(a => a.Actor.Owner == player) .Where(a => a.Actor.Owner == player)
.Count(); .Count();
cachedBases = world.ActorsWithTrait<BaseBuilding>() cachedBases = world.ActorsWithTrait<BaseProvider>()
.Where(a => a.Actor.Owner == player) .Where(a => a.Actor.Owner == player)
.Count(); .Count();
} }
@@ -254,8 +267,9 @@ namespace OpenRA.Mods.Common.AI
} }
// Only consider building this if there is enough water inside the base perimeter and there are close enough adjacent buildings // Only consider building this if there is enough water inside the base perimeter and there are close enough adjacent buildings
if (waterAvailable && ai.CloseEnoughToWater() if (waterState == Water.EnoughWater && ai.Info.NewProductionCashThreshold > 0
&& ai.Info.NewProductionCashThreshold > 0 && playerResources.Resources > ai.Info.NewProductionCashThreshold) && playerResources.Resources > ai.Info.NewProductionCashThreshold
&& ai.CloseEnoughToWater())
{ {
var navalproduction = GetProducibleBuilding("NavalProduction", buildableThings); var navalproduction = GetProducibleBuilding("NavalProduction", buildableThings);
if (navalproduction != null && HasSufficientPowerForActor(navalproduction)) if (navalproduction != null && HasSufficientPowerForActor(navalproduction))
@@ -310,7 +324,7 @@ namespace OpenRA.Mods.Common.AI
// TODO: Extend this check to cover any naval structure, not just production. // TODO: Extend this check to cover any naval structure, not just production.
if (ai.Info.BuildingCommonNames.ContainsKey("NavalProduction") if (ai.Info.BuildingCommonNames.ContainsKey("NavalProduction")
&& ai.Info.BuildingCommonNames["NavalProduction"].Contains(name) && ai.Info.BuildingCommonNames["NavalProduction"].Contains(name)
&& (!waterAvailable || !ai.CloseEnoughToWater())) && (waterState == Water.NotEnoughWater || !ai.CloseEnoughToWater()))
continue; continue;
// Will this put us into low power? // Will this put us into low power?

View File

@@ -55,22 +55,27 @@ namespace OpenRA.Mods.Common.AI
[Desc("Minimum excess power the AI should try to maintain.")] [Desc("Minimum excess power the AI should try to maintain.")]
public readonly int MinimumExcessPower = 0; public readonly int MinimumExcessPower = 0;
[Desc("Minimum delay (in ticks) between structure production checks when there is no active production.")] [Desc("Delay (in ticks) between structure production checks when there is no active production.",
"A StructureProductionRandomBonusDelay is added to this.")]
public readonly int StructureProductionInactiveDelay = 125; public readonly int StructureProductionInactiveDelay = 125;
[Desc("Minimum delay (in ticks) between structure production checks when actively building things.")] [Desc("Delay (in ticks) between structure production checks when actively building things.",
"A StructureProductionRandomBonusDelay is added to this.")]
public readonly int StructureProductionActiveDelay = 10; public readonly int StructureProductionActiveDelay = 10;
[Desc("A random delay (in ticks) of up to this is added to production delays.")] [Desc("A random delay (in ticks) of up to this is added to active/inactive production delays.")]
public readonly int StructureProductionRandomBonusDelay = 10; public readonly int StructureProductionRandomBonusDelay = 10;
[Desc("How long to wait (in ticks) until retrying to build structure after the last 3 consecutive attempts failed.")] [Desc("Delay (in ticks) until retrying to build structure after the last 3 consecutive attempts failed.")]
public readonly int StructureProductionResumeDelay = 1500; public readonly int StructureProductionResumeDelay = 1500;
[Desc("After how many failed attempts to place a structure should AI give up and wait", [Desc("After how many failed attempts to place a structure should AI give up and wait",
"for StructureProductionResumeDelay before retrying.")] "for StructureProductionResumeDelay before retrying.")]
public readonly int MaximumFailedPlacementAttempts = 3; public readonly int MaximumFailedPlacementAttempts = 3;
[Desc("Delay (in ticks) until rechecking for new BaseProviders.")]
public readonly int CheckForNewBasesDelay = 1500;
[Desc("Minimum range at which to build defensive structures near a combat hotspot.")] [Desc("Minimum range at which to build defensive structures near a combat hotspot.")]
public readonly int MinimumDefenseRadius = 5; public readonly int MinimumDefenseRadius = 5;
@@ -268,20 +273,20 @@ namespace OpenRA.Mods.Common.AI
// TODO: Possibly give this a more generic name when terrain type is unhardcoded // TODO: Possibly give this a more generic name when terrain type is unhardcoded
public bool EnoughWaterToBuildNaval() public bool EnoughWaterToBuildNaval()
{ {
var baseBuildings = World.Actors.Where( var baseProviders = World.Actors.Where(
a => a.Owner == Player a => a.Owner == Player
&& a.HasTrait<BaseBuilding>() && a.HasTrait<BaseProvider>()
&& !a.HasTrait<Mobile>()); && !a.HasTrait<Mobile>());
foreach (var b in baseBuildings) foreach (var b in baseProviders)
{ {
// TODO: Unhardcode terrain type // TODO: Unhardcode terrain type
var playerWorld = Player.World; var playerWorld = Player.World;
var countWaterCells = Map.FindTilesInCircle(b.Location, Info.MaxBaseRadius) var countWaterCells = Map.FindTilesInCircle(b.Location, Info.MaxBaseRadius)
.Where(c => playerWorld.Map.Contains(c) .Where(c => playerWorld.Map.Contains(c)
&& playerWorld.Map.GetTerrainInfo(c).Type == "Water" && playerWorld.Map.GetTerrainInfo(c).IsWater
&& Util.AdjacentCells(playerWorld, Target.FromCell(playerWorld, c)) && Util.AdjacentCells(playerWorld, Target.FromCell(playerWorld, c))
.All(a => playerWorld.Map.GetTerrainInfo(a).Type == "Water")) .All(a => playerWorld.Map.GetTerrainInfo(a).IsWater))
.Count(); .Count();
if (countWaterCells > 0) if (countWaterCells > 0)
@@ -305,9 +310,9 @@ namespace OpenRA.Mods.Common.AI
var playerWorld = Player.World; var playerWorld = Player.World;
var adjacentWater = Map.FindTilesInCircle(a.Location, Info.CheckForWaterRadius) var adjacentWater = Map.FindTilesInCircle(a.Location, Info.CheckForWaterRadius)
.Where(c => playerWorld.Map.Contains(c) .Where(c => playerWorld.Map.Contains(c)
&& playerWorld.Map.GetTerrainInfo(c).Type == "Water" && playerWorld.Map.GetTerrainInfo(c).IsWater
&& Util.AdjacentCells(playerWorld, Target.FromCell(playerWorld, c)) && Util.AdjacentCells(playerWorld, Target.FromCell(playerWorld, c))
.All(b => playerWorld.Map.GetTerrainInfo(b).Type == "Water")) .All(b => playerWorld.Map.GetTerrainInfo(b).IsWater))
.Count(); .Count();
if (adjacentWater > 0) if (adjacentWater > 0)