mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 08:21:02 +02:00
171 lines
6.9 KiB
Lua
171 lines
6.9 KiB
Lua
-- Driver: regression coverage for #231 "Missing Blue Dialogue (first rival
|
|
-- battle exit line)".
|
|
--
|
|
-- The first lab rival battle is a coordinate trigger in
|
|
-- data/scripts/oaks_lab.lua onStep (OaksLabRivalChallengesPlayerScript,
|
|
-- wYCoord == 6). In pret/pokered scripts/OaksLab.asm the post-battle
|
|
-- OaksLabRivalEndBattleScript heals + flags, then on WIN prints the
|
|
-- "I picked the wrong POKéMON!" gloat, and on BOTH win and loss prints the
|
|
-- shared exit line _OaksLabRivalSmellYouLaterText ("OK! I'll make my POKéMON
|
|
-- fight to toughen it up!\012<PLAYER>! Gramps! Smell you later!") before Blue
|
|
-- walks out. The buggy onStep sequence omitted that exit line entirely, so
|
|
-- Blue left the lab silently.
|
|
--
|
|
-- Scenario WIN: after the battle Blue must gloat ("picked the wrong POKéMON")
|
|
-- AND say the exit line ("Smell you later").
|
|
-- Scenario LOSS: Blue skips the gloat (that taunt was shown in-battle via
|
|
-- Rival1WinText) but must STILL say the exit line ("Smell you later").
|
|
--
|
|
-- Both scenarios FAIL before the fix (the exit line never appears) and PASS
|
|
-- after it.
|
|
--
|
|
-- Mechanics: TextBox.new is hooked to APPEND every raw `text` arg (before
|
|
-- {PLAYER}/{RIVAL} substitution, so the token-free search substrings survive)
|
|
-- into `seen`; the whole SmellYouLater string, incl. the \012 page break,
|
|
-- arrives in one TextBox.new call. start_battle is stubbed to record the
|
|
-- result and RETURN NIL (not "end"): ScriptRunner then continues
|
|
-- synchronously into heal_party/set_flag/jump_if_false/show_text with NO
|
|
-- BattleState push and NO yield, so the run needs no party and cannot hang.
|
|
-- heal_party is a safe no-op on an empty party.
|
|
|
|
return function(game)
|
|
local U = dofile("tests/drivers/util.lua")
|
|
local DIR = os.getenv("SHOT_DIR") or "/tmp/shots"
|
|
|
|
-- accumulate every raw text a TextBox is built with
|
|
local TextBox = require("src.render.TextBox")
|
|
local origNew = TextBox.new
|
|
local seen = {}
|
|
TextBox.new = function(g, text, ...)
|
|
if type(text) == "string" then seen[#seen + 1] = text end
|
|
return origNew(g, text, ...)
|
|
end
|
|
|
|
-- Stub start_battle: set the win/loss result the post-battle rows branch on
|
|
-- and return nil so ScriptRunner falls through to heal_party/set_flag/
|
|
-- jump_if_false/show_text with no BattleState and no yield. ScriptRunner
|
|
-- resolves the live Commands.start_battle when no mod overrides it, so this
|
|
-- monkeypatch intercepts the onStep challenge (Commands.resolve).
|
|
local Commands = require("src.script.Commands")
|
|
local origStartBattle = Commands.start_battle
|
|
local winResult = true
|
|
Commands.start_battle = function(ctx, _kind, _a, _b)
|
|
ctx.lastBattleResult = winResult and "win" or "lose"
|
|
ctx.lastCheck = winResult -- Rival1: check = (result == "win")
|
|
return nil
|
|
end
|
|
|
|
local function restore()
|
|
TextBox.new = origNew
|
|
Commands.start_battle = origStartBattle
|
|
end
|
|
|
|
-- Set the pre-battle flag state AND clear the rival's object toggle. The
|
|
-- WIN sequence ends with hide_object OAKSLAB_RIVAL, which persists as
|
|
-- save.objectToggles.OAKS_LAB.OAKSLAB_RIVAL = false; without clearing it the
|
|
-- LOSS re-teleport spawns with the rival hidden and onStep (which returns
|
|
-- false when npcByIndex(1) is nil) never fires the challenge. Must run
|
|
-- BEFORE U.teleport, since the map spawns its objects on push.
|
|
local function resetSave()
|
|
local flags = game.save.flags or {}
|
|
game.save.flags = flags
|
|
flags.EVENT_FOLLOWED_OAK_INTO_LAB = true
|
|
flags.EVENT_GOT_STARTER = true
|
|
flags.EVENT_CHOSE_SQUIRTLE = true
|
|
flags.EVENT_BATTLED_RIVAL_IN_OAKS_LAB = nil
|
|
if game.save.objectToggles and game.save.objectToggles.OAKS_LAB then
|
|
game.save.objectToggles.OAKS_LAB.OAKSLAB_RIVAL = nil
|
|
end
|
|
end
|
|
|
|
local function seenHas(sub)
|
|
for _, t in ipairs(seen) do
|
|
if t:find(sub, 1, true) then return true end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Walk down onto the y>=6 trigger, then page through the post-battle boxes
|
|
-- and the walk-out. Bounded so the window always quits. Captures the exit
|
|
-- line box the first time "Smell you later" appears, and a final overworld
|
|
-- shot once Blue has despawned (hide_object at the end of the sequence).
|
|
local function drive(smellShot, endShot)
|
|
local grabbedSmell = false
|
|
for _ = 1, 500 do
|
|
if (not grabbedSmell) and seenHas("Smell you later") then
|
|
U.wait(18) -- let the typewriter reveal the line before the shot
|
|
U.shot(game, smellShot)
|
|
grabbedSmell = true
|
|
end
|
|
local rival = game.overworld:npcByIndex(1)
|
|
if rival == nil then break end -- rival walked out + hid: sequence done
|
|
-- before the trigger fires the player must step down onto y>=6; after,
|
|
-- A pages every text box (IllTakeYouOn, IPicked, SmellYouLater)
|
|
local p = game.overworld.player
|
|
if p and (p.cellY or 0) < 6 then
|
|
U.hold(game, "down", 8)
|
|
end
|
|
U.tap(game, "a")
|
|
U.wait(3)
|
|
end
|
|
U.wait(6)
|
|
U.shot(game, endShot)
|
|
return grabbedSmell
|
|
end
|
|
|
|
-- ---- Scenario WIN
|
|
resetSave()
|
|
U.teleport(game, "OAKS_LAB", 4, 5, "down")
|
|
U.wait(6)
|
|
winResult = true
|
|
seen = {}
|
|
do
|
|
local p = game.overworld.player
|
|
U.log("WIN start cell:", tostring(p.cellX), tostring(p.cellY),
|
|
"rival:", tostring(game.overworld:npcByIndex(1) ~= nil))
|
|
end
|
|
U.shot(game, DIR .. "/win_before.png")
|
|
drive(DIR .. "/win_smell.png", DIR .. "/win_end.png")
|
|
local winGloat = seenHas("picked the")
|
|
local winSmell = seenHas("Smell you later")
|
|
local winPass = winGloat and winSmell
|
|
U.log("WIN gloat(picked the):", tostring(winGloat),
|
|
"exit(Smell you later):", tostring(winSmell))
|
|
U.log("WIN", winPass and "PASS" or "FAIL")
|
|
|
|
-- ---- Scenario LOSS
|
|
resetSave()
|
|
U.teleport(game, "OAKS_LAB", 4, 5, "down")
|
|
U.wait(6)
|
|
winResult = false
|
|
seen = {}
|
|
do
|
|
local p = game.overworld.player
|
|
U.log("LOSS start cell:", tostring(p.cellX), tostring(p.cellY),
|
|
"rival:", tostring(game.overworld:npcByIndex(1) ~= nil))
|
|
end
|
|
U.shot(game, DIR .. "/loss_before.png")
|
|
drive(DIR .. "/loss_smell.png", DIR .. "/loss_end.png")
|
|
local lossGloat = seenHas("picked the")
|
|
local lossSmell = seenHas("Smell you later")
|
|
-- loss must skip the win gloat but still print the shared exit line
|
|
local lossPass = lossSmell and (not lossGloat)
|
|
U.log("LOSS gloat(picked the):", tostring(lossGloat),
|
|
"exit(Smell you later):", tostring(lossSmell))
|
|
U.log("LOSS", lossPass and "PASS" or "FAIL")
|
|
|
|
-- restore hooks before any assert so a failure can't leave them installed
|
|
restore()
|
|
|
|
U.log("RESULT bug231", (winPass and lossPass) and "PASS" or "FAIL")
|
|
assert(winPass,
|
|
"WIN: after the first lab rival battle Blue must gloat "
|
|
.. "('I picked the wrong POKéMON!') AND say the exit line "
|
|
.. "('Smell you later'); got gloat=" .. tostring(winGloat)
|
|
.. " exit=" .. tostring(winSmell))
|
|
assert(lossPass,
|
|
"LOSS: after losing the first lab rival battle Blue must skip the gloat "
|
|
.. "but STILL say the exit line ('Smell you later'); got gloat="
|
|
.. tostring(lossGloat) .. " exit=" .. tostring(lossSmell))
|
|
end
|