Create proper data structures for hardcoded AI classes.
This commit is contained in:
@@ -170,12 +170,8 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ActorInfo GetProducibleBuilding(string commonName, IEnumerable<ActorInfo> buildables, Func<ActorInfo, int> orderBy = null)
|
ActorInfo GetProducibleBuilding(HashSet<string> actors, IEnumerable<ActorInfo> buildables, Func<ActorInfo, int> orderBy = null)
|
||||||
{
|
{
|
||||||
HashSet<string> actors;
|
|
||||||
if (!ai.Info.BuildingCommonNames.TryGetValue(commonName, out actors))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var available = buildables.Where(actor =>
|
var available = buildables.Where(actor =>
|
||||||
{
|
{
|
||||||
// Are we able to build this?
|
// Are we able to build this?
|
||||||
@@ -205,7 +201,7 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
var buildableThings = queue.BuildableItems();
|
var buildableThings = queue.BuildableItems();
|
||||||
|
|
||||||
// This gets used quite a bit, so let's cache it here
|
// This gets used quite a bit, so let's cache it here
|
||||||
var power = GetProducibleBuilding("Power", buildableThings,
|
var power = GetProducibleBuilding(ai.Info.BuildingCommonNames.Power, buildableThings,
|
||||||
a => a.TraitInfos<PowerInfo>().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(p => p.Amount));
|
a => a.TraitInfos<PowerInfo>().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(p => p.Amount));
|
||||||
|
|
||||||
// First priority is to get out of a low power situation
|
// First priority is to get out of a low power situation
|
||||||
@@ -221,7 +217,7 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
// Next is to build up a strong economy
|
// Next is to build up a strong economy
|
||||||
if (!ai.HasAdequateProc() || !ai.HasMinimumProc())
|
if (!ai.HasAdequateProc() || !ai.HasMinimumProc())
|
||||||
{
|
{
|
||||||
var refinery = GetProducibleBuilding("Refinery", buildableThings);
|
var refinery = GetProducibleBuilding(ai.Info.BuildingCommonNames.Refinery, buildableThings);
|
||||||
if (refinery != null && HasSufficientPowerForActor(refinery))
|
if (refinery != null && HasSufficientPowerForActor(refinery))
|
||||||
{
|
{
|
||||||
HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (refinery)", queue.Actor.Owner, refinery.Name);
|
HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (refinery)", queue.Actor.Owner, refinery.Name);
|
||||||
@@ -238,7 +234,7 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
// Make sure that we can spend as fast as we are earning
|
// Make sure that we can spend as fast as we are earning
|
||||||
if (ai.Info.NewProductionCashThreshold > 0 && playerResources.Resources > ai.Info.NewProductionCashThreshold)
|
if (ai.Info.NewProductionCashThreshold > 0 && playerResources.Resources > ai.Info.NewProductionCashThreshold)
|
||||||
{
|
{
|
||||||
var production = GetProducibleBuilding("Production", buildableThings);
|
var production = GetProducibleBuilding(ai.Info.BuildingCommonNames.Production, buildableThings);
|
||||||
if (production != null && HasSufficientPowerForActor(production))
|
if (production != null && HasSufficientPowerForActor(production))
|
||||||
{
|
{
|
||||||
HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (production)", queue.Actor.Owner, production.Name);
|
HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (production)", queue.Actor.Owner, production.Name);
|
||||||
@@ -257,7 +253,7 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
&& playerResources.Resources > ai.Info.NewProductionCashThreshold
|
&& playerResources.Resources > ai.Info.NewProductionCashThreshold
|
||||||
&& ai.CloseEnoughToWater())
|
&& ai.CloseEnoughToWater())
|
||||||
{
|
{
|
||||||
var navalproduction = GetProducibleBuilding("NavalProduction", buildableThings);
|
var navalproduction = GetProducibleBuilding(ai.Info.BuildingCommonNames.NavalProduction, buildableThings);
|
||||||
if (navalproduction != null && HasSufficientPowerForActor(navalproduction))
|
if (navalproduction != null && HasSufficientPowerForActor(navalproduction))
|
||||||
{
|
{
|
||||||
HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (navalproduction)", queue.Actor.Owner, navalproduction.Name);
|
HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (navalproduction)", queue.Actor.Owner, navalproduction.Name);
|
||||||
@@ -274,7 +270,7 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
// Create some head room for resource storage if we really need it
|
// Create some head room for resource storage if we really need it
|
||||||
if (playerResources.Resources > 0.8 * playerResources.ResourceCapacity)
|
if (playerResources.Resources > 0.8 * playerResources.ResourceCapacity)
|
||||||
{
|
{
|
||||||
var silo = GetProducibleBuilding("Silo", buildableThings);
|
var silo = GetProducibleBuilding(ai.Info.BuildingCommonNames.Silo, buildableThings);
|
||||||
if (silo != null && HasSufficientPowerForActor(silo))
|
if (silo != null && HasSufficientPowerForActor(silo))
|
||||||
{
|
{
|
||||||
HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (silo)", queue.Actor.Owner, silo.Name);
|
HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (silo)", queue.Actor.Owner, silo.Name);
|
||||||
@@ -308,8 +304,7 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
// If we're considering to build a naval structure, check whether there is enough water inside the base perimeter
|
// If we're considering to build a naval structure, check whether there is enough water inside the base perimeter
|
||||||
// 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.ContainsKey("NavalProduction")
|
if (ai.Info.BuildingCommonNames.NavalProduction.Contains(name)
|
||||||
&& ai.Info.BuildingCommonNames["NavalProduction"].Contains(name)
|
|
||||||
&& (waterState == Water.NotEnoughWater || !ai.CloseEnoughToWater()))
|
&& (waterState == Water.NotEnoughWater || !ai.CloseEnoughToWater()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,23 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
{
|
{
|
||||||
public sealed class HackyAIInfo : IBotInfo, ITraitInfo
|
public sealed class HackyAIInfo : IBotInfo, ITraitInfo
|
||||||
{
|
{
|
||||||
|
public class UnitCategories
|
||||||
|
{
|
||||||
|
public readonly HashSet<string> Mcv = new HashSet<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BuildingCategories
|
||||||
|
{
|
||||||
|
public readonly HashSet<string> ConstructionYard = new HashSet<string>();
|
||||||
|
public readonly HashSet<string> VehiclesFactory = new HashSet<string>();
|
||||||
|
public readonly HashSet<string> Refinery = new HashSet<string>();
|
||||||
|
public readonly HashSet<string> Power = new HashSet<string>();
|
||||||
|
public readonly HashSet<string> Barracks = new HashSet<string>();
|
||||||
|
public readonly HashSet<string> Production = new HashSet<string>();
|
||||||
|
public readonly HashSet<string> NavalProduction = new HashSet<string>();
|
||||||
|
public readonly HashSet<string> Silo = new HashSet<string>();
|
||||||
|
}
|
||||||
|
|
||||||
[Desc("Ingame name this bot uses.")]
|
[Desc("Ingame name this bot uses.")]
|
||||||
public readonly string Name = "Unnamed Bot";
|
public readonly string Name = "Unnamed Bot";
|
||||||
|
|
||||||
@@ -133,11 +150,13 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
public readonly Dictionary<string, float> BuildingFractions = null;
|
public readonly Dictionary<string, float> BuildingFractions = null;
|
||||||
|
|
||||||
[Desc("Tells the AI what unit types fall under the same common name. Only supported entry is Mcv.")]
|
[Desc("Tells the AI what unit types fall under the same common name. Only supported entry is Mcv.")]
|
||||||
public readonly Dictionary<string, HashSet<string>> UnitsCommonNames = null;
|
[FieldLoader.LoadUsing("LoadUnitCategories", true)]
|
||||||
|
public readonly UnitCategories UnitsCommonNames;
|
||||||
|
|
||||||
[Desc("Tells the AI what building types fall under the same common name.",
|
[Desc("Tells the AI what building types fall under the same common name.",
|
||||||
"Possible keys are ConstructionYard, Power, Refinery, Silo , Barracks, Production, VehiclesFactory, NavalProduction.")]
|
"Possible keys are ConstructionYard, Power, Refinery, Silo , Barracks, Production, VehiclesFactory, NavalProduction.")]
|
||||||
public readonly Dictionary<string, HashSet<string>> BuildingCommonNames = null;
|
[FieldLoader.LoadUsing("LoadBuildingCategories", true)]
|
||||||
|
public readonly BuildingCategories BuildingCommonNames;
|
||||||
|
|
||||||
[Desc("What buildings should the AI have a maximum limit to build.")]
|
[Desc("What buildings should the AI have a maximum limit to build.")]
|
||||||
public readonly Dictionary<string, int> BuildingLimits = null;
|
public readonly Dictionary<string, int> BuildingLimits = null;
|
||||||
@@ -147,6 +166,18 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
[FieldLoader.LoadUsing("LoadDecisions")]
|
[FieldLoader.LoadUsing("LoadDecisions")]
|
||||||
public readonly List<SupportPowerDecision> PowerDecisions = new List<SupportPowerDecision>();
|
public readonly List<SupportPowerDecision> PowerDecisions = new List<SupportPowerDecision>();
|
||||||
|
|
||||||
|
static object LoadUnitCategories(MiniYaml yaml)
|
||||||
|
{
|
||||||
|
var categories = yaml.Nodes.First(n => n.Key == "UnitsCommonNames");
|
||||||
|
return FieldLoader.Load<UnitCategories>(categories.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static object LoadBuildingCategories(MiniYaml yaml)
|
||||||
|
{
|
||||||
|
var categories = yaml.Nodes.First(n => n.Key == "BuildingCommonNames");
|
||||||
|
return FieldLoader.Load<BuildingCategories>(categories.Value);
|
||||||
|
}
|
||||||
|
|
||||||
static object LoadDecisions(MiniYaml yaml)
|
static object LoadDecisions(MiniYaml yaml)
|
||||||
{
|
{
|
||||||
var ret = new List<SupportPowerDecision>();
|
var ret = new List<SupportPowerDecision>();
|
||||||
@@ -378,57 +409,38 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
return World.ActorsHavingTrait<IPositionable>().Count(a => a.Owner == owner && a.Info.Name == unit);
|
return World.ActorsHavingTrait<IPositionable>().Count(a => a.Owner == owner && a.Info.Name == unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
int? CountBuildingByCommonName(string commonName, Player owner)
|
int CountBuildingByCommonName(HashSet<string> buildings, Player owner)
|
||||||
{
|
{
|
||||||
if (!Info.BuildingCommonNames.ContainsKey(commonName))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return World.ActorsHavingTrait<Building>()
|
return World.ActorsHavingTrait<Building>()
|
||||||
.Count(a => a.Owner == owner && Info.BuildingCommonNames[commonName].Contains(a.Info.Name));
|
.Count(a => a.Owner == owner && buildings.Contains(a.Info.Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActorInfo GetBuildingInfoByCommonName(string commonName, Player owner)
|
public ActorInfo GetInfoByCommonName(HashSet<string> names, Player owner)
|
||||||
{
|
{
|
||||||
if (commonName == "ConstructionYard")
|
return Map.Rules.Actors.Where(k => names.Contains(k.Key)).Random(Random).Value;
|
||||||
return Map.Rules.Actors.Where(k => Info.BuildingCommonNames[commonName].Contains(k.Key)).Random(Random).Value;
|
|
||||||
|
|
||||||
return GetInfoByCommonName(Info.BuildingCommonNames, commonName, owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActorInfo GetUnitInfoByCommonName(string commonName, Player owner)
|
|
||||||
{
|
|
||||||
return GetInfoByCommonName(Info.UnitsCommonNames, commonName, owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActorInfo GetInfoByCommonName(Dictionary<string, HashSet<string>> names, string commonName, Player owner)
|
|
||||||
{
|
|
||||||
if (!names.Any() || !names.ContainsKey(commonName))
|
|
||||||
throw new InvalidOperationException("Can't find {0} in the HackyAI UnitsCommonNames definition.".F(commonName));
|
|
||||||
|
|
||||||
return Map.Rules.Actors.Where(k => names[commonName].Contains(k.Key)).Random(Random).Value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasAdequateFact()
|
public bool HasAdequateFact()
|
||||||
{
|
{
|
||||||
// Require at least one construction yard, unless we have no vehicles factory (can't build it).
|
// Require at least one construction yard, unless we have no vehicles factory (can't build it).
|
||||||
return CountBuildingByCommonName("ConstructionYard", Player) > 0 ||
|
return CountBuildingByCommonName(Info.BuildingCommonNames.ConstructionYard, Player) > 0 ||
|
||||||
CountBuildingByCommonName("VehiclesFactory", Player) == 0;
|
CountBuildingByCommonName(Info.BuildingCommonNames.VehiclesFactory, Player) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasAdequateProc()
|
public bool HasAdequateProc()
|
||||||
{
|
{
|
||||||
// Require at least one refinery, unless we have no power (can't build it).
|
// Require at least one refinery, unless we have no power (can't build it).
|
||||||
return CountBuildingByCommonName("Refinery", Player) > 0 ||
|
return CountBuildingByCommonName(Info.BuildingCommonNames.Refinery, Player) > 0 ||
|
||||||
CountBuildingByCommonName("Power", Player) == 0;
|
CountBuildingByCommonName(Info.BuildingCommonNames.Power, Player) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasMinimumProc()
|
public bool HasMinimumProc()
|
||||||
{
|
{
|
||||||
// Require at least two refineries, unless we have no power (can't build it)
|
// Require at least two refineries, unless we have no power (can't build it)
|
||||||
// or barracks (higher priority?)
|
// or barracks (higher priority?)
|
||||||
return CountBuildingByCommonName("Refinery", Player) >= 2 ||
|
return CountBuildingByCommonName(Info.BuildingCommonNames.Refinery, Player) >= 2 ||
|
||||||
CountBuildingByCommonName("Power", Player) == 0 ||
|
CountBuildingByCommonName(Info.BuildingCommonNames.Power, Player) == 0 ||
|
||||||
CountBuildingByCommonName("Barracks", Player) == 0;
|
CountBuildingByCommonName(Info.BuildingCommonNames.Barracks, Player) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For mods like RA (number of building must match the number of aircraft)
|
// For mods like RA (number of building must match the number of aircraft)
|
||||||
@@ -1014,7 +1026,7 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
// No construction yards - Build a new MCV
|
// No construction yards - Build a new MCV
|
||||||
if (!HasAdequateFact() && !self.World.ActorsHavingTrait<BaseBuilding>()
|
if (!HasAdequateFact() && !self.World.ActorsHavingTrait<BaseBuilding>()
|
||||||
.Any(a => a.Owner == Player && a.Info.HasTraitInfo<MobileInfo>()))
|
.Any(a => a.Owner == Player && a.Info.HasTraitInfo<MobileInfo>()))
|
||||||
BuildUnit("Vehicle", GetUnitInfoByCommonName("Mcv", Player).Name);
|
BuildUnit("Vehicle", GetInfoByCommonName(Info.UnitsCommonNames.Mcv, Player).Name);
|
||||||
|
|
||||||
foreach (var q in Info.UnitQueues)
|
foreach (var q in Info.UnitQueues)
|
||||||
BuildUnit(q, unitsHangingAroundTheBase.Count < Info.IdleBaseUnitsMaximum);
|
BuildUnit(q, unitsHangingAroundTheBase.Count < Info.IdleBaseUnitsMaximum);
|
||||||
|
|||||||
Reference in New Issue
Block a user