mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
283 lines
9.7 KiB
Lua
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
|