mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 05:32:07 -04:00
289 lines
12 KiB
Lua
289 lines
12 KiB
Lua
#!/usr/bin/env luajit
|
|
-- pokefirered/src/teachy_tv.c:420 InitTeachyTvController
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
require("tests.game3_cache").requireData("game3_teachy_model_test")
|
|
|
|
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 okReq, TeachyTv = pcall(require, "src.core.game3.teachy_tv")
|
|
if not okReq then
|
|
print("[FAIL] src/core/game3/teachy_tv.lua does not load: " .. tostring(TeachyTv))
|
|
os.exit(1)
|
|
end
|
|
|
|
local Bag = require("src.core.game3.bag")
|
|
local Schema = require("src.core.game3.save_schema_firered")
|
|
local ItemUse = require("src.core.game3.item_use")
|
|
local ItemsData = require("src.core.game3.items_data")
|
|
|
|
local RomText = require("src.core.game3.rom_text")
|
|
|
|
local S = TeachyTv.SCRIPT
|
|
|
|
local function newSession()
|
|
return { modData = {}, bag = Bag.new(), party = {} }
|
|
end
|
|
|
|
print("[test] 1. pokefirered/include/teachy_tv.h:4 enum TeachyTvScript")
|
|
eq(S.BATTLE, 0, "TTVSCR_BATTLE")
|
|
eq(S.STATUS, 1, "TTVSCR_STATUS")
|
|
eq(S.MATCHUPS, 2, "TTVSCR_MATCHUPS")
|
|
eq(S.CATCHING, 3, "TTVSCR_CATCHING")
|
|
eq(S.TMS, 4, "TTVSCR_TMS")
|
|
eq(S.REGISTER, 5, "TTVSCR_REGISTER")
|
|
eq(#TeachyTv.ORDER, 6, "six lessons")
|
|
do
|
|
local want = { S.BATTLE, S.STATUS, S.MATCHUPS, S.CATCHING, S.TMS, S.REGISTER }
|
|
local same = true
|
|
for i = 1, #want do
|
|
if TeachyTv.ORDER[i] ~= want[i] then same = false end
|
|
end
|
|
check(same, "sListMenuItems order is battle, status, matchups, catching, TMs, register")
|
|
end
|
|
|
|
print("[test] 2. pokefirered/src/data/text/teachy_tv.h:1 every lesson has both halves")
|
|
do
|
|
local labels = {
|
|
[S.BATTLE] = "Teach me how to battle.",
|
|
[S.STATUS] = "What are status problems?",
|
|
[S.MATCHUPS] = "What are type matchups?",
|
|
[S.CATCHING] = "I want to catch POKéMON.",
|
|
[S.TMS] = "Teach me about TMs.",
|
|
[S.REGISTER] = "How do I register an item?",
|
|
}
|
|
for _, id in ipairs(TeachyTv.ORDER) do
|
|
local lesson = TeachyTv.lesson(id)
|
|
check(lesson ~= nil, "lesson " .. id .. " exists")
|
|
eq(RomText.plain(lesson.labelKey), labels[id], "row label for lesson " .. id)
|
|
check(RomText.has(lesson.introKey), lesson.key .. " has a Script1 string")
|
|
check(RomText.has(lesson.outroKey), lesson.key .. " has a Script2 string")
|
|
end
|
|
end
|
|
|
|
print("[test] 3. page sequences")
|
|
do
|
|
for _, id in ipairs(TeachyTv.ORDER) do
|
|
local intro = TeachyTv.introPages(id)
|
|
local outro = TeachyTv.outroPages(id)
|
|
check(#intro >= 2, TeachyTv.lesson(id).key .. " intro pages=" .. #intro)
|
|
check(#outro >= 2, TeachyTv.lesson(id).key .. " outro pages=" .. #outro)
|
|
local blank = 0
|
|
for _, page in ipairs(intro) do
|
|
if page == "" then blank = blank + 1 end
|
|
if select(2, page:gsub("\n", "")) > 1 then blank = blank + 1 end
|
|
end
|
|
eq(blank, 0, TeachyTv.lesson(id).key .. " intro pages are non-empty and at most two lines")
|
|
end
|
|
local battle = TeachyTv.introPages(S.BATTLE)
|
|
eq(battle[1], "Today, the POKé DUDE's here to\ntell you about how you can battle",
|
|
"gTeachyTvText_BattleScript1 page 1")
|
|
eq(battle[2], "POKéMON!", "the \\l line becomes page 2")
|
|
local last = TeachyTv.outroPages(S.BATTLE)
|
|
eq(last[#last], "Remember, TRAINERS, a good deed\na day brings happiness to stay!",
|
|
"BattleScript2 ends on the sign-off")
|
|
local tms = TeachyTv.outroPages(S.TMS)
|
|
eq(tms[1], "Wow, I talked a lot today!\nAll righty, be seeing you!",
|
|
"gTeachyTvText_TMsScript2 page 1")
|
|
end
|
|
|
|
print("[test] 4. pokefirered/src/data/text/teachy_tv.h:8 the Pokedude intros")
|
|
do
|
|
local hello = TeachyTv.pagesOf(TeachyTv.HELLO)
|
|
check(#hello >= 4, "PokedudeSaysHello pages=" .. #hello)
|
|
eq(hello[1], "Hey, all you TRAINERS out there!\nHELLO, TRAINERS!", "hello page 1")
|
|
local tmTypes = TeachyTv.pagesOf(TeachyTv.TM_TYPES)
|
|
eq(tmTypes[#tmTypes], "There's one other thing!", "gPokedudeText_TMTypes last page")
|
|
local tmDesc = TeachyTv.pagesOf(TeachyTv.TM_DESCRIPTION)
|
|
check(#tmDesc >= 5, "gPokedudeText_ReadTMDescription pages=" .. #tmDesc)
|
|
end
|
|
|
|
print("[test] 5. pokefirered/src/teachy_tv.c:554 the TM CASE gate")
|
|
do
|
|
local s = newSession()
|
|
check(not TeachyTv.hasTmCase(s), "no TM CASE on a fresh bag")
|
|
local rows = TeachyTv.menuItems(s)
|
|
eq(#rows, 5, "sListMenuItems_NoTMCase is four lessons plus CANCEL")
|
|
eq(rows[5].index, TeachyTv.CANCEL, "CANCEL is the last row")
|
|
eq(rows[4].index, S.CATCHING, "the last lesson without a TM CASE is catching")
|
|
eq(TeachyTv.maxShowed(s), 5, "maxShowed 5 without a TM CASE")
|
|
|
|
Bag.add(s.bag, TeachyTv.ITEM_TM_CASE, 1)
|
|
check(TeachyTv.hasTmCase(s), "CheckBagHasItem(ITEM_TM_CASE, 1)")
|
|
rows = TeachyTv.menuItems(s)
|
|
eq(#rows, 7, "sListMenuItems is six lessons plus CANCEL")
|
|
eq(rows[5].index, S.TMS, "the TM lesson appears with a TM CASE")
|
|
eq(rows[6].index, S.REGISTER, "the register lesson appears with a TM CASE")
|
|
eq(rows[7].index, TeachyTv.CANCEL, "CANCEL stays last")
|
|
eq(TeachyTv.maxShowed(s), 6, "maxShowed 6 with a TM CASE")
|
|
end
|
|
|
|
print("[test] 6. pokefirered/src/teachy_tv.c:272 the per-lesson step clusters")
|
|
do
|
|
for _, id in ipairs({ S.BATTLE, S.STATUS, S.MATCHUPS, S.CATCHING }) do
|
|
eq(#TeachyTv.STEPS[id], 19, TeachyTv.lesson(id).key .. " runs the 19-step grass cluster")
|
|
eq(TeachyTv.STEPS[id][9], "start_anim_npc_walk_into_grass",
|
|
TeachyTv.lesson(id).key .. " walks the dude into the grass")
|
|
eq(TeachyTv.RESUME_STEP[id], 12, TeachyTv.lesson(id).key .. " resumes at step 12")
|
|
check(TeachyTv.endsInBattle(id), TeachyTv.lesson(id).key .. " hands off to a battle")
|
|
eq(TeachyTv.bagLocation(id), nil, TeachyTv.lesson(id).key .. " opens no bag")
|
|
end
|
|
for _, id in ipairs({ S.TMS, S.REGISTER }) do
|
|
eq(#TeachyTv.STEPS[id], 16, TeachyTv.lesson(id).key .. " runs the 16-step bag cluster")
|
|
eq(TeachyTv.STEPS[id][9], "battle_or_fade",
|
|
TeachyTv.lesson(id).key .. " skips the grass walk")
|
|
eq(TeachyTv.RESUME_STEP[id], 9, TeachyTv.lesson(id).key .. " resumes at step 9")
|
|
check(not TeachyTv.endsInBattle(id), TeachyTv.lesson(id).key .. " runs no battle")
|
|
end
|
|
-- pokefirered/include/constants/item_menu.h:18
|
|
eq(TeachyTv.bagLocation(S.TMS), 10, "ITEMMENULOCATION_TTVSCR_TMS")
|
|
eq(TeachyTv.bagLocation(S.REGISTER), 9, "ITEMMENULOCATION_TTVSCR_REGISTER")
|
|
-- pokefirered/src/teachy_tv.c:1181
|
|
eq(TeachyTv.battleTransition(S.BATTLE), TeachyTv.TRANSITION.WHITE_BARS_FADE,
|
|
"the battle lesson uses B_TRANSITION_WHITE_BARS_FADE")
|
|
eq(TeachyTv.battleTransition(S.CATCHING), TeachyTv.TRANSITION.SLICE,
|
|
"every other lesson uses B_TRANSITION_SLICE")
|
|
end
|
|
|
|
print("[test] 7. pokefirered/src/teachy_tv.c:420 the controller modes")
|
|
do
|
|
local s = newSession()
|
|
local res = TeachyTv.resources(s)
|
|
eq(res.mode, TeachyTv.MODE.FRESH, "a fresh block is mode 0")
|
|
eq(res.whichScript, S.BATTLE, "whichScript starts at TTVSCR_BATTLE")
|
|
eq(res.scrollOffset, 0, "scrollOffset starts at 0")
|
|
eq(res.selectedRow, 0, "selectedRow starts at 0")
|
|
|
|
TeachyTv.selectLesson(s, S.REGISTER)
|
|
res.scrollOffset, res.selectedRow = 1, 3
|
|
TeachyTv.initController(s, TeachyTv.MODE.FRESH)
|
|
eq(res.whichScript, S.BATTLE, "mode 0 resets whichScript")
|
|
eq(res.scrollOffset, 0, "mode 0 resets scrollOffset")
|
|
eq(res.selectedRow, 0, "mode 0 resets selectedRow")
|
|
|
|
TeachyTv.selectLesson(s, S.MATCHUPS)
|
|
res.selectedRow = 2
|
|
-- pokefirered/src/teachy_tv.c:445
|
|
TeachyTv.setModeToResume(s)
|
|
eq(res.mode, TeachyTv.MODE.RESUME_LIST, "SetTeachyTvControllerModeToResume sets mode 1")
|
|
-- pokefirered/src/teachy_tv.c:437
|
|
TeachyTv.returnToTv(s)
|
|
eq(res.mode, TeachyTv.MODE.FRESH, "mode 1 collapses to mode 0 in InitTeachyTvController")
|
|
eq(res.whichScript, S.MATCHUPS, "but mode 1 does not run the mode 0 reset")
|
|
|
|
TeachyTv.selectLesson(s, S.CATCHING)
|
|
res.selectedRow = 3
|
|
TeachyTv.returnToTv(s)
|
|
eq(res.mode, TeachyTv.MODE.RESUME_SCRIPT, "a non-resume return comes back as mode 2")
|
|
eq(res.whichScript, S.CATCHING, "mode 2 keeps the lesson that ran")
|
|
eq(res.selectedRow, 3, "mode 2 keeps the list row")
|
|
|
|
-- pokefirered/src/teachy_tv.c:1208
|
|
eq(TeachyTv.modeAfterBattle(TeachyTv.B_OUTCOME_DREW), TeachyTv.MODE.RESUME_LIST,
|
|
"B_OUTCOME_DREW drops back to the lesson list")
|
|
eq(TeachyTv.modeAfterBattle(1), TeachyTv.MODE.RESUME_SCRIPT,
|
|
"any other outcome resumes the lesson script")
|
|
end
|
|
|
|
print("[test] 8. watched state survives a save round trip")
|
|
do
|
|
local s = newSession()
|
|
eq(TeachyTv.watchedCount(s), 0, "nothing watched on a new game")
|
|
check(TeachyTv.markWatched(s, S.CATCHING), "markWatched(CATCHING)")
|
|
check(TeachyTv.markWatched(s, S.TMS), "markWatched(TMS)")
|
|
TeachyTv.selectLesson(s, S.TMS)
|
|
eq(TeachyTv.watchedCount(s), 2, "two lessons watched")
|
|
|
|
local save = Schema.toSaveTable(s)
|
|
local back = Schema.fromSaveTable(save)
|
|
check(TeachyTv.hasWatched(back, S.CATCHING), "CATCHING survived the save")
|
|
check(TeachyTv.hasWatched(back, S.TMS), "TMS survived the save")
|
|
check(not TeachyTv.hasWatched(back, S.BATTLE), "BATTLE was never watched")
|
|
eq(TeachyTv.watchedCount(back), 2, "the count survived the save")
|
|
|
|
local res = TeachyTv.resources(back)
|
|
eq(res.whichScript, S.TMS, "whichScript survived the save")
|
|
end
|
|
|
|
print("[test] 9. pokefirered/src/item_use.c:518 FieldUseFunc_TeachyTv")
|
|
do
|
|
eq(TeachyTv.ITEM_TEACHY_TV, 366, "ITEM_TEACHY_TV")
|
|
local info = ItemsData.info(TeachyTv.ITEM_TEACHY_TV)
|
|
if not info then
|
|
print("[skip] no cache for the item pack, bag entry point not exercised")
|
|
else
|
|
eq(info.pocket, "KEY_ITEMS", "TEACHY TV is a key item")
|
|
local s = newSession()
|
|
Bag.add(s.bag, TeachyTv.ITEM_TEACHY_TV, 1)
|
|
local ok, kind, text = ItemUse.useField(s, s.bag, TeachyTv.ITEM_TEACHY_TV)
|
|
check(ok, "USE on the TEACHY TV succeeds")
|
|
eq(kind, "teachy_tv", "the key-item reject arm no longer swallows it")
|
|
eq(text, nil, "no OAK refusal message")
|
|
local res = TeachyTv.resources(s)
|
|
eq(res.mode, TeachyTv.MODE.FRESH, "the bag opens the TV with InitTeachyTvController(0)")
|
|
eq(res.whichScript, S.BATTLE, "and on the first lesson row")
|
|
eq(Bag.get(s.bag, TeachyTv.ITEM_TEACHY_TV), 1, "a key item is not consumed")
|
|
|
|
-- pokefirered/src/item_use.c:534 InitTeachyTvFromBag
|
|
local Stack = require("src.ui.game3.stack")
|
|
local Ui = require("src.ui.game3.teachy_tv")
|
|
check(Ui.isOpen(), "USE put the TV screen on the stack")
|
|
eq(Stack.top() and Stack.top().id, "teachy_tv", "the TV is the top layer")
|
|
eq(Ui.state, "list", "and it is showing the lesson list")
|
|
eq(#Ui.rows(), 5, "four lessons plus CANCEL in the screen's own rows")
|
|
Ui.close()
|
|
Stack.clear()
|
|
end
|
|
end
|
|
|
|
print("[test] 9b. pokefirered/src/item_menu.c:2022 UseRegisteredKeyItemOnField")
|
|
do
|
|
local info = ItemsData.info(TeachyTv.ITEM_TEACHY_TV)
|
|
if not info then
|
|
print("[skip] no cache for the item pack, the registered item path not exercised")
|
|
else
|
|
local Stack = require("src.ui.game3.stack")
|
|
local Ui = require("src.ui.game3.teachy_tv")
|
|
local Game3 = require("src.core.Game3")
|
|
local selectInput = {
|
|
wasPressed = function(_, key) return key == "select" end,
|
|
isDown = function() return false end,
|
|
}
|
|
Stack.clear()
|
|
local s = newSession()
|
|
Bag.add(s.bag, TeachyTv.ITEM_TEACHY_TV, 1)
|
|
-- pokefirered/src/data/text/teachy_tv.h:182 gTeachyTvText_RegisterScript2
|
|
s.registeredItem = TeachyTv.ITEM_TEACHY_TV
|
|
Game3._handleRegisteredItem({ input = selectInput, session = s })
|
|
check(Ui.isOpen(), "SELECT on the registered TEACHY TV opened the TV")
|
|
eq(TeachyTv.resources(s).mode, TeachyTv.MODE.FRESH, "through InitTeachyTvController(0)")
|
|
Ui.close()
|
|
Stack.clear()
|
|
end
|
|
end
|
|
|
|
print("[test] 10. pokefirered/data/specials.inc carries no Teachy TV special")
|
|
do
|
|
local Natives = require("src.core.game3.scripting.natives")
|
|
local mod = Natives.MODULES and Natives.MODULES["natives_teachy"]
|
|
check(mod == nil, "no natives_teachy module: the TV is reached from item_use.c, not a script")
|
|
end
|
|
|
|
if failed > 0 then
|
|
print(string.format("FAILED %d check(s)", failed))
|
|
os.exit(1)
|
|
end
|
|
print("All teachy tv model checks passed.")
|