HackyAI: Refactor and remove duplicated logic.

This commit is contained in:
Guido L
2018-01-14 15:22:37 +01:00
committed by reaperrr
parent c46a050da3
commit 21472f2cc6
2 changed files with 49 additions and 88 deletions

View File

@@ -75,7 +75,7 @@ namespace OpenRA.Mods.Common.AI
if (waterState == Water.NotChecked) if (waterState == Water.NotChecked)
{ {
if (ai.EnoughWaterToBuildNaval()) if (ai.IsAreaAvailable<BaseProvider>(ai.Info.MaxBaseRadius, ai.Info.WaterTerrainTypes))
waterState = Water.EnoughWater; waterState = Water.EnoughWater;
else else
{ {
@@ -255,7 +255,7 @@ 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 (waterState == Water.EnoughWater && ai.Info.NewProductionCashThreshold > 0 if (waterState == Water.EnoughWater && ai.Info.NewProductionCashThreshold > 0
&& playerResources.Resources > ai.Info.NewProductionCashThreshold && playerResources.Resources > ai.Info.NewProductionCashThreshold
&& ai.CloseEnoughToWater()) && ai.IsAreaAvailable<GivesBuildableArea>(ai.Info.CheckForWaterRadius, ai.Info.WaterTerrainTypes))
{ {
var navalproduction = GetProducibleBuilding(ai.Info.BuildingCommonNames.NavalProduction, buildableThings); var navalproduction = GetProducibleBuilding(ai.Info.BuildingCommonNames.NavalProduction, buildableThings);
if (navalproduction != null && HasSufficientPowerForActor(navalproduction)) if (navalproduction != null && HasSufficientPowerForActor(navalproduction))
@@ -309,7 +309,7 @@ namespace OpenRA.Mods.Common.AI
// and any structure providing buildable area close enough to that water. // and any structure providing buildable area close enough to that water.
// 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.NavalProduction.Contains(name) if (ai.Info.BuildingCommonNames.NavalProduction.Contains(name)
&& (waterState == Water.NotEnoughWater || !ai.CloseEnoughToWater())) && (waterState == Water.NotEnoughWater || !ai.IsAreaAvailable<GivesBuildableArea>(ai.Info.CheckForWaterRadius, ai.Info.WaterTerrainTypes)))
continue; continue;
// Will this put us into low power? // Will this put us into low power?

View File

@@ -381,52 +381,16 @@ namespace OpenRA.Mods.Common.AI
resourceTypeIndices.Set(tileset.GetTerrainIndex(t.TerrainType), true); resourceTypeIndices.Set(tileset.GetTerrainIndex(t.TerrainType), true);
} }
// TODO: Possibly give this a more generic name when terrain type is unhardcoded public bool IsAreaAvailable<T>(int radius, HashSet<string> terrainTypes)
public bool EnoughWaterToBuildNaval()
{ {
var baseProviders = World.ActorsHavingTrait<BaseProvider>() var cells = World.ActorsHavingTrait<T>().Where(a => a.Owner == Player);
.Where(a => a.Owner == Player);
foreach (var b in baseProviders) // TODO: Properly check building foundation rather than 3x3 area.
{ return cells.Select(a => Map.FindTilesInCircle(a.Location, radius)
// TODO: Properly check building foundation rather than 3x3 area .Count(c => Map.Contains(c) && terrainTypes.Contains(Map.GetTerrainInfo(c).Type) &&
var playerWorld = Player.World; Util.AdjacentCells(World, Target.FromCell(World, c))
var countWaterCells = Map.FindTilesInCircle(b.Location, Info.MaxBaseRadius) .All(ac => terrainTypes.Contains(Map.GetTerrainInfo(ac).Type))))
.Where(c => playerWorld.Map.Contains(c) .Any(availableCells => availableCells > 0);
&& Info.WaterTerrainTypes.Contains(playerWorld.Map.GetTerrainInfo(c).Type)
&& Util.AdjacentCells(playerWorld, Target.FromCell(playerWorld, c))
.All(a => Info.WaterTerrainTypes.Contains(playerWorld.Map.GetTerrainInfo(a).Type)))
.Count();
if (countWaterCells > 0)
return true;
}
return false;
}
// Check whether we have at least one building providing buildable area close enough to water to build naval structures
public bool CloseEnoughToWater()
{
var areaProviders = World.ActorsHavingTrait<GivesBuildableArea>()
.Where(a => a.Owner == Player);
foreach (var a in areaProviders)
{
// TODO: Properly check building foundation rather than 3x3 area
var playerWorld = Player.World;
var adjacentWater = Map.FindTilesInCircle(a.Location, Info.CheckForWaterRadius)
.Where(c => playerWorld.Map.Contains(c)
&& Info.WaterTerrainTypes.Contains(playerWorld.Map.GetTerrainInfo(c).Type)
&& Util.AdjacentCells(playerWorld, Target.FromCell(playerWorld, c))
.All(ac => Info.WaterTerrainTypes.Contains(playerWorld.Map.GetTerrainInfo(ac).Type)))
.Count();
if (adjacentWater > 0)
return true;
}
return false;
} }
public void QueueOrder(Order order) public void QueueOrder(Order order)
@@ -464,19 +428,19 @@ namespace OpenRA.Mods.Common.AI
return null; return null;
} }
int CountBuilding(string frac, Player owner) IEnumerable<Actor> GetActorsWithTrait<T>()
{ {
return World.ActorsHavingTrait<Building>().Count(a => a.Owner == owner && a.Info.Name == frac); return World.ActorsHavingTrait<T>();
} }
int CountUnits(string unit, Player owner) int CountActorsWithTrait<T>(string actorName, Player owner)
{ {
return World.ActorsHavingTrait<IPositionable>().Count(a => a.Owner == owner && a.Info.Name == unit); return GetActorsWithTrait<T>().Count(a => a.Owner == owner && a.Info.Name == actorName);
} }
int CountBuildingByCommonName(HashSet<string> buildings, Player owner) int CountBuildingByCommonName(HashSet<string> buildings, Player owner)
{ {
return World.ActorsHavingTrait<Building>() return GetActorsWithTrait<Building>()
.Count(a => a.Owner == owner && buildings.Contains(a.Info.Name)); .Count(a => a.Owner == owner && buildings.Contains(a.Info.Name));
} }
@@ -520,8 +484,8 @@ namespace OpenRA.Mods.Common.AI
var hasAmmoPool = actorInfo.TraitInfos<AmmoPoolInfo>().Any(); var hasAmmoPool = actorInfo.TraitInfos<AmmoPoolInfo>().Any();
if (hasAmmoPool && aircraftInfo.RearmBuildings.Count > 0) if (hasAmmoPool && aircraftInfo.RearmBuildings.Count > 0)
{ {
var countOwnAir = CountUnits(actorInfo.Name, Player); var countOwnAir = CountActorsWithTrait<IPositionable>(actorInfo.Name, Player);
var countBuildings = aircraftInfo.RearmBuildings.Sum(b => CountBuilding(b, Player)); var countBuildings = aircraftInfo.RearmBuildings.Sum(b => CountActorsWithTrait<Building>(b, Player));
if (countOwnAir >= countBuildings) if (countOwnAir >= countBuildings)
return false; return false;
} }
@@ -607,7 +571,7 @@ namespace OpenRA.Mods.Common.AI
ticks++; ticks++;
if (ticks == 1) if (ticks == 1)
InitializeBase(self); InitializeBase(self, false);
if (ticks % FeedbackTime == 0) if (ticks % FeedbackTime == 0)
ProductionUnits(self); ProductionUnits(self);
@@ -686,7 +650,7 @@ namespace OpenRA.Mods.Common.AI
if (resLayer != null && !resLayer.IsResourceLayerEmpty) if (resLayer != null && !resLayer.IsResourceLayerEmpty)
GiveOrdersToIdleHarvesters(); GiveOrdersToIdleHarvesters();
FindNewUnits(self); FindNewUnits(self);
FindAndDeployBackupMcv(self); InitializeBase(self, true);
} }
if (--minAttackForceDelayTicks <= 0) if (--minAttackForceDelayTicks <= 0)
@@ -995,50 +959,47 @@ namespace OpenRA.Mods.Common.AI
return possibleRallyPoints.Random(Random); return possibleRallyPoints.Random(Random);
} }
void InitializeBase(Actor self) void InitializeBase(Actor self, bool chooseLocation)
{ {
// Find and deploy our mcv var mcv = FindAndDeployMcv(self, chooseLocation);
var mcv = self.World.Actors.FirstOrDefault(a => a.Owner == Player &&
Info.UnitsCommonNames.Mcv.Contains(a.Info.Name)); if (mcv == null)
return;
if (mcv != null)
{
initialBaseCenter = mcv.Location; initialBaseCenter = mcv.Location;
defenseCenter = mcv.Location; defenseCenter = mcv.Location;
QueueOrder(new Order("DeployTransform", mcv, false));
}
else
BotDebug("AI: Can't find BaseBuildUnit.");
} }
// Find any newly constructed MCVs and deploy them at a sensible // Find any MCV and deploy them at a sensible location.
// backup location. Actor FindAndDeployMcv(Actor self, bool move)
void FindAndDeployBackupMcv(Actor self)
{ {
var mcvs = self.World.Actors.Where(a => a.Owner == Player && var mcv = self.World.Actors.FirstOrDefault(a => a.Owner == Player &&
Info.UnitsCommonNames.Mcv.Contains(a.Info.Name)); Info.UnitsCommonNames.Mcv.Contains(a.Info.Name) && a.IsIdle);
foreach (var mcv in mcvs) if (mcv == null)
{ return null;
if (!mcv.IsIdle)
continue;
// Don't try to move and deploy an undeployable actor // Don't try to move and deploy an undeployable actor
var transformsInfo = mcv.Info.TraitInfoOrDefault<TransformsInfo>(); var transformsInfo = mcv.Info.TraitInfoOrDefault<TransformsInfo>();
if (transformsInfo == null) if (transformsInfo == null)
continue; return null;
// If we lack a base, we need to make sure we don't restrict deployment of the MCV to the base! // If we lack a base, we need to make sure we don't restrict deployment of the MCV to the base!
var restrictToBase = Info.RestrictMCVDeploymentFallbackToBase && var restrictToBase = Info.RestrictMCVDeploymentFallbackToBase &&
CountBuildingByCommonName(Info.BuildingCommonNames.ConstructionYard, Player) > 0; CountBuildingByCommonName(Info.BuildingCommonNames.ConstructionYard, Player) > 0;
if (move)
{
var desiredLocation = ChooseBuildLocation(transformsInfo.IntoActor, restrictToBase, BuildingType.Building); var desiredLocation = ChooseBuildLocation(transformsInfo.IntoActor, restrictToBase, BuildingType.Building);
if (desiredLocation == null) if (desiredLocation == null)
continue; return null;
QueueOrder(new Order("Move", mcv, Target.FromCell(World, desiredLocation.Value), true)); QueueOrder(new Order("Move", mcv, Target.FromCell(World, desiredLocation.Value), true));
QueueOrder(new Order("DeployTransform", mcv, true));
} }
QueueOrder(new Order("DeployTransform", mcv, true));
return mcv;
} }
void TryToUseSupportPower(Actor self) void TryToUseSupportPower(Actor self)