fix: settle real battle checkpoint decisions

This commit is contained in:
MaxTomahawk
2026-08-11 17:13:29 +02:00
parent 79ed37699e
commit 41f02ecfbc
2 changed files with 63 additions and 5 deletions
+11 -2
View File
@@ -1871,9 +1871,18 @@ function BattleState:update(dt)
-- its own has no slide to wait for. -- its own has no slide to wait for.
if (self.introSlide or 0) > 0 then return end if (self.introSlide or 0) > 0 then return end
if not self:updateQueue() then if not self:updateQueue() then
if self.afterQueue == "menu" then local destination = self.afterQueue
-- These fields are queue/presentation cursors, not durable battle
-- state. Once the queue has drained, keeping their terminal values
-- makes the real command menu look busy to BattleSafety even though
-- every message, wait and intro animation has settled.
self.afterQueue = nil
self.nextInsert = nil
self.waitFrames = nil
if destination == "menu" then
self.introSlide = nil
self.phase = "menu" self.phase = "menu"
elseif self.afterQueue == "finish" then elseif destination == "finish" then
self:finish() self:finish()
end end
end end
+52 -3
View File
@@ -15,7 +15,8 @@ local StateStack = require("src.core.StateStack")
local Data = Fixtures.fresh() local Data = Fixtures.fresh()
local function makeGame() local function makeGame(kind)
kind = kind or "wild"
local save = SaveData.newGame() local save = SaveData.newGame()
save.meta.playthroughId = "battle-playthrough" save.meta.playthroughId = "battle-playthrough"
save.party = { Pokemon.new(Data, "FIXMON_A", 20) } save.party = { Pokemon.new(Data, "FIXMON_A", 20) }
@@ -29,12 +30,24 @@ local function makeGame()
runner = { isRunning = function() return false end }, runner = { isRunning = function() return false end },
parallelRunners = {}, pendingScripts = {}, parallelQueue = {}, scriptMoves = {}, parallelRunners = {}, pendingScripts = {}, parallelQueue = {}, scriptMoves = {},
} }
function overworld:captureSave(progress)
progress.player.map = self.map.id
progress.player.x = self.player.cellX
progress.player.y = self.player.cellY
progress.player.facing = self.player.facing
end
local game = { data = Data, save = save, stack = stack, overworld = overworld } local game = { data = Data, save = save, stack = stack, overworld = overworld }
stack.states[1] = overworld stack.states[1] = overworld
local battle = BattleState.newWild(game, "FIXMON_B", 12) local battle = kind == "trainer"
and BattleState.newTrainer(game, "OPP_FIX_YOUNGSTER", 1)
or BattleState.newWild(game, "FIXMON_B", 12)
battle.phase = "menu" battle.phase = "menu"
battle.queue = {} battle.queue = {}
battle.checkpointOrigin = { kind = "wild_encounter" } battle.checkpointOrigin = kind == "trainer"
and { kind = "trainer_encounter", map = save.player.map,
npcId = "TRAINER_1", trainerClass = "OPP_FIX_YOUNGSTER", partyIndex = 1,
event = "EVENT_BEAT_TRAINER_1" }
or { kind = "wild_encounter" }
battle.onFinish = function() end battle.onFinish = function() end
stack.states[2] = battle stack.states[2] = battle
return game, overworld, battle return game, overworld, battle
@@ -45,6 +58,42 @@ T.same(Checkpoint.inspect(game), {
canCapture = true, canRestore = true, kind = "battle", canCapture = true, canRestore = true, kind = "battle",
}, "settled standard wild battle is a checkpoint boundary") }, "settled standard wild battle is a checkpoint boundary")
local function settleRealBattle(kind)
local liveGame, _, liveBattle = makeGame(kind)
liveBattle.phase, liveBattle.queue = nil, {}
liveGame.input = {
wasPressed = function(_, button) return button == "a" end,
isDown = function(_, button) return button == "a" end,
}
liveBattle:enter()
local frames = 0
while liveBattle.phase ~= "menu" and frames < 10000 do
frames = frames + 1
liveBattle:update(1 / 60)
end
T.eq(liveBattle.phase, "menu", "the real battle intro reaches its command menu")
return liveGame
end
local realGame = settleRealBattle("wild")
T.same(Checkpoint.inspect(realGame), {
canCapture = true, canRestore = true, kind = "battle",
}, "the completed real battle intro is a checkpoint boundary")
local oldGetRandomState, oldSetRandomState =
love.math.getRandomState, love.math.setRandomState
love.math.getRandomState = function() return "real-boundary-rng" end
love.math.setRandomState = function() end
local realSnapshot, realCaptureCode = Checkpoint.capture(realGame)
T.check(type(realSnapshot) == "table" and realSnapshot.kind == "battle",
"the first real command decision captures for deferred tools: "
.. tostring(realCaptureCode))
love.math.getRandomState, love.math.setRandomState =
oldGetRandomState, oldSetRandomState
local realTrainerGame = settleRealBattle("trainer")
T.same(Checkpoint.inspect(realTrainerGame), {
canCapture = true, canRestore = true, kind = "battle",
}, "the completed real trainer intro is a checkpoint boundary")
local function refused(mutator, code, label) local function refused(mutator, code, label)
local game2, ow2, battle2 = makeGame() local game2, ow2, battle2 = makeGame()
mutator(game2, ow2, battle2) mutator(game2, ow2, battle2)