Files
gen1recomp/tests/engine/oaks_lab_last_ball_bug601.lua
T
Shane McGovern 0dd187fe30 Fix wrong dialogue on Prof. Oak's leftover ball (#601)
After the player picks a starter and the rival takes his, every ball on
the lab table fell through to 'Those are POKé BALLs' instead of the
leftover-mon line.  Port the pokered OaksLabSelectedPokeBallScript ->
OaksLabLastMonScript beat: with EVENT_GOT_STARTER set, Oak turns to face
the player and reads 'That's PROF.OAK's last Pokémon!'.  The ROM's
'#MON' ligature is spelled out as Pokémon.

The renumbering also drops the table's nine out-of-range 'jump 21'
rows (run-time 'end' idioms) for explicit "end" targets, so the
script now validates cleanly.

Tests: T2 suite drives the ball talk table through a ScriptRunner-
compatible executor (leftover text, pre-escort text, pre-pick offer)
plus a T3 driver that talks to the leftover ball in a real game.
2026-08-01 20:55:31 +01:00

130 lines
5.1 KiB
Lua

-- Regression coverage for #601 "Wrong dialogue when interacting with Prof.
-- Oak's last ball" (T2, ROM-free).
--
-- pret/pokered scripts/OaksLab.asm OaksLabSelectedPokeBallScript: once
-- EVENT_GOT_STARTER is set, EVERY ball's text handler jumps to
-- OaksLabLastMonScript -- Oak turns to face the player and reads
-- "_OaksLabLastMonText" ("That's PROF.OAK's last #MON!") instead of
-- re-offering the starter. The buggy port fell through to
-- _OaksLabThoseArePokeBallsText ("Those are POKé BALLs...") on every ball
-- once a starter had been picked. The fix also spells the ROM's "#MON"
-- ligature out as "Pokémon".
--
-- The three ball scripts share one table (starterBall), so this suite
-- drives that table through a mini ScriptRunner-compatible executor:
-- flag checks, jumps, "end" halts and text rows are executed, UI-heavy
-- commands (push_screen, ask, give_pokemon, npc moves) are no-ops with
-- ask recording the offer text. It then asserts the whole flow:
-- * GOT_STARTER + talk -> Oak faces down + "last Pokémon!" line, ends
-- * no GOT_STARTER, not escorted in -> "Those are POKé BALLs"
-- * no GOT_STARTER, escorted in -> the dex/ask offer (unchanged path)
-- plus MapScripts.validateContribution stays clean (the pre-fix table
-- carried nine out-of-range "jump 21" findings -- its run-time "end").
package.path = "./?.lua;./?/init.lua;" .. package.path
local T = require("tests.harness")
local MapScripts = require("src.script.MapScripts")
local contribution = dofile("data/scripts/oaks_lab.lua")
local problems = MapScripts.validateContribution(contribution)
T.eq(#problems, 0, "oaks_lab contribution validates cleanly")
for _, p in ipairs(problems) do
T.check(false, "unexpected finding: " .. p)
end
local BALL = "TEXT_OAKSLAB_CHARMANDER_POKE_BALL"
-- ---- mini executor over the talk rows (ScriptRunner semantics: a jump
-- command returns the next row index or "end" to halt)
local function run(script, flags, answer)
local pc, texts, offers = 1, {}, {}
local lastCheck = nil
while pc <= #script do
local row = script[pc]
local verb = row[1]
if verb == "check_flag" then
lastCheck = flags[row[2]] == true
elseif verb == "jump_if_true" then
if lastCheck then
if row[2] == "end" then break end
pc = row[2] goto next
end
elseif verb == "jump_if_false" then
if not lastCheck then
if row[2] == "end" then break end
pc = row[2] goto next
end
elseif verb == "jump" then
if row[2] == "end" then break end
pc = row[2]
goto next
elseif verb == "show_text" then
texts[#texts + 1] = row[2]
elseif verb == "ask" then
offers[#offers + 1] = row[2]
if answer == false then
pc = pc + 1 -- decline: the next row's jump_if_false decides
goto next
end
end
-- push_screen / give_pokemon / set_flag / hide_object / move_npc_to /
-- face_object: no-op here (set_flag is exercised via the fixture
-- flags table instead of being run)
pc = pc + 1
::next::
end
return texts, offers
end
local function concat(list)
return table.concat(list, "\n")
end
-- ---- leftover ball after the pick: Oak faces down + the last-mon line
local got = { EVENT_GOT_STARTER = true, EVENT_FOLLOWED_OAK_INTO_LAB = true }
local texts, offers = run(contribution.talk[BALL], got, true)
T.eq(#offers, 0, "no starter offer after the pick")
local box = concat(texts)
T.check(box:find("last Pokémon!", 1, true) ~= nil,
"leftover ball says the last-mon line (got: " .. box .. ")")
T.check(box:find("Those are", 1, true) == nil,
"leftover ball no longer says 'Those are POKé BALLs'")
T.check(box:find("#MON", 1, true) == nil,
"the ROM #MON ligature is spelled out as Pokémon")
-- the pokered beat also turns Oak to face the player
T.check(contribution.talk[BALL][20][1] == "face_object"
and contribution.talk[BALL][20][2] == 5
and contribution.talk[BALL][20][3] == "down",
"row 20 faces Oak down before the line (OaksLabLastMonScript)")
-- ---- pre-escort: still the vanilla "Those are POKé BALLs" line
local pre = { EVENT_GOT_STARTER = false, EVENT_FOLLOWED_OAK_INTO_LAB = false }
local t2, o2 = run(contribution.talk[BALL], pre, true)
T.eq(#o2, 0, "no offer before Oak escorts the player in")
T.check(concat(t2):find("ThoseArePokeBalls", 1, true) ~= nil,
"pre-escort balls keep the 'Those are POKé BALLs' line")
-- ---- escorted in but no pick yet: the dex + "You want X?" offer
local mid = { EVENT_GOT_STARTER = false, EVENT_FOLLOWED_OAK_INTO_LAB = true }
local t3, o3 = run(contribution.talk[BALL], mid, true)
T.eq(#o3, 1, "the starter offer still runs before the pick")
T.check(concat(t3):find("last Pokémon!", 1, true) == nil,
"no last-mon line before the pick")
-- ---- all three balls share the same table shape (last-mon beat present)
for _, key in ipairs({
"TEXT_OAKSLAB_CHARMANDER_POKE_BALL",
"TEXT_OAKSLAB_SQUIRTLE_POKE_BALL",
"TEXT_OAKSLAB_BULBASAUR_POKE_BALL",
}) do
local script = contribution.talk[key]
T.check(script and script[21] and script[21][2] and
script[21][2]:find("Pokémon", 1, true) ~= nil,
key .. " carries the last-mon line")
end
T.finish("oaks_lab_last_ball_bug601")