From 128f7d729f94cf8930160ceb6b952da7f76933a6 Mon Sep 17 00:00:00 2001 From: lL1l1 <82986251+lL1l1@users.noreply.github.com> Date: Sun, 10 Nov 2024 02:48:15 -0800 Subject: [PATCH] Add a flat threshold for the brain energy manager (#6514) Places an upper limit on the energy requirement to turn energy-consuming units back on after an energy stall --- changelog/snippets/fix.6514.md | 1 + engine/Sim/CAiBrain.lua | 32 ++++++++++----- lua/TriggerManager.lua | 14 +++++++ .../EnergyManagerBrainComponent.lua | 39 +++++++++++++++---- 4 files changed, 68 insertions(+), 18 deletions(-) create mode 100644 changelog/snippets/fix.6514.md diff --git a/changelog/snippets/fix.6514.md b/changelog/snippets/fix.6514.md new file mode 100644 index 0000000000..997f8b9578 --- /dev/null +++ b/changelog/snippets/fix.6514.md @@ -0,0 +1 @@ +- (#6514) Fix extremely large amounts of energy storage preventing structures from re-enabling themselves after an energy stall due to requiring 0.1% of storage to be full. Now there is an upper limit of 10k on the energy requirement, which is reached at 100k storage. diff --git a/engine/Sim/CAiBrain.lua b/engine/Sim/CAiBrain.lua index a0483eb4c3..b6c3fbdb75 100644 --- a/engine/Sim/CAiBrain.lua +++ b/engine/Sim/CAiBrain.lua @@ -202,7 +202,9 @@ end --- | 'Economy_Output_Energy' --- | 'Economy_Stored_Energy' --- | 'Economy_Reclaimed_Energy' +--- | 'Economy_Ratio_Energy' --- | 'Economy_MaxStorage_Energy' +--- | 'Economy_Trend_Energy' --- | 'Economy_PeakStorage_Energy' --- | 'Economy_TotalProduced_Mass' --- | 'Economy_TotalConsumed_Mass' @@ -210,7 +212,9 @@ end --- | 'Economy_Output_Mass' --- | 'Economy_Stored_Mass' --- | 'Economy_Reclaimed_Mass' +--- | 'Economy_Ratio_Mass' --- | 'Economy_MaxStorage_Mass' +--- | 'Economy_Trend_Mass' --- | 'Economy_PeakStorage_Mass' --- Returns the statistic of the army, if it doesn't exist it creates it and returns the default value @@ -484,23 +488,31 @@ function CAiBrain:PlatoonExists(platoon) end --- Remove an army stats trigger. --- @param statName String, army's stat, example: "Economy_Ratio_Mass". --- @param triggerName String, unique name of the trigger. +---@param statName AIBrainBlueprintStatUnits | AIBrainBlueprintStatEnemies | AIBrainBlueprintStatEconomy | AIBrainBlueprintStatDamage +---@param triggerName string # unique name of the trigger. function CAiBrain:RemoveArmyStatsTrigger(statName, triggerName) end --- Sets army's stat to value. --- @param statName String, army's stat, example: "Economy_Ratio_Mass". --- @param value Number. +---@param statName AIBrainBlueprintStatUnits | AIBrainBlueprintStatEnemies | AIBrainBlueprintStatEconomy | AIBrainBlueprintStatDamage # army's stat, example: "Economy_Ratio_Mass". +---@param value number function CAiBrain:SetArmyStat(statName, value) end +---@alias ComparatorString +---| "LessThan" +---| "LessThanOrEqual" +---| "GreaterThan" +---| "GreaterThanOrEqual" +---| "Equal" + --- Creates a new stat trigger. --- @param statName String, army's stat, example: "Economy_Ratio_Mass". --- @param triggerName String, unique name of the trigger. --- @param compareType String, available types: 'LessThan', 'LessThanOrEqual', 'GreaterThan', 'GreaterThanOrEqual', 'Equal'. --- @param value Number. -function CAiBrain:SetArmyStatsTrigger(statName, triggerName, compareType, value) +---@param statName AIBrainBlueprintStatUnits | AIBrainBlueprintStatEnemies | AIBrainBlueprintStatEconomy | AIBrainBlueprintStatDamage # army's stat +---@param triggerName string # unique name of the trigger. See `RemoveArmyStatsTrigger` to remove occupied names. +---@param compareType ComparatorString # available types: `LessThan`, `LessThanOrEqual`, `GreaterThan`, `GreaterThanOrEqual`, `Equal` +---@param value number # +---@param category EntityCategory? # +function CAiBrain:SetArmyStatsTrigger(statName, triggerName, compareType, value, category) end --- Set the current enemy for this brain to attack. @@ -518,7 +530,7 @@ function CAiBrain:SetGreaterOf(statname, val) end --- Set if the brain should share resources to the allies. --- @param bool ture/false +---@param bool boolean function CAiBrain:SetResourceSharing(bool) end diff --git a/lua/TriggerManager.lua b/lua/TriggerManager.lua index 0ebb533a97..30eaa989f8 100644 --- a/lua/TriggerManager.lua +++ b/lua/TriggerManager.lua @@ -394,6 +394,20 @@ Manager = { return true end, + ---@class EconStatsTriggerSpec + ---@field Name string + ---@field Parameters EconStatsTriggerParams + + ---@class EconStatsTriggerParams + ---@field Brain AIBrain + ---@field ResourceType 'Mass' | 'Energy' + ---@field EconType 'TotalProduced' | 'TotalConsumed' | 'Income' | 'Output' | 'Stored' | 'Reclaimed' | 'Ratio' | 'MaxStorage' | 'PeakStorage' | 'Trend' + ---@field CompareType ComparatorString? + ---@field Number number # Value to compare against + + ---@param self TriggerManager + ---@param spec EconStatsTriggerSpec + ---@return boolean EconStats = function(self, spec) local params = spec.Parameters if not params.ResourceType then diff --git a/lua/aibrains/components/EnergyManagerBrainComponent.lua b/lua/aibrains/components/EnergyManagerBrainComponent.lua index bb0bc96084..183f287eb6 100644 --- a/lua/aibrains/components/EnergyManagerBrainComponent.lua +++ b/lua/aibrains/components/EnergyManagerBrainComponent.lua @@ -241,7 +241,7 @@ EnergyManagerBrainComponent = ClassSimple { end, OnStatsTrigger = function(self, triggerName) - if triggerName == "EnergyDepleted" or triggerName == "EnergyViable" then + if triggerName == "EnergyDepleted" or triggerName == "EnergyViable" or triggerName == "SwitchEnergyViableTrigger" then self:OnEnergyTrigger(triggerName) end end, @@ -250,18 +250,41 @@ EnergyManagerBrainComponent = ClassSimple { ---@param self AIBrain ---@param triggerName string OnEnergyTrigger = function(self, triggerName) - if triggerName == "EnergyDepleted" then + if triggerName == "EnergyDepleted" or triggerName == "SwitchEnergyViableTrigger" then -- add trigger when we can recover units - self:SetArmyStatsTrigger('Economy_Ratio_Energy', 'EnergyViable', 'GreaterThanOrEqual', 0.1) - self.EnergyDepleted = true + local energyStorageThreshold = 100000 + local storageRatio = 0.1 + + if self:GetArmyStat('Economy_MaxStorage_Energy', 0).Value < energyStorageThreshold then + -- When we have little storage, turn back on above the ratio, or switch trigger after building enough storage. + self:RemoveArmyStatsTrigger("Economy_Stored_Energy", "EnergyViable") + self:SetArmyStatsTrigger('Economy_Ratio_Energy', 'EnergyViable', 'GreaterThanOrEqual', storageRatio) + + self:RemoveArmyStatsTrigger("Economy_Stored_Energy", "SwitchEnergyViableTrigger") + self:SetArmyStatsTrigger("Economy_MaxStorage_Energy", "SwitchEnergyViableTrigger", "GreaterThanOrEqual", energyStorageThreshold) + else + -- When we have a lot of storage, turn back on above the ratio of the threshold, or switch trigger after losing enough storage. + self:RemoveArmyStatsTrigger("Economy_Ratio_Energy", "EnergyViable") + self:SetArmyStatsTrigger("Economy_Stored_Energy", "EnergyViable", "GreaterThanOrEqual", energyStorageThreshold * storageRatio) + + self:RemoveArmyStatsTrigger("Economy_MaxStorage_Energy", "SwitchEnergyViableTrigger") + self:SetArmyStatsTrigger("Economy_MaxStorage_Energy", "SwitchEnergyViableTrigger", "LessThan", energyStorageThreshold) + end - -- recurse over the list of units and do callbacks accordingly - for id, entity in self.EnergyDependingUnits do - if not IsDestroyed(entity) then - entity:OnEnergyDepleted() + if triggerName == "EnergyDepleted" then + self.EnergyDepleted = true + + -- recurse over the list of units and do callbacks accordingly + for id, entity in self.EnergyDependingUnits do + if not IsDestroyed(entity) then + entity:OnEnergyDepleted() + end end end else + -- clean up the trigger switcher + self:RemoveArmyStatsTrigger("Economy_MaxStorage_Energy", "SwitchEnergyViableTrigger") + -- add trigger when we're depleted self:SetArmyStatsTrigger('Economy_Ratio_Energy', 'EnergyDepleted', 'LessThanOrEqual', 0.0) self.EnergyDepleted = false