diff --git a/OpenRA.Mods.Common/AI/AIUtils.cs b/OpenRA.Mods.Common/AI/AIUtils.cs new file mode 100644 index 0000000000..477f985bcb --- /dev/null +++ b/OpenRA.Mods.Common/AI/AIUtils.cs @@ -0,0 +1,51 @@ +#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.Collections.Generic; +using System.Linq; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.AI +{ + public enum BuildingType { Building, Defense, Refinery } + + public class CaptureTarget where TInfoType : class, ITraitInfoInterface + { + internal readonly Actor Actor; + internal readonly TInfoType Info; + + /// The order string given to the capturer so they can capture this actor. + /// ExternalCaptureActor + internal readonly string OrderString; + + internal CaptureTarget(Actor actor, string orderString) + { + Actor = actor; + Info = actor.Info.TraitInfoOrDefault(); + OrderString = orderString; + } + } + + public static class AIUtils + { + public static bool IsAreaAvailable(World world, Player player, Map map, int radius, HashSet terrainTypes) + { + var cells = world.ActorsHavingTrait().Where(a => a.Owner == player); + + // TODO: Properly check building foundation rather than 3x3 area. + return cells.Select(a => map.FindTilesInCircle(a.Location, radius) + .Count(c => map.Contains(c) && terrainTypes.Contains(map.GetTerrainInfo(c).Type) && + Util.AdjacentCells(world, Target.FromCell(world, c)) + .All(ac => terrainTypes.Contains(map.GetTerrainInfo(ac).Type)))) + .Any(availableCells => availableCells > 0); + } + } +} diff --git a/OpenRA.Mods.Common/AI/BaseBuilder.cs b/OpenRA.Mods.Common/AI/BaseBuilder.cs index aa17421c22..1774231633 100644 --- a/OpenRA.Mods.Common/AI/BaseBuilder.cs +++ b/OpenRA.Mods.Common/AI/BaseBuilder.cs @@ -77,7 +77,7 @@ namespace OpenRA.Mods.Common.AI if (waterState == Water.NotChecked) { - if (ai.IsAreaAvailable(ai.Info.MaxBaseRadius, ai.Info.WaterTerrainTypes)) + if (AIUtils.IsAreaAvailable(ai.World, ai.Player, ai.Map, ai.Info.MaxBaseRadius, ai.Info.WaterTerrainTypes)) waterState = Water.EnoughWater; else { @@ -259,7 +259,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 if (waterState == Water.EnoughWater && ai.Info.NewProductionCashThreshold > 0 && playerResources.Resources > ai.Info.NewProductionCashThreshold - && ai.IsAreaAvailable(ai.Info.CheckForWaterRadius, ai.Info.WaterTerrainTypes)) + && AIUtils.IsAreaAvailable(ai.World, ai.Player, ai.Map, ai.Info.CheckForWaterRadius, ai.Info.WaterTerrainTypes)) { var navalproduction = GetProducibleBuilding(ai.Info.BuildingCommonNames.NavalProduction, buildableThings); if (navalproduction != null && HasSufficientPowerForActor(navalproduction)) @@ -313,7 +313,8 @@ namespace OpenRA.Mods.Common.AI // and any structure providing buildable area close enough to that water. // TODO: Extend this check to cover any naval structure, not just production. if (ai.Info.BuildingCommonNames.NavalProduction.Contains(name) - && (waterState == Water.NotEnoughWater || !ai.IsAreaAvailable(ai.Info.CheckForWaterRadius, ai.Info.WaterTerrainTypes))) + && (waterState == Water.NotEnoughWater + || !AIUtils.IsAreaAvailable(ai.World, ai.Player, ai.Map, ai.Info.CheckForWaterRadius, ai.Info.WaterTerrainTypes))) continue; // Will this put us into low power? diff --git a/OpenRA.Mods.Common/AI/HackyAI.cs b/OpenRA.Mods.Common/AI/HackyAI.cs index 387fb4cfa2..67d469db34 100644 --- a/OpenRA.Mods.Common/AI/HackyAI.cs +++ b/OpenRA.Mods.Common/AI/HackyAI.cs @@ -21,23 +21,6 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.AI { - class CaptureTarget where TInfoType : class, ITraitInfoInterface - { - internal readonly Actor Actor; - internal readonly TInfoType Info; - - /// The order string given to the capturer so they can capture this actor. - /// ExternalCaptureActor - internal readonly string OrderString; - - internal CaptureTarget(Actor actor, string orderString) - { - Actor = actor; - Info = actor.Info.TraitInfoOrDefault(); - OrderString = orderString; - } - } - public sealed class HackyAIInfo : IBotInfo, ITraitInfo { public class UnitCategories @@ -264,8 +247,6 @@ namespace OpenRA.Mods.Common.AI public object Create(ActorInitializer init) { return new HackyAI(this, init); } } - public enum BuildingType { Building, Defense, Refinery } - public sealed class HackyAI : ITick, IBot, INotifyDamage { public MersenneTwister Random { get; private set; } @@ -390,18 +371,6 @@ namespace OpenRA.Mods.Common.AI resourceTypeIndices.Set(tileset.GetTerrainIndex(t.TerrainType), true); } - public bool IsAreaAvailable(int radius, HashSet terrainTypes) - { - var cells = World.ActorsHavingTrait().Where(a => a.Owner == Player); - - // TODO: Properly check building foundation rather than 3x3 area. - return cells.Select(a => Map.FindTilesInCircle(a.Location, radius) - .Count(c => Map.Contains(c) && terrainTypes.Contains(Map.GetTerrainInfo(c).Type) && - Util.AdjacentCells(World, Target.FromCell(World, c)) - .All(ac => terrainTypes.Contains(Map.GetTerrainInfo(ac).Type)))) - .Any(availableCells => availableCells > 0); - } - public void QueueOrder(Order order) { orders.Enqueue(order); diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index fe37cc87c9..60199610aa 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -122,6 +122,7 @@ +