diff --git a/src/battle/BattleState.lua b/src/battle/BattleState.lua index 2830497f..075572a9 100644 --- a/src/battle/BattleState.lua +++ b/src/battle/BattleState.lua @@ -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 -- --------------------------------------------------------------------- diff --git a/src/world/OverworldController.lua b/src/world/OverworldController.lua index 49564151..ac620cd4 100644 --- a/src/world/OverworldController.lua +++ b/src/world/OverworldController.lua @@ -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, {