Files
bryanthaboi ae6cac89e1 G2 support
2026-08-11 11:53:30 -04:00

138 lines
4.7 KiB
Lua

-- The starter choice at Elm's lab, which is the first `pokepic` a player meets.
--
-- maps/ElmsLab.asm, ElmsLabPokeBallScript (one per ball, Cyndaquil's at (6,3)):
--
-- turnobject ELMSLAB_ELM, DOWN
-- reanchormap
-- pokepic CYNDAQUIL
-- cry CYNDAQUIL
-- waitbutton
-- closepokepic
-- opentext
-- writetext ElmsLabText_ChooseCyndaquil
-- yesorno
--
-- The pic goes up with NO text window under it, so nothing else in that run of
-- commands consumes a button press: `waitbutton` (Script_waitbutton ->
-- WaitButton, home/text.asm) is the ONLY thing holding the frame, and it is
-- what gives the player time to look at the mon before the yes/no. The port
-- used to treat every `waitbutton` as already-paid-for by the text box that
-- usually precedes it, so pokepic / closepokepic ran inside one VM resume and
-- the pic was created and destroyed without a single frame drawing it (#911).
--
-- This driver stands in front of the Cyndaquil ball, presses A, and counts the
-- frames where World.pokePic is actually up. It shoots the popup, so a human
-- can see the pic and not just a number.
--
-- POKEPORT_GAME=gold POKEPORT_DRIVER=tests/drivers/gold_starter_pic.lua \
-- POKEPORT_SHOT_DIR=/tmp/gold-starter \
-- perl -e 'alarm 280; exec @ARGV' \
-- python3 -c "import pty; pty.spawn(['love','.'])"
local SHOT_DIR = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/gold-starter"
-- main.lua's love.visible / love.focus handlers call Game:visible / Game:focus,
-- and src/core/Game2.lua defines neither -- so on Gold any window event (another
-- app taking the screen, which is constant when drivers share a machine) kills
-- the run before a single assertion prints. Fixing that belongs in Game2, not
-- in a driver; surviving it belongs here, so this run swallows the two
-- callbacks. Loaded from love.load, i.e. after main.lua has installed its own.
function love.visible() end
function love.focus() end
return function(game)
local w = game.world
local fails = 0
local function wait(n) for _ = 1, n do coroutine.yield() end end
local function ok(cond, msg)
if cond then
print("[starter] ok " .. msg)
else
fails = fails + 1
print("[starter] FAIL " .. msg)
end
return cond
end
local function shot(name)
local path = SHOT_DIR .. "/" .. name .. ".png"
game.capturePath = path
for _ = 1, 120 do
if not game.capturePath then break end
coroutine.yield()
end
wait(1)
local f = io.open(path, "rb")
if f then f:close() return true end
print("[starter] FAIL screenshot did not reach disk: " .. path)
fails = fails + 1
return false
end
local function tap(btn)
table.insert(game.input.pressQueue, btn)
coroutine.yield()
game.input.state[btn] = false
end
os.execute('mkdir -p "' .. SHOT_DIR .. '" 2>/dev/null')
wait(45)
-- SCENE_ELMSLAB_NOTHING: the meet-Elm walk-in has already played, which is
-- the state a player is in when they walk over to the balls.
w.mapScenes.ELMS_LAB = 1
w:setMap("ELMS_LAB", 6, 4, "up")
wait(20)
local shown, hidden = 0, 0
local realShow, realHide = w.showPokePic, w.hidePokePic
w.showPokePic = function(self, species)
shown = shown + 1
return realShow(self, species)
end
tap("a")
-- Count the frames the pic is actually up. The pic is a field on the world
-- rather than a pushed state, so this is the same thing love.draw reads.
local picFrames, sawPic = 0, false
for i = 1, 260 do
if w.pokePic then
picFrames = picFrames + 1
if not sawPic then
sawPic = true
shot("01_starter_popup")
end
end
if sawPic and not w.pokePic then break end
-- Press A again only once the pic has been on screen a while, so the
-- driver measures the hold instead of ending it on frame one.
if picFrames == 90 then tap("a") end
if i % 4 == 0 and not sawPic then tap("a") end
coroutine.yield()
end
ok(shown > 0, "the ball script ran `pokepic` (" .. shown .. " call(s))")
ok(picFrames > 0,
"and the pic was on screen for " .. picFrames .. " drawn frame(s)")
ok(picFrames >= 30,
"long enough to read: `waitbutton` holds it until the player presses A")
-- The yes/no follows the pic, so a run that got this far is at the prompt.
for _ = 1, 120 do
if w.choicebox or game.stack:top() ~= game.overworld then break end
coroutine.yield()
end
-- The box opens empty and types itself in over the next few frames, so a
-- capture on the frame it appeared would photograph a blank window and
-- prove nothing about the question.
wait(60)
shot("02_confirm_prompt")
if fails > 0 then
error(("gold starter pic: %d assertion(s) failed"):format(fails))
end
print("[driver] PASS gold starter pic: pokepic holds until A, then asks")
end