diff --git a/luarules/gadgets/api_missions.lua b/luarules/gadgets/api_missions.lua index 68c5589f9a4..360d70cfda6 100644 --- a/luarules/gadgets/api_missions.lua +++ b/luarules/gadgets/api_missions.lua @@ -44,7 +44,8 @@ function gadget:Initialize() --scriptPath = 'mission-api-tests/sound_test.lua' --scriptPath = 'mission-api-tests/issue_orders_test.lua' --scriptPath = 'mission-api-tests/unit_triggers_test.lua' - scriptPath = 'mission-api-tests/feature_triggers_test.lua' + --scriptPath = 'mission-api-tests/feature_triggers_test.lua' + scriptPath = 'mission-api-tests/resource_test.lua' if not scriptPath then gadgetHandler:RemoveGadget() diff --git a/luarules/gadgets/api_missions_triggers.lua b/luarules/gadgets/api_missions_triggers.lua index d6279e9b156..cb7c6d092b8 100644 --- a/luarules/gadgets/api_missions_triggers.lua +++ b/luarules/gadgets/api_missions_triggers.lua @@ -342,6 +342,27 @@ local function checkFeatureDestroyed(trigger, featureID, featureDefID, attackerA activateTrigger(trigger) end +local resourceConsecutiveFrames = {} +local function checkResources(trigger, triggerID, index) + local conditionMet = true + if trigger.parameters.metal and select(index, Spring.GetTeamResources(trigger.parameters.teamID, "metal")) < trigger.parameters.metal then + conditionMet = false + end + if trigger.parameters.energy and select(index, Spring.GetTeamResources(trigger.parameters.teamID, "energy")) < trigger.parameters.energy then + conditionMet = false + end + + local requiredConsecutiveFrames = trigger.parameters.stableFrames or 0 + if conditionMet then + resourceConsecutiveFrames[triggerID] = (resourceConsecutiveFrames[triggerID] or 0) + gameFrameTriggersCheckInterval + if resourceConsecutiveFrames[triggerID] >= requiredConsecutiveFrames then + activateTrigger(trigger) + end + else + resourceConsecutiveFrames[triggerID] = 0 + end +end + ---------------------------------------------------------------- --- Call-ins: @@ -372,14 +393,25 @@ function gadget:GameFrame(frameNumber) processTriggersOfType(types.UnitEnteredLocation, function(trigger, triggerID) checkUnitEnteredLocation(trigger, triggerID) end) - processTriggersOfType(types.UnitLeftLocation, function(trigger, triggerID) checkUnitLeftLocation(trigger, triggerID) end) - processTriggersOfType(types.UnitDwellLocation, function(trigger, triggerID) checkUnitDwellLocation(trigger, triggerID) end) + + processTriggersOfType(types.ResourceStored, function(trigger, triggerID) + checkResources(trigger, triggerID, 1) + end) + processTriggersOfType(types.ResourceIncome, function(trigger, triggerID) + checkResources(trigger, triggerID, 4) + end) + processTriggersOfType(types.ResourceExpense, function(trigger, triggerID) + checkResources(trigger, triggerID, 5) + end) + processTriggersOfType(types.ResourcePull, function(trigger, triggerID) + checkResources(trigger, triggerID, 3) + end) end function gadget:MetaUnitAdded(_, unitDefID, unitTeam) diff --git a/luarules/mission_api/actions.lua b/luarules/mission_api/actions.lua index b39e3d8b873..f18d1dd9cdf 100644 --- a/luarules/mission_api/actions.lua +++ b/luarules/mission_api/actions.lua @@ -300,6 +300,15 @@ local function custom(func) func() end +local function addResources(teamID, metal, energy) + if metal then + Spring.AddTeamResource(teamID, 'metal', metal) + end + if energy then + Spring.AddTeamResource(teamID, 'energy', energy) + end +end + return { -- Triggers EnableTrigger = enableTrigger, @@ -340,4 +349,7 @@ return { -- Custom Custom = custom, + + -- Other + AddResources = addResources, } diff --git a/luarules/mission_api/actions_dispatcher.lua b/luarules/mission_api/actions_dispatcher.lua index 830df1fae0b..3c7da1dd733 100644 --- a/luarules/mission_api/actions_dispatcher.lua +++ b/luarules/mission_api/actions_dispatcher.lua @@ -38,6 +38,7 @@ local typeMapping = { [types.ClearAllMarkers] = actionFunctions.ClearAllMarkers, [types.Victory] = actionFunctions.Victory, [types.Defeat] = actionFunctions.Defeat, + [types.AddResources] = actionFunctions.AddResources, } -- unpack() does not handle optional parameters, as it cannot pass a value as nil diff --git a/luarules/mission_api/actions_schema.lua b/luarules/mission_api/actions_schema.lua index 10306d30c06..381dc0d77cd 100644 --- a/luarules/mission_api/actions_schema.lua +++ b/luarules/mission_api/actions_schema.lua @@ -53,6 +53,9 @@ local actionTypes = { -- Custom Custom = 900, + + -- Other + AddResources = 1000, } --============================================================-- @@ -354,6 +357,26 @@ local parameters = { type = Types.Function, }, }, + + -- Other + [actionTypes.AddResources] = { + [1] = { + name = 'teamID', + required = true, + type = Types.TeamID, + }, + [2] = { + name = 'metal', + required = false, + type = Types.Number, + }, + [3] = { + name = 'energy', + required = false, + type = Types.Number, + }, + requiresOneOf = { 'metal', 'energy', }, + }, } --============================================================-- diff --git a/luarules/mission_api/triggers_schema.lua b/luarules/mission_api/triggers_schema.lua index e697cb5c79e..fc08be183a7 100644 --- a/luarules/mission_api/triggers_schema.lua +++ b/luarules/mission_api/triggers_schema.lua @@ -25,7 +25,9 @@ local triggerTypes = { -- Resources ResourceStored = 400, - ResourceProduction = 401, + ResourceIncome = 401, + ResourceExpense = 402, + ResourcePull = 403, -- Statistics TotalUnitsLost = 500, @@ -386,8 +388,93 @@ local parameters = { }, -- Resources - [triggerTypes.ResourceStored] = { }, - [triggerTypes.ResourceProduction] = { }, + [triggerTypes.ResourceStored] = { + [1] = { + name = 'teamID', + required = true, + type = Types.TeamID, + }, + [2] = { + name = 'metal', + required = false, + type = Types.Number, + }, + [3] = { + name = 'energy', + required = false, + type = Types.Number, + }, + requiresOneOf = { 'metal', 'energy', }, + }, + [triggerTypes.ResourceIncome] = { + [1] = { + name = 'teamID', + required = true, + type = Types.TeamID, + }, + [2] = { + name = 'metal', + required = false, + type = Types.Number, + }, + [3] = { + name = 'energy', + required = false, + type = Types.Number, + }, + [4] = { + name = 'stableFrames', + required = false, + type = Types.Number, + }, + requiresOneOf = { 'metal', 'energy', }, + }, + [triggerTypes.ResourceExpense] = { + [1] = { + name = 'teamID', + required = true, + type = Types.TeamID, + }, + [2] = { + name = 'metal', + required = false, + type = Types.Number, + }, + [3] = { + name = 'energy', + required = false, + type = Types.Number, + }, + [4] = { + name = 'stableFrames', + required = false, + type = Types.Number, + }, + requiresOneOf = { 'metal', 'energy', }, + }, + [triggerTypes.ResourcePull] = { + [1] = { + name = 'teamID', + required = true, + type = Types.TeamID, + }, + [2] = { + name = 'metal', + required = false, + type = Types.Number, + }, + [3] = { + name = 'energy', + required = false, + type = Types.Number, + }, + [4] = { + name = 'stableFrames', + required = false, + type = Types.Number, + }, + requiresOneOf = { 'metal', 'energy', }, + }, -- Statistics [triggerTypes.TotalUnitsLost] = { }, diff --git a/singleplayer/mission-api-tests/resource_test.lua b/singleplayer/mission-api-tests/resource_test.lua new file mode 100644 index 00000000000..5410f857ea6 --- /dev/null +++ b/singleplayer/mission-api-tests/resource_test.lua @@ -0,0 +1,360 @@ +--- +--- Resource triggers and actions test mission. +--- + +local triggerTypes = GG['MissionAPI'].TriggerTypes +local actionTypes = GG['MissionAPI'].ActionTypes + +local triggers = { + + -- ── Bootstrap ───────────────────────────────────────────────────────────── + + start = { + type = triggerTypes.TimeElapsed, + parameters = { + gameFrame = 1, + }, + actions = { 'spawnMetalStorage', 'spawnEnergyStorage' }, + }, + + waveMetalAndEnergy = { + type = triggerTypes.TimeElapsed, + parameters = { + gameFrame = 120, + }, + actions = { 'addMetalAndEnergy', 'messageWaveMetalAndEnergy' }, + }, + + waveMetalOnly = { + type = triggerTypes.TimeElapsed, + parameters = { + gameFrame = 240, + }, + actions = { 'addMetalOnly', 'messageWaveMetalOnly' }, + }, + + waveEnergyOnly = { + type = triggerTypes.TimeElapsed, + parameters = { + gameFrame = 360, + }, + actions = { 'addEnergyOnly', 'messageWaveEnergyOnly' }, + }, + + waveFusion = { + type = triggerTypes.TimeElapsed, + parameters = { + gameFrame = 600, + }, + actions = { 'spawnFusion', 'messageWaveFusion' }, + }, + + waveMetalMaker = { + type = triggerTypes.TimeElapsed, + parameters = { + gameFrame = 720, + }, + actions = { 'spawnMetalMaker', 'messageWaveMetalMaker' }, + }, + + waveNuke = { + type = triggerTypes.TimeElapsed, + parameters = { + gameFrame = 900, + }, + actions = { 'spawnNuke', 'messageWaveNuke' }, + }, + + -- ── ResourceStored ──────────────────────────────────────────────────────── + + enoughMetalStored = { + type = triggerTypes.ResourceStored, + parameters = { + teamID = 0, + metal = 1500, + }, + actions = { 'messageMetalStored' }, + }, + + enoughEnergyStored = { + type = triggerTypes.ResourceStored, + parameters = { + teamID = 0, + energy = 3000, + }, + actions = { 'messageEnergyStored' }, + }, + + bothResourcesStored = { + type = triggerTypes.ResourceStored, + parameters = { + teamID = 0, + metal = 1800, + energy = 3500, + }, + actions = { 'messageBothStored' }, + }, + + -- ── ResourceIncome ──────────────────────────────────────────────────────── + + metalIncomeReached = { + type = triggerTypes.ResourceIncome, + parameters = { + teamID = 0, + metal = 5, + stableFrames = 150, + }, + actions = { 'messageMetalIncome' }, + }, + + energyIncomeReached = { + type = triggerTypes.ResourceIncome, + parameters = { + teamID = 0, + energy = 500, + stableFrames = 150, + }, + actions = { 'messageEnergyIncome' }, + }, + + -- ── ResourceExpense ─────────────────────────────────────────────────────── + + metalExpenseReached = { + type = triggerTypes.ResourceExpense, + parameters = { + teamID = 0, + metal = 5, + }, + actions = { 'messageMetalExpense' }, + }, + + energyExpenseReached = { + type = triggerTypes.ResourceExpense, + parameters = { + teamID = 0, + energy = 5, + }, + actions = { 'messageEnergyExpense' }, + }, + + -- ── ResourcePull ────────────────────────────────────────────────────────── + + metalPullReached = { + type = triggerTypes.ResourcePull, + parameters = { + teamID = 0, + metal = 1, + }, + actions = { 'messageMetalPull' }, + }, + + energyPullReached = { + type = triggerTypes.ResourcePull, + parameters = { + teamID = 0, + energy = 100, + }, + actions = { 'messageEnergyPull' }, + }, +} + +local actions = { + + -- ── Setup ───────────────────────────────────────────────────────────────── + + spawnMetalStorage = { + type = actionTypes.SpawnUnits, + parameters = { + unitDefName = 'armmstor', + teamID = 0, + position = { x = 1900, z = 1800 }, + }, + }, + + spawnEnergyStorage = { + type = actionTypes.SpawnUnits, + parameters = { + unitDefName = 'armestor', + teamID = 0, + position = { x = 1900, z = 1900 }, + }, + }, + + spawnFusion = { + type = actionTypes.SpawnUnits, + parameters = { + unitDefName = 'armfus', + teamID = 0, + position = { x = 1800, z = 1900 }, + }, + }, + + spawnMetalMaker = { + type = actionTypes.SpawnUnits, + parameters = { + unitDefName = 'armmmkr', + teamID = 0, + position = { x = 1800, z = 2000 }, + }, + }, + + spawnNuke = { + type = actionTypes.SpawnUnits, + parameters = { + unitDefName = 'corsilo', + teamID = 0, + position = { x = 2000, z = 2100 }, + }, + }, + + -- ── AddResources (metal + energy together) ──────────────────────────────── + + addMetalAndEnergy = { + type = actionTypes.AddResources, + parameters = { + teamID = 0, + metal = 500, + energy = 1000, + }, + }, + + -- ── AddResources (metal only) ───────────────────────────────────────────── + + addMetalOnly = { + type = actionTypes.AddResources, + parameters = { + teamID = 0, + metal = 250, + }, + }, + + -- ── AddResources (energy only) ──────────────────────────────────────────── + + addEnergyOnly = { + type = actionTypes.AddResources, + parameters = { + teamID = 0, + energy = 500, + }, + }, + + -- ── Wave messages ───────────────────────────────────────────────────────── + + messageWaveFusion = { + type = actionTypes.SendMessage, + parameters = { + message = "[Resource Test] Spawning Arm Fusion Reactor (armfus).", + }, + }, + + messageWaveMetalMaker = { + type = actionTypes.SendMessage, + parameters = { + message = "[Resource Test] Spawning Arm Advanced Metal Maker (armmmkr).", + }, + }, + + messageWaveNuke = { + type = actionTypes.SendMessage, + parameters = { + message = "[Resource Test] Spawning Core Silo (corsilo).", + }, + }, + + messageWaveMetalAndEnergy = { + type = actionTypes.SendMessage, + parameters = { + message = "[Resource Test] Adding 500 metal and 1000 energy.", + }, + }, + + messageWaveMetalOnly = { + type = actionTypes.SendMessage, + parameters = { + message = "[Resource Test] Adding 250 metal only.", + }, + }, + + messageWaveEnergyOnly = { + type = actionTypes.SendMessage, + parameters = { + message = "[Resource Test] Adding 2000 energy only.", + }, + }, + + -- ── ResourceStored messages ─────────────────────────────────────────────── + + messageMetalStored = { + type = actionTypes.SendMessage, + parameters = { + message = "[Resource Test] Team 0 has >= 1500 metal stored.", + }, + }, + + messageEnergyStored = { + type = actionTypes.SendMessage, + parameters = { + message = "[Resource Test] Team 0 has >= 3000 energy stored.", + }, + }, + + messageBothStored = { + type = actionTypes.SendMessage, + parameters = { + message = "[Resource Test] Team 0 has >= 2000 metal AND >= 5000 energy stored.", + }, + }, + + -- ── ResourceIncome messages ─────────────────────────────────────────────── + + messageMetalIncome = { + type = actionTypes.SendMessage, + parameters = { + message = "[Resource Test] Team 0 metal income >= 5 m/s.", + }, + }, + + messageEnergyIncome = { + type = actionTypes.SendMessage, + parameters = { + message = "[Resource Test] Team 0 energy income >= 500 e/s.", + }, + }, + + -- ── ResourceExpense messages ────────────────────────────────────────────── + + messageMetalExpense = { + type = actionTypes.SendMessage, + parameters = { + message = "[Resource Test] Team 0 metal expense >= 5 m/s.", + }, + }, + + messageEnergyExpense = { + type = actionTypes.SendMessage, + parameters = { + message = "[Resource Test] Team 0 energy expense >= 5 e/s.", + }, + }, + + -- ── ResourcePull messages ───────────────────────────────────────────────── + + messageMetalPull = { + type = actionTypes.SendMessage, + parameters = { + message = "[Resource Test] Team 0 metal pull >= 1 m/s.", + }, + }, + + messageEnergyPull = { + type = actionTypes.SendMessage, + parameters = { + message = "[Resource Test] Team 0 energy pull >= 100 e/s.", + }, + }, +} + +return { + Triggers = triggers, + Actions = actions, +}