Files
gen1recomp/tests/drivers/game3_tower_records.lua
2026-09-21 11:14:17 -04:00

283 lines
9.7 KiB
Lua

local U = require("tests.drivers.util")
local DIR = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/game3_tower_records"
local LOBBY = "FR_TRAINER_TOWER_LOBBY"
local FLOOR_1F = "FR_TRAINER_TOWER_1F"
local ROOF = "FR_TRAINER_TOWER_ROOF"
-- pokefirered/include/constants/metatile_behaviors.h:126
local MB_TRAINER_TOWER_MONITOR = 0xA3
local failures = 0
local function result(ok, label)
print((ok and "PASS " or "FAIL ") .. label)
if not ok then failures = failures + 1 end
return ok
end
local function finish()
if failures == 0 then
print("PASS tower_records")
love.event.quit(0)
else
print("FAIL tower_records failures=" .. failures)
love.event.quit(1)
end
end
local function run(game)
for _ = 1, 900 do
if game.phase == "boot" and game.boot then break end
U.wait(1)
end
game:_handleBootAction({ action = "new_game", name = "RED" })
U.wait(240)
local Runtime = require("src.core.game3.runtime")
local Map = require("src.core.game3.map")
local Space = require("src.core.game3.scripting.space")
local Player = require("src.core.game3.player")
local Party = require("src.core.game3.party")
local Collision = require("src.core.game3.collision")
local Choice = require("src.ui.game3.choice")
local Message = require("src.ui.game3.message")
local Fade = require("src.ui.game3.fade")
local Bag = require("src.core.game3.bag")
local Tower = require("src.core.game3.trainer_tower")
local Screen = require("src.ui.game3.trainer_tower_records")
local session = Runtime.getSession()
if not result(session ~= nil, "new game reached the game3 field") then return end
local function vmRunning() return (Space.vm and Space.vm:isRunning()) and true or false end
local function messageOpen() return (Message.isOpen and Message.isOpen()) and true or false end
local function place(x, y, facing)
Player.cellX, Player.cellY = x, y
Player.px, Player.py = x * 16, y * 16
Player.targetX, Player.targetY = x, y
Player.facing = facing or "up"
if game.session then
game.session.x, game.session.y, game.session.facing = x, y, facing or "up"
end
end
local function goTo(mapId, x, y, facing)
Map.load(nil, game, mapId, { x = x, y = y, facing = facing or "up" })
place(x, y, facing)
U.wait(90)
end
local function mashUntil(cond, tries)
for _ = 1, (tries or 80) do
if cond() then return true end
if messageOpen() or Choice.isOpen() then U.tap(game, "a") end
U.wait(6)
end
return cond()
end
local function fadeState()
return string.format("Fade.t=%s active=%s mode=%s", tostring(Fade.t),
tostring(Fade.isActive()), tostring(Fade.mode))
end
Party.giveMon(session, 1, 20)
goTo(LOBBY, 9, 12, "up")
result(session.map == LOBBY, "the player is in the Trainer Tower lobby")
for _ = 1, 8 do
if Player.cellY <= 7 or vmRunning() then break end
U.hold(game, "up", 20)
U.wait(8)
end
result(mashUntil(function() return Choice.isOpen() end, 60),
"the receptionist asked whether to challenge the trainers")
U.tap(game, "a")
U.wait(30)
result(mashUntil(function() return Choice.isOpen() end, 60), "the challenge mode list opened")
U.tap(game, "a")
for _ = 1, 40 do
if not messageOpen() and not vmRunning() then break end
if messageOpen() then U.tap(game, "a") end
U.wait(8)
end
result(Tower.getChallengeId(session) == Tower.CHALLENGE_TYPE.SINGLE,
"a SINGLE challenge is running")
result(Tower.isTimerRunning(session), "the challenge clock started")
U.wait(120)
place(8, 11, "up")
U.wait(20)
local clockBefore = Tower.record(session).timer
U.tap(game, "a")
local opened = false
for _ = 1, 120 do
if Screen.isOpen() then opened = true break end
U.wait(4)
end
result(opened, "the lobby records board opened on A")
if not opened then
print("[driver] " .. fadeState() .. " vm=" .. tostring(vmRunning()))
U.shot(game, DIR .. "/tower_records_00_no_board.png")
return
end
result(Screen.kind() == "tower", "TrainerTower_Lobby_EventScript_ShowRecords set VAR_0x8004 = 1")
for _ = 1, 60 do
if not Fade.isActive() then break end
U.wait(2)
end
print("[driver] board up: " .. fadeState())
result((tonumber(Fade.t) or 0) == 0,
"the board took the script's FADE_TO_BLACK back instead of leaving a black screen")
local rows = Screen.rows()
local labels = {}
for i, row in ipairs(rows) do labels[i] = tostring(row.label) end
print("[driver] rows = " .. table.concat(labels, "/") .. " first time = "
.. tostring(rows[1] and rows[1].time))
result(#rows == 4 and table.concat(labels, "/") == "SINGLE/DOUBLE/KNOCKOUT/MIXED",
"the board lists the four challenge types")
U.wait(60)
local clockDuring = Tower.record(session).timer
print(string.format("[driver] clock %s -> %s behind the board", tostring(clockBefore),
tostring(clockDuring)))
result(clockDuring > clockBefore,
"the challenge clock keeps running behind the board (pokefirered/src/main.c:390)")
U.shot(game, DIR .. "/tower_records_01_board.png")
U.tap(game, "a")
local closed = false
for _ = 1, 120 do
if not Screen.isOpen() then closed = true break end
U.wait(4)
end
result(closed, "A closed the board")
for _ = 1, 90 do
if not Fade.isActive() and not vmRunning() then break end
U.wait(2)
end
print("[driver] after close: " .. fadeState() .. " vm=" .. tostring(vmRunning()))
result((tonumber(Fade.t) or 0) == 0, "the lobby is lit again, not the stitch round's black screen")
result(not vmRunning(), "waitstate released and the script ran releaseall")
U.shot(game, DIR .. "/tower_records_02_lobby_lit.png")
-- pokefirered/src/field_control_avatar.c:538 TrainerTower_EventScript_ShowTime
goTo(FLOOR_1F, 10, 14, "up")
U.wait(40)
local mx, my
for y = 0, 31 do
for x = 0, 31 do
if Collision.behavior(x, y) == MB_TRAINER_TOWER_MONITOR then
mx, my = x, y
break
end
end
if mx then break end
end
print("[driver] trainer tower monitor at " .. tostring(mx) .. "," .. tostring(my))
if result(mx ~= nil, "1F carries the 0xA3 Trainer Tower monitor tile") then
place(mx, my + 1, "up")
U.wait(20)
U.tap(game, "a")
local showed = mashUntil(function() return messageOpen() end, 60)
result(showed, "the monitor ran ttower_gettime and opened the clock message")
if showed then
U.wait(45)
U.shot(game, DIR .. "/tower_records_03_floor_clock.png")
for _ = 1, 40 do
if not messageOpen() and not vmRunning() then break end
U.tap(game, "a")
U.wait(8)
end
end
end
-- pokefirered/data/scripts/trainer_tower.inc:265 TrainerTower_EventScript_SpeakToOwner
goTo(ROOF, 9, 8, "up")
result(session.map == ROOF, "the player reached the roof")
U.wait(40)
local runTime = Tower.record(session).timer
U.tap(game, "a")
local spoke = mashUntil(function() return messageOpen() or vmRunning() end, 60)
result(spoke, "the tower owner answered")
U.wait(60)
U.shot(game, DIR .. "/tower_records_04_roof_owner.png")
local prize = Tower.prizeItem(session, Tower.CHALLENGE_TYPE.SINGLE)
local shotPrize = false
for _ = 1, 200 do
if not shotPrize and messageOpen() and Bag.has(session.bag, prize, 1) then
shotPrize = true
-- pokefirered/data/scripts/trainer_tower.inc:287 Text_ObtainedTheX
for _ = 1, 4 do
U.tap(game, "a")
U.wait(30)
end
U.wait(120)
U.shot(game, DIR .. "/tower_records_05_prize.png")
end
if not messageOpen() and not vmRunning() then break end
U.tap(game, "a")
U.wait(6)
end
result(shotPrize, "the prize message was on screen while the item was in the bag")
local rec = Tower.record(session)
print(string.format("[driver] prize item %s held=%s spokeToOwner=%s prize=%s"
.. " checkedFinalTime=%s best=%s runTime=%s", tostring(prize),
tostring(Bag.has(session.bag, prize, 1)), tostring(rec.spokeToOwner),
tostring(rec.receivedPrize), tostring(rec.checkedFinalTime), tostring(rec.bestTime),
tostring(runTime)))
result(rec.spokeToOwner, "GetOwnerState marked the owner spoken to")
result(rec.receivedPrize and Bag.has(session.bag, prize, 1),
"the roof owner handed the SINGLE prize over and it is in the bag")
result(rec.checkedFinalTime and rec.bestTime < Tower.MAX_TIME,
"CheckFinalTime took the new record branch")
result(rec.bestTime >= runTime, "and the record is this run's clock")
result(not Tower.isTimerRunning(session), "the clock stopped when the owner was reached")
goTo(LOBBY, 8, 11, "up")
U.wait(40)
U.tap(game, "a")
local reopened = false
for _ = 1, 120 do
if Screen.isOpen() then reopened = true break end
U.wait(4)
end
result(reopened, "the board opened again after the run")
if reopened then
for _ = 1, 60 do
if not Fade.isActive() then break end
U.wait(2)
end
rows = Screen.rows()
print("[driver] SINGLE row now reads " .. tostring(rows[1] and rows[1].time)
.. ", DOUBLE row " .. tostring(rows[2] and rows[2].time))
result(rows[1] and rows[1].time ~= "59MIN. 59.99SEC.",
"the SINGLE row shows the record the run just set")
result(rows[2] and rows[2].time == "59MIN. 59.99SEC.",
"and the modes that were never played still show the ceiling")
U.shot(game, DIR .. "/tower_records_06_new_record.png")
U.tap(game, "a")
for _ = 1, 120 do
if not Screen.isOpen() then break end
U.wait(4)
end
for _ = 1, 90 do
if not Fade.isActive() then break end
U.wait(2)
end
result(not Screen.isOpen() and (tonumber(Fade.t) or 0) == 0,
"and it closed back to a lit lobby")
end
end
return function(game)
local ok, err = pcall(run, game)
if not ok then
print("FAIL tower_records driver error: " .. tostring(err))
failures = failures + 1
end
finish()
end