From 6ff394991da0830a8b2bebf483cae450c71dd08b Mon Sep 17 00:00:00 2001 From: reaperrr Date: Tue, 21 Jul 2015 07:24:53 +0200 Subject: [PATCH] Fixed AI BaseBuilder low power check. This was extremely borked: The priority overrides for refinery, production and silo ignored power completely and never checked whether the structure might drive the AI into low power state; whereas the check for regular building checked whether the structure's power -exceeded- MinimumExcessPower (previously just 0). The catch is that power draw is represented by negative numbers, so the only buildings that would trigger this were - power plants. Now it checks whether the sum of building power draw (negative) and AI's current power level are lower than MinimumExcessPower, if not, build the structure, if yes, build a power plant instead. Additionally, this check is now performed for the early-game priority overrides as well. Last but not least the AI would not(!) give power plants priority if it was already low on power. This has been fixed as well. --- OpenRA.Mods.Common/AI/BaseBuilder.cs | 58 ++++++++++++++++++---------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/OpenRA.Mods.Common/AI/BaseBuilder.cs b/OpenRA.Mods.Common/AI/BaseBuilder.cs index e70fa4656e..97edb93780 100644 --- a/OpenRA.Mods.Common/AI/BaseBuilder.cs +++ b/OpenRA.Mods.Common/AI/BaseBuilder.cs @@ -130,22 +130,27 @@ namespace OpenRA.Mods.Common.AI return available.RandomOrDefault(ai.Random); } + bool HasSufficientPowerForActor(ActorInfo actorInfo) + { + return (actorInfo.Traits.WithInterface().Where(i => i.UpgradeMinEnabledLevel < 1) + .Sum(p => p.Amount) + playerPower.ExcessPower) >= ai.Info.MinimumExcessPower; + } + ActorInfo ChooseBuildingToBuild(ProductionQueue queue) { var buildableThings = queue.BuildableItems(); + // This gets used quite a bit, so let's cache it here + var power = GetProducibleBuilding("Power", buildableThings, + a => a.Traits.WithInterface().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(p => p.Amount)); + // First priority is to get out of a low power situation if (playerPower.ExcessPower < ai.Info.MinimumExcessPower) { - var power = GetProducibleBuilding("Power", buildableThings, a => a.Traits.WithInterface().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(p => p.Amount)); if (power != null && power.Traits.WithInterface().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(p => p.Amount) > 0) { - // TODO: Handle the case when of when we actually do need a power plant because we don't have enough but are also suffering from a power outage - if (playerPower.PowerOutageRemainingTicks <= 0) - { - HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (low power)", queue.Actor.Owner, power.Name); - return power; - } + HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (low power)", queue.Actor.Owner, power.Name); + return power; } } @@ -153,33 +158,51 @@ namespace OpenRA.Mods.Common.AI if (!ai.HasAdequateProc() || !ai.HasMinimumProc()) { var refinery = GetProducibleBuilding("Refinery", buildableThings); - if (refinery != null) + if (refinery != null && HasSufficientPowerForActor(refinery)) { HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (refinery)", queue.Actor.Owner, refinery.Name); return refinery; } + + if (power != null && refinery != null && !HasSufficientPowerForActor(refinery)) + { + HackyAI.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); + return power; + } } // Make sure that we can can spend as fast as we are earning if (ai.Info.NewProductionCashThreshold > 0 && playerResources.Resources > ai.Info.NewProductionCashThreshold) { var production = GetProducibleBuilding("Production", buildableThings); - if (production != null) + if (production != null && HasSufficientPowerForActor(production)) { HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (production)", queue.Actor.Owner, production.Name); return production; } + + if (power != null && production != null && !HasSufficientPowerForActor(production)) + { + HackyAI.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); + return power; + } } // Create some head room for resource storage if we really need it if (playerResources.AlertSilo) { var silo = GetProducibleBuilding("Silo", buildableThings); - if (silo != null) + if (silo != null && HasSufficientPowerForActor(silo)) { HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (silo)", queue.Actor.Owner, silo.Name); return silo; } + + if (power != null && silo != null && !HasSufficientPowerForActor(silo)) + { + HackyAI.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); + return power; + } } // Build everything else @@ -200,23 +223,18 @@ namespace OpenRA.Mods.Common.AI continue; // Will this put us into low power? - var actor = world.Map.Rules.Actors[frac.Key]; - var pis = actor.Traits.WithInterface().Where(i => i.UpgradeMinEnabledLevel < 1); - if (playerPower.ExcessPower < ai.Info.MinimumExcessPower || playerPower.ExcessPower < pis.Sum(pi => pi.Amount)) + var actor = world.Map.Rules.Actors[name]; + if (playerPower.ExcessPower < ai.Info.MinimumExcessPower || !HasSufficientPowerForActor(actor)) { // Try building a power plant instead - var power = GetProducibleBuilding("Power", - buildableThings, a => a.Traits.WithInterface().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(pi => pi.Amount)); if (power != null && power.Traits.WithInterface().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(pi => pi.Amount) > 0) { - // TODO: Handle the case when of when we actually do need a power plant because we don't have enough but are also suffering from a power outage if (playerPower.PowerOutageRemainingTicks > 0) - HackyAI.BotDebug("AI: {0} is suffering from a power outage; not going to build {1}", queue.Actor.Owner, power.Name); + HackyAI.BotDebug("{0} decided to build {1}: Priority override (is low power)", queue.Actor.Owner, power.Name); else - { HackyAI.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); - return power; - } + + return power; } }