Guarantee the post-battle fade-in by moving it into BattleState:finish()

pushBattle wrapped onFinish to push Transition.battleReturn on any
non-lose result, but that only fires for callers that route through
pushBattle correctly. Moving the same push into BattleState:finish()
instead -- the one choke point every battle (wild, trainer, walk-up,
scripted, link) already passes through on exit -- makes the fade
unconditional rather than dependent on each call site's wiring.
This commit is contained in:
spiritsnails
2026-08-01 16:41:51 -06:00
parent f109530c5f
commit 6b012a31ce
2 changed files with 19 additions and 27 deletions
+15 -1
View File
@@ -4430,7 +4430,21 @@ function BattleState:finish()
require("src.core.Music").restoreMap(self.data)
self.game.stack:pop()
Runtime.emit("battle.ended", { battle = self, result = self.result or "run" })
if self.onFinish then self.onFinish(self.result or "run") end
-- Coming back from the battle screen is a fade, not a cut: EnterMap sees
-- BIT_BATTLE_OVER_OR_BLACKOUT set and runs MapEntryAfterBattle
-- (home/overworld.asm:22, :749-753) = GBFadeInFromWhite. This is the one
-- choke point every battle -- wild, trainer, walk-up, scripted, link --
-- passes through on its way out, so the fade is guaranteed here rather
-- than depending on each caller having wrapped onFinish correctly.
local result = self.result or "run"
local onFinish = self.onFinish
if result == "lose" then
-- the blackout path warps to the heal point with its own transition
if onFinish then onFinish(result) end
return
end
self.game.stack:push(require("src.render.Transition").battleReturn(self.game,
function() if onFinish then onFinish(result) end end))
end
-- ---------------------------------------------------------------------
+4 -26
View File
@@ -718,32 +718,10 @@ function OverworldState:pushBattle(battle)
require("src.core.Music").playBattle(Game.data, battle:computeMusicKind())
end
-- Coming back from the battle screen is a fade, not a cut: EnterMap sees
-- BIT_BATTLE_OVER_OR_BLACKOUT set and runs MapEntryAfterBattle
-- (home/overworld.asm:22, :749-753) = GBFadeInFromWhite, behind the
-- `ld c, 10 / call DelayFrames` at :351-352.
--
-- It is wrapped around onFinish here, at the one funnel every battle goes
-- through, rather than inside afterBattle: a script-driven win defers
-- afterBattle into ctx.afterScript so an evolution screen cannot be buried
-- under the trainer's follow-up text (see Commands.start_battle), and the
-- fade inherited that deferral -- on a rival battle it fired after the
-- post-battle dialogue AND the walk-off, instead of when the battle ended.
--
-- The rest of onFinish runs as the fade's onDone, which is also the
-- hardware order: MapEntryAfterBattle fades the map back in, and only then
-- does the map script get to run. The overworld is frozen meanwhile --
-- StateStack updates the top state only -- so nothing moves under it.
local finish = battle.onFinish
battle.onFinish = function(result)
if result == "lose" then
-- the blackout path warps to the heal point with its own transition
if finish then finish(result) end
return
end
Game.stack:push(require("src.render.Transition").battleReturn(Game,
function() if finish then finish(result) end end))
end
-- The fade back in from white on the way out is BattleState:finish()'s
-- job now -- the one choke point every battle passes through on exit,
-- guaranteed regardless of which caller pushed the battle -- so this
-- function only owns the entry wipe.
Game.stack:push(BattleTransition.new(Game, function()
Game.stack:push(battle)
end, {