From 9934e4d765ea019bf06901d6b4758bffa1c3a098 Mon Sep 17 00:00:00 2001 From: spiritsnails <307422241+spiritsnails@users.noreply.github.com> Date: Sun, 2 Aug 2026 00:34:46 -0600 Subject: [PATCH] fix: Warden's House gibberish line offers YES/NO WardensHouseWardenText prints Gibberish1, calls YesNoChoice, and answers with Gibberish2 on yes / Gibberish3 on no (scripts/WardensHouse.asm). The port printed the question and ended the script. Both reply strings were already extracted and unused, so this is script wiring only. The branch targets become labels: they were hand-numbered absolute rows, already mis-pointed once (#535), and any insert meant renumbering jumps that had no way to announce they were stale. Also fixes the parity suite's show_text instrumentation, which stopped forwarding arguments at `subs` and so dropped the extraOpts carrying Commands.ask's choice callback -- silently turning every ask under test back into a plain show_text. --- data/scripts/story.lua | 36 ++++-- .../wardens_house_yesno_bug645_test.lua | 103 ++++++++++++++++++ tests/parity_wardens_house_bug535.lua | 46 ++++++-- 3 files changed, 166 insertions(+), 19 deletions(-) create mode 100644 tests/drivers/wardens_house_yesno_bug645_test.lua diff --git a/data/scripts/story.lua b/data/scripts/story.lua index 84d17361..459f2157 100644 --- a/data/scripts/story.lua +++ b/data/scripts/story.lua @@ -645,16 +645,18 @@ M.SAFARI_ZONE_SECRET_HOUSE = { M.WARDENS_HOUSE = { talk = { TEXT_WARDENSHOUSE_WARDEN = { + -- Labelled rather than hand-numbered: the branches here have been + -- re-pointed twice now (#535, #645), and every insert used to mean + -- renumbering three jumps that had no way of announcing they were stale. { "face_player" }, -- 1 { "check_flag", "EVENT_GOT_HM04" }, -- 2 -- #535: previously jumped to the same silent-end target as the - -- give-then-thank fallthrough (row 13), so the warden said nothing - -- on every visit after the trade. pokered's .got_item branch - -- (scripts/WardensHouse.asm) instead prints HM04ExplanationText, - -- so route here to the new row 17 that does the same. - { "jump_if_true", 17 }, -- 3 + -- give-then-thank fallthrough, so the warden said nothing on every + -- visit after the trade. pokered's .got_item branch + -- (scripts/WardensHouse.asm) instead prints HM04ExplanationText. + { "jump_if_true", "got_hm04" }, -- 3 { "check_item", "GOLD_TEETH" }, -- 4 - { "jump_if_false", 15 }, -- 5 + { "jump_if_false", "no_teeth" }, -- 5 { "show_text", "_WardensHouseWardenGaveTheGoldTeethText" }, -- 6 { "take_item", "GOLD_TEETH", 1 }, -- 7 { "set_flag", "EVENT_GAVE_GOLD_TEETH" }, -- 8 @@ -663,15 +665,27 @@ M.WARDENS_HOUSE = { { "give_item", "HM_STRENGTH", 1, false }, -- 10 { "show_text", "_WardensHouseWardenReceivedHM04Text" }, -- 11 { "set_flag", "EVENT_GOT_HM04" }, -- 12 - { "jump", 18 }, -- 13 (already got it this convo; jp .done) - { "jump", 18 }, -- 14 (unused) - { "show_text", "_WardensHouseWardenGibberish1Text" }, -- 15 - { "jump", 18 }, -- 16 (#535: skip the new explanation row below) + { "jump", "end" }, -- 13 (jp .done) + + -- #645: WardensHouseWardenText prints Gibberish1, then YesNoChoice, + -- and the warden answers the same gibberish either way -- Gibberish2 + -- on yes, Gibberish3 on no (scripts/WardensHouse.asm). The port + -- printed the question and walked off before the answer. + { "label", "no_teeth" }, -- 14 + { "ask", "_WardensHouseWardenGibberish1Text" }, -- 15 + { "jump_if_true", "gibberish_yes" }, -- 16 + { "show_text", "_WardensHouseWardenGibberish3Text" }, -- 17 + { "jump", "end" }, -- 18 + { "label", "gibberish_yes" }, -- 19 + { "show_text", "_WardensHouseWardenGibberish2Text" }, -- 20 + { "jump", "end" }, -- 21 + -- #535: pokered .got_item branch (scripts/WardensHouse.asm) -- -- printed on every subsequent talk once EVENT_GOT_HM04 is set. -- Text is _WardensHouseWardenHM04ExplanationText (text/WardensHouse.asm): -- HM04 teaches Strength, and hints at the Safari Zone secret house. - { "show_text", "_WardensHouseWardenHM04ExplanationText" }, -- 17 + { "label", "got_hm04" }, -- 22 + { "show_text", "_WardensHouseWardenHM04ExplanationText" }, -- 23 }, }, } diff --git a/tests/drivers/wardens_house_yesno_bug645_test.lua b/tests/drivers/wardens_house_yesno_bug645_test.lua new file mode 100644 index 00000000..c117d7e4 --- /dev/null +++ b/tests/drivers/wardens_house_yesno_bug645_test.lua @@ -0,0 +1,103 @@ +-- Driver: #645 Warden's House YES/NO. +-- +-- WardensHouseWardenText prints Gibberish1, calls YesNoChoice, and answers +-- with Gibberish2 on yes / Gibberish3 on no (scripts/WardensHouse.asm). The +-- port printed the question and ended the script, so the box just closed. +-- +-- Runs the conversation twice against the live script: once answering YES, +-- once walking the cursor down to NO, shooting the YES/NO box and each reply. +-- POKEPORT_DRIVER=tests/drivers/wardens_house_yesno_bug645_test.lua \ +-- POKEPORT_SPEED=2 lovec . +return function(game) + local U = dofile("tests/drivers/util.lua") + local DIR = os.getenv("SHOT_DIR") or "/tmp/shots" + + -- record what the script actually prints, so the log is checkable on its + -- own and does not depend on reading the shots + local Commands = require("src.script.Commands") + local shown = {} + local origShow = Commands.show_text + -- forward every argument: the 4th is extraOpts, which is how Commands.ask + -- passes its `choice` callback -- drop it and the YES/NO box never appears + Commands.show_text = function(ctx, textId, ...) + shown[#shown + 1] = textId + U.log("text:", textId) + return origShow(ctx, textId, ...) + end + + local ChoiceBox = require("src.ui.ChoiceBox") + local function choiceUp() + local top = game.stack:top() + return top and getmetatable(top) == ChoiceBox and top or nil + end + + -- mash A until the YES/NO box is up, then hand it back + local function talkUntilChoice() + U.tap(game, "a") + for _ = 1, 600 do + local box = choiceUp() + if box then return box end + U.tap(game, "a") + U.wait(3) + end + return nil + end + + local function talkDone() + for _ = 1, 600 do + if game.stack:top() == game.overworld then return true end + U.tap(game, "a") + U.wait(3) + end + return false + end + + -- the warden stands at (2,3); face him from the tile below + U.teleport(game, "WARDENS_HOUSE", 2, 4, "up") + -- Lead-in: this is meant to be WATCHED, not just logged. At SPEED=2 the + -- driver runs two iterations per rendered frame, so a wait of N is N/120 + -- seconds on screen -- long enough here to find the window before anything + -- happens. Every beat below is held the same way. + U.log("watch now: talking to the WARDEN in 5 seconds") + U.wait(600) + U.shot(game, DIR .. "/warden_0_before.png") + + -- ---------------------------------------------------------------- YES + shown = {} + local box = talkUntilChoice() + if not box then + U.log("FAIL no YES/NO box appeared after the gibberish line") + else + U.log("YES/NO box is up, cursor on", box.index == 1 and "YES" or "NO") + U.wait(360) -- hold the box on screen for 3s + U.shot(game, DIR .. "/warden_1_yesno_box.png") + U.tap(game, "a") -- take the default, YES + U.wait(360) + U.shot(game, DIR .. "/warden_2_yes_reply.png") + end + talkDone() + U.log("YES branch printed:", table.concat(shown, ",")) + U.log("now the same conversation again, answering NO") + U.wait(360) + + -- ----------------------------------------------------------------- NO + shown = {} + box = talkUntilChoice() + if not box then + U.log("FAIL no YES/NO box on the second talk") + else + U.wait(240) + U.tap(game, "down") -- walk the cursor onto NO, on screen + U.wait(360) + U.log("cursor now on", box.index == 1 and "YES" or "NO") + U.shot(game, DIR .. "/warden_3_yesno_on_no.png") + U.tap(game, "a") + U.wait(360) + U.shot(game, DIR .. "/warden_4_no_reply.png") + end + talkDone() + U.log("NO branch printed:", table.concat(shown, ",")) + + U.wait(360) + Commands.show_text = origShow +end diff --git a/tests/parity_wardens_house_bug535.lua b/tests/parity_wardens_house_bug535.lua index aafda88c..7ab69ee5 100644 --- a/tests/parity_wardens_house_bug535.lua +++ b/tests/parity_wardens_house_bug535.lua @@ -36,13 +36,22 @@ local script = story.WARDENS_HOUSE.talk.TEXT_WARDENSHOUSE_WARDEN -- instrument show_text the way parity_gift_atomicity.lua does, to record -- exactly which text ids actually printed local shown = {} +-- forward EVERY argument: the 4th is extraOpts, which is how Commands.ask +-- hands down its `choice` callback. A wrapper that stops at `subs` silently +-- turns every ask in the script back into a plain show_text -- no YES/NO box, +-- and ctx.lastCheck left holding whatever the previous check_* put there. local origShow = Commands.show_text -Commands.show_text = function(ctx, textId, subs) +Commands.show_text = function(ctx, textId, ...) shown[#shown + 1] = textId - return origShow(ctx, textId, subs) + return origShow(ctx, textId, ...) end -local function runScript() +-- `button` drives the whole conversation: both A and B page a text box, and +-- on the YES/NO box A takes the cursor's default (YES) while B snaps to NO +-- and answers false (ChoiceBox:update, .choseSecondMenuItem). So holding A +-- runs the yes branch and holding B runs the no branch, with no reaching +-- into the choice box from the test. +local function runScript(button) shown = {} StateStack:init() local ow = { map = { id = "WARDENS_HOUSE", def = { label = "WardensHouse" } }, @@ -53,7 +62,7 @@ local function runScript() local guard = 0 while r:isRunning() and guard < 3000 do guard = guard + 1 - Input.pressed = { a = true } + Input.pressed = { [button or "a"] = true } StateStack:update(1 / 60) r:update() end @@ -85,13 +94,34 @@ check(runScript(), "a third talk completes") eq(table.concat(shown, ","), "_WardensHouseWardenHM04ExplanationText", "the explanation text repeats on every later talk, not just the first") --- === 3) unrelated path is unchanged: no GOLD TEETH, no flag yet === +-- === 3) no GOLD TEETH yet: the gibberish question, then a YES/NO, then the +-- warden's answer -- Gibberish2 on yes, Gibberish3 on no (#645). +-- The port used to stop dead after the question. === Game.save = SaveData.newGame() -check(runScript(), "empty-handed talk completes") -eq(table.concat(shown, ","), "_WardensHouseWardenGibberish1Text", - "without the GOLD TEETH the Warden's gibberish line is unchanged") +check(runScript("a"), "empty-handed talk completes on yes") +eq(table.concat(shown, ","), + "_WardensHouseWardenGibberish1Text,_WardensHouseWardenGibberish2Text", + "answering YES gets the warden's reply, not silence (#645)") check(not Flags.get(Game.save, "EVENT_GOT_HM04"), "no HM04 yet") +Game.save = SaveData.newGame() +check(runScript("b"), "empty-handed talk completes on no") +eq(table.concat(shown, ","), + "_WardensHouseWardenGibberish1Text,_WardensHouseWardenGibberish3Text", + "and answering NO gets the other reply (#645)") + +-- the question is asked, not just printed: `ask` is what puts the YES/NO box +-- up, so a future edit that downgrades it back to show_text fails here +local askRow +for _, row in ipairs(script) do + if row[2] == "_WardensHouseWardenGibberish1Text" then askRow = row[1] end +end +eq(askRow, "ask", "the gibberish line is asked with a YES/NO, not just shown") + +-- neither answer touches the teeth trade +check(not Flags.get(Game.save, "EVENT_GAVE_GOLD_TEETH"), + "and neither answer hands over teeth the player does not have") + Commands.show_text = origShow S.finish()