mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 05:32:07 -04:00
380 lines
14 KiB
Lua
380 lines
14 KiB
Lua
#!/usr/bin/env luajit
|
|
-- pokefirered/src/fame_checker.c:729
|
|
|
|
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 Cache = require("tests.game3_cache")
|
|
local cacheRoot = Cache.mount("trainers.lua")
|
|
if not cacheRoot then
|
|
print("[skip] game3_fame_screen: " .. tostring(Cache.reason))
|
|
os.exit(0)
|
|
end
|
|
print("[info] FireRed cache at " .. cacheRoot)
|
|
|
|
local FameChecker = require("src.core.game3.fame_checker")
|
|
local Ui = require("src.ui.game3.fame_checker")
|
|
local Stack = require("src.ui.game3.stack")
|
|
|
|
local PERSON = FameChecker.PERSON
|
|
local PICK = FameChecker.PICKSTATE
|
|
local NSLOT = FameChecker.NUM_FLAVOR_TEXTS
|
|
|
|
local function press(key)
|
|
return { wasPressed = function(_, k) return k == key end }
|
|
end
|
|
|
|
local function newSession()
|
|
return { modData = {}, name = "RED", rivalName = "BLUE" }
|
|
end
|
|
|
|
local function openWith(session, opts)
|
|
Stack.clear()
|
|
Ui.show(session, opts)
|
|
end
|
|
|
|
local function labels()
|
|
local out = {}
|
|
for i, row in ipairs(Ui.rows()) do out[i] = row.label end
|
|
return out
|
|
end
|
|
|
|
print("[test] 1. the list carries the drawn people and CANCEL")
|
|
do
|
|
local s = newSession()
|
|
openWith(s)
|
|
local rows = Ui.rows()
|
|
eq(#rows, 2, "a new game lists OAK and CANCEL")
|
|
eq(rows[1].person, PERSON.OAK, "OAK is the first row")
|
|
check(rows[2].cancel == true, "the last row is CANCEL")
|
|
-- pokefirered/src/strings.c:128 gFameCheckerText_Cancel
|
|
eq(rows[2].label, "CANCEL", "the CANCEL label")
|
|
|
|
FameChecker.updatePickState(PERSON.BROCK, PICK.COLORED, s)
|
|
FameChecker.updatePickState(PERSON.MISTY, PICK.SILHOUETTE, s)
|
|
rows = Ui.rows()
|
|
eq(#rows, 4, "two more people join the list")
|
|
eq(rows[2].person, PERSON.BROCK, "BROCK is row 2")
|
|
eq(rows[3].person, PERSON.MISTY, "MISTY is row 3")
|
|
eq(rows[2].label, "BROCK", "BROCK's list label comes from gTrainers")
|
|
eq(rows[3].label, "MISTY", "MISTY's list label comes from gTrainers")
|
|
check(rows[4].cancel == true, "CANCEL stays last")
|
|
|
|
local names = table.concat(labels(), ",")
|
|
check(not names:find("KOGA"), "an undrawn person is not listed (" .. names .. ")")
|
|
end
|
|
|
|
print("[test] 2. the non-trainer names")
|
|
do
|
|
local s = newSession()
|
|
FameChecker.fullyUnlock(s)
|
|
openWith(s)
|
|
-- pokefirered/src/strings.c:1272
|
|
eq(Ui.personName(PERSON.OAK), "OAK", "OAK")
|
|
eq(Ui.personName(PERSON.DAISY), "DAISY", "DAISY")
|
|
eq(Ui.personName(PERSON.BILL), "BILL", "BILL")
|
|
eq(Ui.personName(PERSON.MRFUJI), "FUJI", "FUJI")
|
|
eq(Ui.personName(PERSON.GIOVANNI), "GIOVANNI", "GIOVANNI comes from gTrainers")
|
|
eq(#Ui.rows(), FameChecker.NUM_PERSONS + 1, "a fully unlocked checker lists all 16 plus CANCEL")
|
|
end
|
|
|
|
print("[test] 3. the per-person page shows only the unlocked slots")
|
|
do
|
|
local s = newSession()
|
|
FameChecker.updatePickState(PERSON.BROCK, PICK.COLORED, s)
|
|
FameChecker.setFlavorText(PERSON.BROCK, 1, s)
|
|
FameChecker.setFlavorText(PERSON.BROCK, 4, s)
|
|
openWith(s)
|
|
Ui.handleInput(press("down"))
|
|
eq(Ui.selectedPerson(), PERSON.BROCK, "the cursor is on BROCK")
|
|
local icons = Ui.icons(PERSON.BROCK)
|
|
local unlocked = {}
|
|
for slot = 0, NSLOT - 1 do
|
|
if icons[slot].unlocked then unlocked[#unlocked + 1] = slot end
|
|
end
|
|
eq(#unlocked, 2, "two of the six panels are unlocked")
|
|
eq(unlocked[1], 1, "slot 1 is unlocked")
|
|
eq(unlocked[2], 4, "slot 4 is unlocked")
|
|
-- pokefirered/src/fame_checker.c:265 sFameCheckerArrayNpcGraphicsIds
|
|
eq(icons[1].graphicsId, 80, "slot 1 draws OBJ_EVENT_GFX_BROCK")
|
|
eq(icons[4].graphicsId, 30, "slot 4 draws OBJ_EVENT_GFX_BALDING_MAN")
|
|
eq(icons[0].graphicsId, nil, "a locked slot has no icon graphic")
|
|
eq(icons[2].graphicsId, nil, "a locked slot has no icon graphic")
|
|
check(Ui.personHasUnlockedPanels(PERSON.BROCK), "BROCK has unlocked panels")
|
|
check(not Ui.personHasUnlockedPanels(PERSON.KOGA), "KOGA has none")
|
|
end
|
|
|
|
print("[test] 4. the pick mode picture")
|
|
do
|
|
local s = newSession()
|
|
FameChecker.updatePickState(PERSON.BROCK, PICK.COLORED, s)
|
|
FameChecker.updatePickState(PERSON.MISTY, PICK.SILHOUETTE, s)
|
|
openWith(s)
|
|
-- pokefirered/src/fame_checker.c:171 sFameCheckerTrainerPicIdxs
|
|
local source, path = Ui.portraitSource(PERSON.BROCK)
|
|
eq(source, "trainer", "BROCK uses CreateTrainerPicSprite")
|
|
check(path:find("/trainers/front/116%.rgba$") ~= nil,
|
|
"TRAINER_PIC_LEADER_BROCK is pic 116 (" .. tostring(path) .. ")")
|
|
local f = io.open(path, "rb")
|
|
check(f ~= nil, "the trainer pic is in the cache today")
|
|
if f then
|
|
local bytes = f:read("*a")
|
|
f:close()
|
|
eq(#bytes, 64 * 64 * 4, "it is a 64x64 RGBA sprite")
|
|
end
|
|
|
|
-- pokefirered/src/fame_checker.c:1347
|
|
for _, p in ipairs({ PERSON.OAK, PERSON.DAISY, PERSON.BILL, PERSON.MRFUJI }) do
|
|
local src2, path2 = Ui.portraitSource(p)
|
|
eq(src2, "art", "person " .. p .. " uses the Fame Checker's own art")
|
|
check(path2:find("/fame_checker/" .. p .. "%.rgba$") ~= nil,
|
|
"person " .. p .. " reads " .. tostring(path2))
|
|
end
|
|
eq(Ui.portraitSource(FameChecker.NUM_PERSONS), nil, "person 16 has no picture")
|
|
|
|
-- pokefirered/src/fame_checker.c:1374 sSilhouettePalette
|
|
eq(FameChecker.pickState(s, PERSON.MISTY), PICK.SILHOUETTE,
|
|
"MISTY is drawn as a silhouette")
|
|
eq(FameChecker.pickState(s, PERSON.BROCK), PICK.COLORED, "BROCK is drawn coloured")
|
|
end
|
|
|
|
print("[test] 5. the art and text missing fallback")
|
|
do
|
|
local s = newSession()
|
|
FameChecker.updatePickState(PERSON.BROCK, PICK.COLORED, s)
|
|
FameChecker.setFlavorText(PERSON.BROCK, 1, s)
|
|
openWith(s)
|
|
Ui.handleInput(press("down"))
|
|
Ui.handleInput(press("start"))
|
|
check(Ui.pickMode, "START put BROCK in pick mode")
|
|
local pack = Ui.pack()
|
|
if pack == nil then
|
|
print("[info] no fame_checker pack in this cache; checking the fallback")
|
|
eq(Ui.flavorText(PERSON.BROCK, 1), nil, "no flavour text without the pack")
|
|
eq(Ui.pickModeText(PERSON.BROCK), nil, "no name or quote text without the pack")
|
|
eq(Ui.messageText(), "BROCK", "pick mode falls back to the name-only list entry")
|
|
Ui.handleInput(press("start"))
|
|
check(not Ui.pickMode, "START left pick mode again")
|
|
Ui.handleInput(press("a"))
|
|
eq(Ui.mode, "flavor", "the page still opens")
|
|
-- pokefirered/src/fame_checker.c:944
|
|
eq(Ui.messageText(), nil, "a locked icon leaves the message box empty")
|
|
Ui.handleInput(press("right"))
|
|
eq(Ui.iconCursor, 1, "the cursor is on the unlocked icon")
|
|
eq(Ui.messageText(), "BROCK", "the page falls back to the name-only entry")
|
|
else
|
|
print("[info] fame_checker pack present; checking the real text")
|
|
local text = Ui.flavorText(PERSON.BROCK, 1)
|
|
check(type(text) == "string" and #text > 0, "BROCK slot 1 has flavour text")
|
|
check(type(Ui.pickModeText(PERSON.BROCK)) == "string", "BROCK has a name string")
|
|
local msg = Ui.messageText()
|
|
check(type(msg) == "string" and #msg > 0, "pick mode prints it")
|
|
end
|
|
end
|
|
|
|
print("[test] 6. START enters and leaves pick mode")
|
|
do
|
|
local s = newSession()
|
|
FameChecker.updatePickState(PERSON.BROCK, PICK.COLORED, s)
|
|
openWith(s)
|
|
Ui.handleInput(press("down"))
|
|
check(not Ui.pickMode, "pick mode starts off")
|
|
Ui.handleInput(press("start"))
|
|
check(Ui.pickMode, "START enters pick mode")
|
|
Ui.handleInput(press("start"))
|
|
check(not Ui.pickMode, "START leaves it again")
|
|
Ui.handleInput(press("start"))
|
|
check(Ui.pickMode, "START enters it once more")
|
|
Ui.handleInput(press("b"))
|
|
check(not Ui.pickMode, "B leaves pick mode instead of closing")
|
|
check(Ui.isOpen(), "the screen is still open")
|
|
|
|
Ui.handleInput(press("down"))
|
|
check(Ui.selectedRow().cancel == true, "the cursor walked down to CANCEL")
|
|
Ui.handleInput(press("start"))
|
|
check(not Ui.pickMode, "START over CANCEL does nothing")
|
|
end
|
|
|
|
print("[test] 7. A opens the flavour text page, B backs out, CANCEL closes")
|
|
do
|
|
local s = newSession()
|
|
FameChecker.updatePickState(PERSON.KOGA, PICK.SILHOUETTE, s)
|
|
openWith(s)
|
|
Ui.handleInput(press("down"))
|
|
eq(Ui.selectedPerson(), PERSON.KOGA, "KOGA is row 2")
|
|
Ui.handleInput(press("a"))
|
|
eq(Ui.mode, "top", "A does nothing for a person with no unlocked panels")
|
|
|
|
FameChecker.setFlavorText(PERSON.KOGA, 0, s)
|
|
Ui.handleInput(press("a"))
|
|
eq(Ui.mode, "flavor", "A opens the page once a panel is unlocked")
|
|
Ui.handleInput(press("b"))
|
|
eq(Ui.mode, "top", "B returns to the list")
|
|
check(Ui.isOpen(), "and leaves the screen open")
|
|
|
|
Ui.handleInput(press("down"))
|
|
check(Ui.selectedRow().cancel == true, "the cursor is on CANCEL")
|
|
Ui.handleInput(press("a"))
|
|
check(not Ui.isOpen(), "A on CANCEL closes the Fame Checker")
|
|
check(not Stack.has("fame_checker"), "and pops the UI stack layer")
|
|
end
|
|
|
|
print("[test] 8. B and SELECT close from the top menu")
|
|
do
|
|
local s = newSession()
|
|
openWith(s)
|
|
Ui.handleInput(press("b"))
|
|
check(not Ui.isOpen(), "B closes from the top menu")
|
|
|
|
-- pokefirered/src/fame_checker.c:741
|
|
openWith(s)
|
|
Ui.handleInput(press("select"))
|
|
check(not Ui.isOpen(), "SELECT closes when opened from the field")
|
|
|
|
openWith(s, { fromBag = true })
|
|
Ui.handleInput(press("select"))
|
|
check(Ui.isOpen(), "SELECT does nothing when opened from the bag")
|
|
Ui.close()
|
|
end
|
|
|
|
print("[test] 9. the icon cursor wraps the way pret wraps it")
|
|
do
|
|
local s = newSession()
|
|
FameChecker.updatePickState(PERSON.BROCK, PICK.COLORED, s)
|
|
for slot = 0, NSLOT - 1 do FameChecker.setFlavorText(PERSON.BROCK, slot, s) end
|
|
openWith(s)
|
|
Ui.handleInput(press("down"))
|
|
Ui.handleInput(press("a"))
|
|
eq(Ui.mode, "flavor", "the page is open")
|
|
eq(Ui.iconCursor, 0, "it starts on slot 0")
|
|
|
|
-- pokefirered/src/fame_checker.c:889
|
|
Ui.handleInput(press("right"))
|
|
eq(Ui.iconCursor, 1, "right moves along the row")
|
|
Ui.handleInput(press("right"))
|
|
eq(Ui.iconCursor, 2, "right again")
|
|
Ui.handleInput(press("right"))
|
|
eq(Ui.iconCursor, 0, "right off the end of the row wraps back to its start")
|
|
Ui.handleInput(press("left"))
|
|
eq(Ui.iconCursor, 2, "left off the start wraps to the end of the row")
|
|
Ui.handleInput(press("down"))
|
|
eq(Ui.iconCursor, 5, "down moves to the second row")
|
|
Ui.handleInput(press("down"))
|
|
eq(Ui.iconCursor, 2, "down again comes back to the first row")
|
|
Ui.handleInput(press("up"))
|
|
eq(Ui.iconCursor, 5, "up is the same move as down")
|
|
Ui.handleInput(press("left"))
|
|
eq(Ui.iconCursor, 4, "left on the second row")
|
|
Ui.handleInput(press("right"))
|
|
eq(Ui.iconCursor, 5, "right back")
|
|
|
|
Ui.handleInput(press("b"))
|
|
Ui.handleInput(press("down"))
|
|
eq(Ui.iconCursor, 0, "moving the list cursor resets the icon cursor")
|
|
Ui.close()
|
|
end
|
|
|
|
print("[test] 10. the UI help line tracks pret's three states")
|
|
do
|
|
local s = newSession()
|
|
FameChecker.updatePickState(PERSON.BROCK, PICK.COLORED, s)
|
|
openWith(s)
|
|
Ui.handleInput(press("down"))
|
|
-- pokefirered/src/strings.c:1270 gFameCheckerText_PickScreenUI
|
|
eq(Ui.helpText(), "{START_BUTTON}PICK {DPAD_UPDOWN}SELECT {B_BUTTON}CANCEL",
|
|
"a person with no unlocked panels shows PrintUIHelp(1)")
|
|
FameChecker.setFlavorText(PERSON.BROCK, 0, s)
|
|
-- pokefirered/src/strings.c:1269 gFameCheckerText_MainScreenUI
|
|
eq(Ui.helpText(), "{START_BUTTON}PICK {DPAD_UPDOWN}SELECT {A_BUTTON}OK",
|
|
"once a panel is unlocked it shows PrintUIHelp(0)")
|
|
Ui.handleInput(press("start"))
|
|
eq(Ui.helpText(), "{START_BUTTON}PICK {DPAD_UPDOWN}SELECT {B_BUTTON}CANCEL",
|
|
"pick mode shows PrintUIHelp(1)")
|
|
Ui.handleInput(press("start"))
|
|
Ui.handleInput(press("a"))
|
|
-- pokefirered/src/strings.c:1271 gFameCheckerText_FlavorTextUI
|
|
eq(Ui.helpText(), "{DPAD_ANY}PICK {A_BUTTON}READ {B_BUTTON}CANCEL",
|
|
"the flavour text page shows PrintUIHelp(2)")
|
|
Ui.close()
|
|
end
|
|
|
|
print("[test] 11. the CANCEL row describes itself")
|
|
do
|
|
local s = newSession()
|
|
openWith(s)
|
|
Ui.handleInput(press("down"))
|
|
-- pokefirered/src/strings.c:601 gFameCheckerText_FameCheckerWillBeClosed
|
|
eq(Ui.messageText(), "The FAME CHECKER will be closed.",
|
|
"PrintCancelDescription runs on the CANCEL row")
|
|
Ui.handleInput(press("up"))
|
|
eq(Ui.messageText(), nil, "a person row leaves the message box empty")
|
|
Ui.close()
|
|
end
|
|
|
|
print("[test] 12. the list scrolls the way pret's ListMenu scrolls")
|
|
do
|
|
local s = newSession()
|
|
for p = PERSON.BROCK, PERSON.BLAINE do
|
|
FameChecker.updatePickState(p, PICK.COLORED, s)
|
|
end
|
|
openWith(s)
|
|
eq(#Ui.rows(), 9, "eight drawn people plus CANCEL")
|
|
local function row() return Ui.cursor - 1 - Ui.scroll end
|
|
eq(Ui.scroll, 0, "the list starts unscrolled")
|
|
eq(row(), 0, "and on its first row")
|
|
|
|
-- pokefirered/src/list_menu.c:438 ListMenuUpdateSelectedRowIndexAndScrollOffset
|
|
for i = 1, 3 do
|
|
Ui.handleInput(press("down"))
|
|
eq(Ui.scroll, 0, "down " .. i .. " does not scroll yet")
|
|
eq(row(), i, "down " .. i .. " walks the cursor to row " .. i)
|
|
end
|
|
Ui.handleInput(press("down"))
|
|
eq(Ui.cursor, 5, "the fourth down selects the fifth entry")
|
|
eq(Ui.scroll, 1, "and scrolls the list instead of the cursor")
|
|
eq(row(), 3, "the cursor holds on row 3 going down")
|
|
|
|
for _ = 1, 4 do Ui.handleInput(press("down")) end
|
|
eq(Ui.cursor, 9, "eight downs reach CANCEL")
|
|
eq(Ui.scroll, 4, "the list is scrolled to its end")
|
|
eq(row(), 4, "and the cursor drops to the last visible row")
|
|
Ui.handleInput(press("down"))
|
|
eq(Ui.cursor, 9, "down on the last row does not wrap")
|
|
|
|
for _ = 1, 3 do Ui.handleInput(press("up")) end
|
|
eq(Ui.cursor, 6, "three ups walk back up the window")
|
|
eq(Ui.scroll, 4, "without scrolling")
|
|
eq(row(), 1, "the cursor holds on row 1 going up")
|
|
Ui.handleInput(press("up"))
|
|
eq(Ui.cursor, 5, "the fourth up selects the fifth entry")
|
|
eq(Ui.scroll, 3, "and scrolls the list back")
|
|
eq(row(), 1, "the cursor stays on row 1")
|
|
|
|
for _ = 1, 8 do Ui.handleInput(press("up")) end
|
|
eq(Ui.cursor, 1, "eight more ups reach the top")
|
|
eq(Ui.scroll, 0, "with the list unscrolled")
|
|
Ui.handleInput(press("up"))
|
|
eq(Ui.cursor, 1, "up on the first row does not wrap")
|
|
Ui.close()
|
|
end
|
|
|
|
if failed == 0 then
|
|
print("PASS game3_fame_screen")
|
|
os.exit(0)
|
|
end
|
|
print("FAIL game3_fame_screen failures=" .. failed)
|
|
os.exit(1)
|