diff --git a/src/battle/BattleState.lua b/src/battle/BattleState.lua index 4306cd56..2f90f29e 100644 --- a/src/battle/BattleState.lua +++ b/src/battle/BattleState.lua @@ -2677,10 +2677,24 @@ function BattleState:updateFx() -- so a sounding siren rides out the next hit's HP drain instead of -- dropping out mid-announcement (#293) self.lowHealthAlarmOn = self:lowHealthAlarmActive() - if self.lowHealthAlarmOn then - Sound.startLoop(self.data, "Low_Health_Alarm") + -- battle.low_health_alarm: on/off toggle for the siren loop, ctx.on + -- mirrors self.lowHealthAlarmOn. Vanilla just starts/stops the loop + -- each frame; a mod can wrap this to reshape the toggle (e.g. force + -- ctx.on false after some budget) before letting vanilla act on it. + if Runtime.wantsHook("battle.low_health_alarm") then + Runtime.call("battle.low_health_alarm", function(ctx) + if ctx.on then + Sound.startLoop(ctx.battle.data, "Low_Health_Alarm") + else + Sound.stopLoop("Low_Health_Alarm") + end + end, { on = self.lowHealthAlarmOn, battle = self }) else - Sound.stopLoop("Low_Health_Alarm") + if self.lowHealthAlarmOn then + Sound.startLoop(self.data, "Low_Health_Alarm") + else + Sound.stopLoop("Low_Health_Alarm") + end end end diff --git a/tests/mod_battle_tests.lua b/tests/mod_battle_tests.lua index 93c35319..a6794031 100644 --- a/tests/mod_battle_tests.lua +++ b/tests/mod_battle_tests.lua @@ -718,6 +718,42 @@ do unsub() end +-- ------- battle.low_health_alarm hook: mirrors the siren toggle + +do + local Sound = require("src.core.Sound") + local calls = {} + local origStart, origStop = Sound.startLoop, Sound.stopLoop + Sound.startLoop = function(data, name) calls[#calls + 1] = { "start", name } end + Sound.stopLoop = function(name) calls[#calls + 1] = { "stop", name } end + + local game = makeGame({ Pokemon.new(Data, "BULBASAUR", 20) }) + local battle = BattleState.newWild(game, "RATTATA", 5) + battle.player.mon.hp = 1 + battle.player.shownHP = 1 + + local seenOn = nil + local unsub = hooks:wrap("battle.low_health_alarm", function(nextFn, ctx) + seenOn = ctx.on + return nextFn(ctx) + end) + battle:updateFx() + check(seenOn == true, "battle.low_health_alarm hook sees the alarm toggle on") + check(calls[#calls][1] == "start" and calls[#calls][2] == "Low_Health_Alarm", + "an unmodified hook still starts the siren loop") + unsub() + + unsub = hooks:wrap("battle.low_health_alarm", function(nextFn, ctx) + ctx.on = false + return nextFn(ctx) + end) + battle:updateFx() + check(calls[#calls][1] == "stop", "a mod can force the alarm off before vanilla acts") + unsub() + + Sound.startLoop, Sound.stopLoop = origStart, origStop +end + -- ------- battle events: the scripted sequence do