diff --git a/src/core/Game.lua b/src/core/Game.lua index 9cb05fda..0f8fb6de 100644 --- a/src/core/Game.lua +++ b/src/core/Game.lua @@ -159,14 +159,15 @@ function Game:makeTitleState() self:applyOptions(self.save.options) self.stack:push(OverworldState, self.save.player.map, self.save.player.x, self.save.player.y, - self.save.player.facing) + self.save.player.facing, + { via = "boot", freshBoot = true }) Screens.push(self, bootScreens(self).newGame or "OakSpeech", function() end) end, onContinue = function() local loaded, recovered = SaveData.load() if loaded then - self:restoreSave(loaded, recovered) + self:restoreSave(loaded, recovered, { freshBoot = true }) end end, }) @@ -639,7 +640,12 @@ function Game:keypressed(key) return elseif key == "f2" then local loaded, recovered = SaveData.load() - if loaded then self:restoreSave(loaded, recovered) end + if loaded then + -- F2 jumps straight to the loaded save's map/position, with no + -- walking transition -- a hard state teleport like Continue, not a + -- smooth warp -- whether pressed at the title screen or mid-session. + self:restoreSave(loaded, recovered, { freshBoot = true }) + end return elseif key == "-" then self:zoomStep(-1) @@ -1124,7 +1130,7 @@ function Game:applyOptions(opts) if gbcCleared then self:writeOptions() end end -function Game:restoreSave(loaded, recovered) +function Game:restoreSave(loaded, recovered, opts) if ModRuntime.wants("save.loading") then ModRuntime.emit("save.loading", { raw = loaded }) end @@ -1157,8 +1163,12 @@ function Game:restoreSave(loaded, recovered) end -- rebuild the state stack from the save while self.stack:top() do self.stack:pop() end + -- freshBoot threads through from the caller (onContinue and F2 both set + -- it); a future caller that doesn't ask for it keeps the ordinary + -- crossfade by default. self.stack:push(self.overworld, loaded.player.map, - loaded.player.x, loaded.player.y, loaded.player.facing) + loaded.player.x, loaded.player.y, loaded.player.facing, + { via = "boot", freshBoot = opts and opts.freshBoot }) self.saveReport = report if not SaveData.emptyReport(report) then -- the report screen is a Screens id so mods (or the ui milestone) own @@ -1187,9 +1197,12 @@ function Game:restoreCheckpointSave(loaded) self.save = loaded self:adoptSave(loaded) while self.stack:top() do self.stack:pop() end + -- freshBoot unconditionally: Checkpoint.resume (src/core/Checkpoint.lua) + -- is this method's only caller, and it is itself gated to the title + -- session (isTitleSession). self.stack:push(self.overworld, loaded.player.map, loaded.player.x, loaded.player.y, loaded.player.facing, - { via = "checkpoint", checkpoint = true }) + { via = "checkpoint", checkpoint = true, freshBoot = true }) end -- Install a reconstructed battle without calling BattleState:enter(), whose diff --git a/src/world/OverworldController.lua b/src/world/OverworldController.lua index a825d37f..565908a4 100644 --- a/src/world/OverworldController.lua +++ b/src/world/OverworldController.lua @@ -462,8 +462,18 @@ function OverworldState:setMap(mapId, x, y, facing, opts) if not keepMusic then -- ..(home/overworld.asm ln 2346) local Music = require("src.core.Music") + -- opts.freshBoot: switch instantly instead of cross-fading, like every + -- other map's PlayDefaultMusic on real hardware -- set only by + -- Game.lua's hard state teleports (onContinue, New Game, F2, + -- restoreCheckpointSave). Deliberately separate from opts.via == + -- "boot" itself: dev tooling (src/dev/Console.lua's warp verb, + -- src/dev/HotReload.lua's reloadMap) reuses that same default for the + -- surf-restore/fresh-npc-pool branches above and must keep the + -- ordinary crossfade. + local fade = Music.MAP_FADE + if opts and opts.freshBoot then fade = nil end Music.playMap(Game.data, mapId, Game.save.onBike, self.player.surfing, - Music.MAP_FADE) + fade) end -- forced bike/surf tiles fire the moment the player is placed on the