-- Driver: regression coverage for #219 "Early Blue Battle". -- -- TEXT_OAKSLAB_RIVAL (data/scripts/oaks_lab.lua) is the rival's *talk* -- handler. In pret/pokered scripts/OaksLab.asm OaksLabText8, talking to -- the rival after you have a starter but before the lab battle only prints -- _OaksLabRivalMyPokemonLooksStrongerText: the battle itself is a -- coordinate trigger (OaksLabRivalChallengesPlayerScript, wYCoord == 6), -- never a talk action. The buggy handler fell through from that line -- straight into start_battle OPP_RIVAL1, so talking to Blue at the table -- immediately launched the rival fight. -- -- Scenario A (the #219 regression): talk to the rival with a starter but -- no lab battle yet. Correct: the "looks stronger" line shows and NO -- battle starts. Fails before the fix (start_battle fires on talk). -- Scenario B (guard against over-correction): step onto the coordinate -- trigger (y >= 6). Correct: the onStep challenge still starts the -- battle. Passes both before and after the fix. -- -- start_battle is stubbed to record the call and return "end" (halts the -- script cleanly, no BattleState push, no yield) so the run never hangs -- and needs no full party. We also read the raw text handed to -- TextBox.new (before {PLAYER}/{RIVAL} substitution) so "stronger" is -- detectable. return function(game) local U = dofile("tests/drivers/util.lua") local DIR = os.getenv("SHOT_DIR") or "/tmp/shots" -- Capture the raw text the next TextBox is built with. local TextBox = require("src.render.TextBox") local origNew = TextBox.new local lastText TextBox.new = function(g, text, ...) lastText = text return origNew(g, text, ...) end -- Stub start_battle: record it and halt the script instead of pushing a -- BattleState (which would need a real party) or yielding (which would -- hang the driver). ScriptRunner resolves the live Commands.start_battle -- when no mod overrides it, so this monkeypatch intercepts both the talk -- handler and the onStep challenge. local Commands = require("src.script.Commands") local origStartBattle = Commands.start_battle local battleStarted = false Commands.start_battle = function(_ctx, _kind, _a, _b) battleStarted = true return "end" end local function restore() TextBox.new = origNew Commands.start_battle = origStartBattle end local function setFlags() 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 end -- ---- Scenario A: talk to the rival at the table. -- fresh overworld in Oak's lab, player one cell right of the rival -- (object 1 at cell 4,3) and facing him. U.teleport(game, "OAKS_LAB", 5, 3, "left") setFlags() U.wait(6) battleStarted = false lastText = nil U.shot(game, DIR .. "/a_before.png") -- open the rival's textbox U.tap(game, "left"); U.wait(2) for _ = 1, 8 do U.tap(game, "a") for _ = 1, 30 do if lastText then break end U.wait(1) end if lastText then break end end local strongerSeen = lastText ~= nil and lastText:find("stronger") ~= nil -- let the typewriter reveal the line, then shoot the box: the "looks -- stronger" taunt, still overworld, no battle intro U.wait(30) U.shot(game, DIR .. "/a_after.png") -- dismiss the taunt box. Before the fix, closing it drops the script -- into the buggy start_battle rows (battleStarted flips true); after the -- fix it hits jump "end" and the stack settles back to the overworld. -- Stop the moment either happens so we never re-open the box by talking -- again (which would leave a stray TextBox on top and false-fail the -- overworld check). for _ = 1, 20 do if battleStarted then break end if game.stack:top() == game.overworld then break end U.tap(game, "a") U.wait(3) end U.wait(10) -- let any fall-through start_battle fire local aBattleStarted = battleStarted local aOverworld = (game.stack:top() == game.overworld) local aText = lastText or "" local aPass = strongerSeen and (aBattleStarted == false) and aOverworld U.log("SCENARIO A text:", aText) U.log("SCENARIO A strongerSeen:", tostring(strongerSeen), "battleStarted:", tostring(aBattleStarted), "overworld:", tostring(aOverworld)) U.log("SCENARIO A", aPass and "PASS" or "FAIL") -- close any open box before scenario B for _ = 1, 10 do U.tap(game, "a"); U.wait(2) end -- ---- Scenario B: step onto the coordinate trigger (y >= 6). -- Guards the real challenge path so the fix doesn't kill the lab battle. U.teleport(game, "OAKS_LAB", 4, 5, "down") setFlags() U.wait(6) battleStarted = false do local p = game.overworld.player U.log("SCENARIO B start cell:", tostring(p.cellX), tostring(p.cellY), "rival:", tostring(game.overworld:npcByIndex(1) ~= nil)) end -- walk down until the player crosses y == 6 (OaksLabRivalChallenges), -- pausing to mash A so the "I'll take you on" box + rival walk advance for _ = 1, 6 do U.hold(game, "down", 8) for _ = 1, 12 do U.tap(game, "a") U.wait(3) if battleStarted then break end end if battleStarted then break end end do local p = game.overworld.player U.log("SCENARIO B end cell:", tostring(p.cellX), tostring(p.cellY), "lastText:", tostring(lastText)) end U.shot(game, DIR .. "/b_after.png") local bPass = (battleStarted == true) U.log("SCENARIO B battleStarted:", tostring(battleStarted)) U.log("SCENARIO B", bPass and "PASS" or "FAIL") -- restore hooks before any assert so a failure can't leave them installed restore() U.log("RESULT bug219", (aPass and bPass) and "PASS" or "FAIL") assert(aPass, "Scenario A: talking to the rival must only show the 'looks stronger' " .. "line and start no battle (strongerSeen/battleStarted=false/overworld); " .. "text=" .. aText .. " battleStarted=" .. tostring(aBattleStarted)) assert(bPass, "Scenario B: stepping onto the coordinate trigger must still start the " .. "lab battle") end