diff --git a/OpenRA.Mods.Common/AIUtils.cs b/OpenRA.Mods.Common/AIUtils.cs index ea9f0efaa6..e9635ba526 100644 --- a/OpenRA.Mods.Common/AIUtils.cs +++ b/OpenRA.Mods.Common/AIUtils.cs @@ -34,11 +34,12 @@ namespace OpenRA.Mods.Common .Any(availableCells => availableCells > 0); } - public static IEnumerable FindQueues(Player player, string category) + public static ILookup FindQueuesByCategory(Player player) { return player.World.ActorsWithTrait() - .Where(a => a.Actor.Owner == player && a.Trait.Info.Type == category && a.Trait.Enabled) - .Select(a => a.Trait); + .Where(a => a.Actor.Owner == player && a.Trait.Enabled) + .Select(a => a.Trait) + .ToLookup(pq => pq.Info.Type); } public static int CountActorsWithNameAndTrait(string actorName, Player owner) diff --git a/OpenRA.Mods.Common/Traits/BotModules/BaseBuilderBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/BaseBuilderBotModule.cs index e4a9b35774..e785faabd4 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/BaseBuilderBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/BaseBuilderBotModule.cs @@ -220,6 +220,7 @@ namespace OpenRA.Mods.Common.Traits // PERF: We tick only one type of valid queue at a time // if AI gets enough cash, it can fill all of its queues with enough ticks var findQueue = false; + var queuesByCategory = AIUtils.FindQueuesByCategory(player); for (int i = 0, builderIndex = currentBuilderIndex; i < builders.Length; i++) { if (++builderIndex >= builders.Length) @@ -227,7 +228,7 @@ namespace OpenRA.Mods.Common.Traits --builders[builderIndex].WaitTicks; - var queues = AIUtils.FindQueues(player, builders[builderIndex].Category).ToArray(); + var queues = queuesByCategory[builders[builderIndex].Category].ToArray(); if (queues.Length != 0) { if (!findQueue) @@ -254,7 +255,7 @@ namespace OpenRA.Mods.Common.Traits } } - builders[currentBuilderIndex].Tick(bot); + builders[currentBuilderIndex].Tick(bot, queuesByCategory); } void IBotRespondToAttack.RespondToAttack(IBot bot, Actor self, AttackInfo e) diff --git a/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/BaseBuilderQueueManager.cs b/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/BaseBuilderQueueManager.cs index 5f71b25c3e..9f207fcc5e 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/BaseBuilderQueueManager.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/BotModuleLogic/BaseBuilderQueueManager.cs @@ -56,7 +56,7 @@ namespace OpenRA.Mods.Common.Traits waterState = WaterCheck.DontCheck; } - public void Tick(IBot bot) + public void Tick(IBot bot, ILookup queuesByCategory) { // If failed to place something N consecutive times, wait M ticks until resuming building production if (failCount >= baseBuilder.Info.MaximumFailedPlacementAttempts && --failRetryTicks <= 0) @@ -106,7 +106,7 @@ namespace OpenRA.Mods.Common.Traits // PERF: Queue only one actor at a time per category itemQueuedThisTick = false; var active = false; - foreach (var queue in AIUtils.FindQueues(player, Category)) + foreach (var queue in queuesByCategory[Category]) { if (TickQueue(bot, queue)) active = true; diff --git a/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs index 37e2318489..0b8ea8f034 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs @@ -91,25 +91,31 @@ namespace OpenRA.Mods.Common.Traits if (ticks % FeedbackTime == 0) { + ILookup queuesByCategory = null; + var buildRequest = queuedBuildRequests.FirstOrDefault(); if (buildRequest != null) { - BuildUnit(bot, buildRequest); + queuesByCategory ??= AIUtils.FindQueuesByCategory(player); + BuildUnit(bot, buildRequest, queuesByCategory); queuedBuildRequests.Remove(buildRequest); } if (Info.IdleBaseUnitsMaximum <= 0 || Info.IdleBaseUnitsMaximum > idleUnitCount) { + queuesByCategory ??= AIUtils.FindQueuesByCategory(player); for (var i = 0; i < Info.UnitQueues.Length; i++) { if (++currentQueueIndex >= Info.UnitQueues.Length) currentQueueIndex = 0; - if (AIUtils.FindQueues(player, Info.UnitQueues[currentQueueIndex]).Any()) + var category = Info.UnitQueues[currentQueueIndex]; + var queues = queuesByCategory[category].ToArray(); + if (queues.Length != 0) { // PERF: We tick only one type of valid queue at a time // if AI gets enough cash, it can fill all of its queues with enough ticks - BuildRandomUnit(bot, Info.UnitQueues[currentQueueIndex]); + BuildRandomUnit(bot, queues); break; } } @@ -127,13 +133,13 @@ namespace OpenRA.Mods.Common.Traits return queuedBuildRequests.Count(r => r == requestedActor); } - void BuildRandomUnit(IBot bot, string category) + void BuildRandomUnit(IBot bot, ProductionQueue[] queues) { if (Info.UnitsToBuild.Count == 0) return; // Pick a free queue - var queue = AIUtils.FindQueues(player, category).FirstOrDefault(q => !q.AllQueued().Any()); + var queue = queues.FirstOrDefault(q => !q.AllQueued().Any()); if (queue == null) return; @@ -146,7 +152,7 @@ namespace OpenRA.Mods.Common.Traits } // In cases where we want to build a specific unit but don't know the queue name (because there's more than one possibility) - void BuildUnit(IBot bot, string name) + void BuildUnit(IBot bot, string name, ILookup queuesByCategory) { var actorInfo = world.Map.Rules.Actors[name]; if (actorInfo == null) @@ -159,7 +165,7 @@ namespace OpenRA.Mods.Common.Traits ProductionQueue queue = null; foreach (var pq in buildableInfo.Queue) { - queue = AIUtils.FindQueues(player, pq).FirstOrDefault(q => !q.AllQueued().Any()); + queue = queuesByCategory[pq].FirstOrDefault(q => !q.AllQueued().Any()); if (queue != null) break; }