mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
239 lines
8.5 KiB
Lua
239 lines
8.5 KiB
Lua
#!/usr/bin/env luajit
|
|
-- pokefirered/src/battle_records.c:83, src/trainer_tower.c:899, :1054
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local failed = 0
|
|
local function check(cond, msg)
|
|
if cond then
|
|
print("[ok] " .. msg)
|
|
else
|
|
failed = failed + 1
|
|
print("[FAIL] " .. msg)
|
|
end
|
|
end
|
|
|
|
local function eq(a, b, msg)
|
|
check(a == b, string.format("%s (%s == %s)", msg, tostring(a), tostring(b)))
|
|
end
|
|
|
|
local Ctx = require("src.core.game3.scripting.ctx")
|
|
local Flags = require("src.core.game3.scripting.flags")
|
|
local Std = require("src.core.game3.scripting.stdscripts")
|
|
local Natives = require("src.core.game3.scripting.natives")
|
|
local Ops = require("src.core.game3.scripting.ops_a")
|
|
local Bag = require("src.core.game3.bag")
|
|
local Task = require("src.core.game3.task")
|
|
local Stack = require("src.ui.game3.stack")
|
|
local Fade = require("src.ui.game3.fade")
|
|
local Tower = require("src.core.game3.trainer_tower")
|
|
local Screen = require("src.ui.game3.trainer_tower_records")
|
|
|
|
local store = Flags.newStore()
|
|
package.loaded["src.core.game3.scripting.space"] = { store = store }
|
|
|
|
local adapters = { log = function() end }
|
|
|
|
local function newCtx()
|
|
local ctx = Ctx.new({})
|
|
ctx.mode = "bytecode"
|
|
ctx.status = "running"
|
|
return ctx
|
|
end
|
|
|
|
local function useSession(tbl)
|
|
package.loaded["src.core.game3.runtime"] = {
|
|
getSession = function() return tbl end,
|
|
isActive = function() return true end,
|
|
}
|
|
return tbl
|
|
end
|
|
|
|
local function newSession()
|
|
return useSession({
|
|
name = "RED",
|
|
gender = 0,
|
|
trainerId = 4242,
|
|
party = {},
|
|
bag = Bag.new(),
|
|
map = "FR_TRAINER_TOWER_LOBBY",
|
|
modData = {},
|
|
})
|
|
end
|
|
|
|
local function setVar(ctx, id, v) Flags.setVar(store, ctx, id, v) end
|
|
|
|
local function dispatch(ctx, row)
|
|
return Ops.dispatch({ ctx = ctx, store = store, adapters = adapters, setPc = function() end }, row)
|
|
end
|
|
|
|
-- pokefirered/asm/macros/trainer_tower.inc:46 ttower_startchallenge
|
|
local function startChallenge(ctx, mode)
|
|
setVar(ctx, 0x8004, Tower.FUNC.START_CHALLENGE)
|
|
setVar(ctx, 0x8005, mode)
|
|
dispatch(ctx, { op = "special", id = Std.SPECIAL.CallTrainerTowerFunc })
|
|
end
|
|
|
|
local function tick(n, input)
|
|
for _ = 1, (n or 1) do
|
|
Fade.tick(1 / 60)
|
|
Task.update(1 / 60)
|
|
if Screen.isOpen() then
|
|
Screen.update(1 / 60)
|
|
if input then Screen.handleInput(input) end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function press(key)
|
|
return {
|
|
wasPressed = function(_, k) return k == key end,
|
|
isDown = function() return false end,
|
|
}
|
|
end
|
|
|
|
-- pokefirered/data/maps/TrainerTower_Lobby/scripts.inc:209 fadescreen FADE_TO_BLACK
|
|
local function coverScreen()
|
|
Fade.begin(Fade.MODE.TO_BLACK, 1, function() end)
|
|
for _ = 1, 40 do Fade.tick(1 / 60) end
|
|
return (not Fade.isActive()) and Fade.t == 16
|
|
end
|
|
|
|
print("[test] 1. ShowBattleRecords is bound with pret's special id")
|
|
eq(Std.SPECIAL.ShowBattleRecords, 0xC4, "ShowBattleRecords is special 196")
|
|
check(Natives.ALLOW["special:" .. Std.SPECIAL.ShowBattleRecords] ~= nil,
|
|
"special 0xC4 reaches a handler instead of the unknown-special log")
|
|
|
|
print("[test] 2. the lobby board takes the script's black screen back")
|
|
local session = newSession()
|
|
local ctx = newCtx()
|
|
Stack.clear()
|
|
Task.clear()
|
|
Fade.clear()
|
|
check(coverScreen(), "the script's fadescreen FADE_TO_BLACK covered the screen")
|
|
eq(Fade.mode, Fade.MODE.TO_BLACK, "and it is a black cover, not a white one")
|
|
-- pokefirered/data/maps/TrainerTower_Lobby/scripts.inc:210
|
|
setVar(ctx, 0x8004, 1)
|
|
local yielded = dispatch(ctx, { op = "special", id = Std.SPECIAL.ShowBattleRecords })
|
|
check(yielded, "the special yielded the script the way waitstate expects")
|
|
dispatch(ctx, { op = "waitstate" })
|
|
check(Screen.isOpen(), "the records screen is up")
|
|
eq(Screen.kind(), "tower", "VAR_0x8004 = 1 selects the trainer tower records")
|
|
-- pokefirered/src/battle_records.c:130 BeginNormalPaletteFade(PALETTES_ALL, 0, 16, 0)
|
|
eq(Fade.mode, Fade.MODE.FROM_BLACK, "the screen is fading back in, not sitting black")
|
|
tick(40)
|
|
eq(Fade.t, 0, "the black is gone once the fade in finishes")
|
|
check(not ctx.nativePoll(), "the script is still blocked on waitstate")
|
|
|
|
print("[test] 3. the board lists the four challenge types and their best times")
|
|
local rows = Screen.rows()
|
|
eq(#rows, Tower.NUM_CHALLENGE_TYPES, "one row per challenge type")
|
|
local labels = {}
|
|
for i, row in ipairs(rows) do labels[i] = row.label end
|
|
eq(table.concat(labels, "/"), "SINGLE/DOUBLE/KNOCKOUT/MIXED",
|
|
"pokefirered/src/battle_message.c:1364 gTrainerTowerChallengeTypeTexts")
|
|
for i, row in ipairs(rows) do
|
|
eq(row.time, "59MIN. 59.99SEC.", "row " .. i .. " of an untouched save is the ceiling time")
|
|
end
|
|
|
|
print("[test] 4. A closes the screen and hands the field back lit")
|
|
local input = press("a")
|
|
tick(1, input)
|
|
check(Screen.isOpen(), "the close fade is still running")
|
|
eq(Fade.mode, Fade.MODE.TO_BLACK, "pokefirered/src/battle_records.c:179 Task_FadeOut")
|
|
tick(60, input)
|
|
check(not Screen.isOpen(), "the screen closed")
|
|
check(not Stack.busy(), "and left no layer behind")
|
|
check(ctx.nativePoll(), "waitstate released the script")
|
|
tick(40)
|
|
eq(Fade.t, 0, "the field is lit again, not the black screen of the stitch round repro")
|
|
check(not Fade.isActive(), "and nothing is still fading")
|
|
|
|
print("[test] 5. B closes it the same way")
|
|
Fade.clear()
|
|
ctx = newCtx()
|
|
setVar(ctx, 0x8004, 1)
|
|
dispatch(ctx, { op = "special", id = Std.SPECIAL.ShowBattleRecords })
|
|
dispatch(ctx, { op = "waitstate" })
|
|
tick(40)
|
|
check(Screen.isOpen(), "the screen is up")
|
|
tick(80, press("b"))
|
|
check(not Screen.isOpen(), "B closed it (pokefirered/src/battle_records.c:174)")
|
|
check(ctx.nativePoll(), "and released the script")
|
|
tick(40)
|
|
eq(Fade.t, 0, "with the screen handed back lit")
|
|
|
|
print("[test] 6. a beaten mode shows its own record, the others the ceiling")
|
|
session = newSession()
|
|
Task.clear()
|
|
Tower.setBestTime(session, 3661, Tower.CHALLENGE_TYPE.KNOCKOUT)
|
|
ctx = newCtx()
|
|
Fade.clear()
|
|
setVar(ctx, 0x8004, 1)
|
|
dispatch(ctx, { op = "special", id = Std.SPECIAL.ShowBattleRecords })
|
|
dispatch(ctx, { op = "waitstate" })
|
|
rows = Screen.rows()
|
|
eq(rows[3].time, " 1MIN. 1.01SEC.", "the KNOCKOUT row shows the stored record")
|
|
eq(rows[1].time, "59MIN. 59.99SEC.", "the SINGLE row is still unbeaten")
|
|
tick(80, press("a"))
|
|
tick(40)
|
|
check(not Screen.isOpen(), "the screen closed again")
|
|
|
|
print("[test] 7. VAR_0x8004 = 0 is the cable club's link battle record screen")
|
|
-- pokefirered/data/scripts/cable_club.inc:569
|
|
session = newSession()
|
|
session.linkBattleWins = 12
|
|
session.linkBattleLosses = 3
|
|
session.linkBattleDraws = 1
|
|
session.linkBattleRecords = { { name = "BLUE", wins = 7, losses = 2, draws = 1 } }
|
|
ctx = newCtx()
|
|
Fade.clear()
|
|
check(coverScreen(), "the cable club script covered the screen too")
|
|
setVar(ctx, 0x8004, 0)
|
|
dispatch(ctx, { op = "special", id = Std.SPECIAL.ShowBattleRecords })
|
|
dispatch(ctx, { op = "waitstate" })
|
|
eq(Screen.kind(), "link", "VAR_0x8004 = 0 selects the link battle records")
|
|
rows = Screen.rows()
|
|
eq(#rows, Screen.LINK_ROWS, "five opponent rows (pokefirered/include/global.h:238)")
|
|
eq(rows[1].name, "BLUE", "the recorded opponent is listed")
|
|
eq(rows[1].wins, " 7", "with the wins right aligned in four")
|
|
eq(rows[2].name, "-------", "an empty slot is dashes (pokefirered/src/strings.c:599)")
|
|
eq(rows[2].draws, "----", "and so are its columns")
|
|
eq(Screen.totalText(session), "TOTAL RECORD W:12 L:3 D:1 ",
|
|
"pokefirered/src/strings.c:597 left aligns each count in four")
|
|
tick(80, press("a"))
|
|
tick(40)
|
|
eq(Fade.t, 0, "the cable club tile no longer leaves a black screen")
|
|
check(not Screen.isOpen(), "and the screen closed")
|
|
|
|
print("[test] 8. the unused in-field board keeps its cart quirks")
|
|
session = newSession()
|
|
Task.clear()
|
|
Fade.clear()
|
|
Stack.clear()
|
|
ctx = newCtx()
|
|
startChallenge(ctx, Tower.CHALLENGE_TYPE.DOUBLE)
|
|
Tower.setBestTime(session, 600, Tower.CHALLENGE_TYPE.DOUBLE)
|
|
Tower.setBestTime(session, 60, Tower.CHALLENGE_TYPE.SINGLE)
|
|
Screen.show({ session = session, kind = "board" })
|
|
rows = Screen.rows()
|
|
-- pokefirered/src/trainer_tower.c:912 GetTrainerTowerRecordTime(&TRAINER_TOWER.bestTime)
|
|
for i, row in ipairs(rows) do
|
|
eq(row.time, " 0MIN. 10.00SEC.", "board row " .. i .. " shows the current mode's record")
|
|
end
|
|
-- pokefirered/src/trainer_tower.c:915 indexes the label table at i - 1
|
|
eq(rows[1].label, "", "the first board row has no label on the cart")
|
|
eq(rows[2].label, "SINGLE", "and the rest are shifted by one")
|
|
eq(rows[4].label, "KNOCKOUT", "so MIXED is never shown")
|
|
eq(Fade.t, 0, "the board is a field window, so it never blacks the screen out")
|
|
Screen.close()
|
|
check(not Screen.isOpen(), "the board closed")
|
|
Stack.clear()
|
|
Task.clear()
|
|
|
|
if failed > 0 then
|
|
print(string.format("[FAIL] %d check(s) failed", failed))
|
|
os.exit(1)
|
|
end
|
|
print("[pass] trainer tower records screen")
|