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:
@@ -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?
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user