Files
gen1recomp/tests/game3_menu_sfx_test.lua
bryanthaboi dee935a4ea CLOSES #2430
2026-09-23 11:58:32 -04:00

220 lines
8.0 KiB
Lua

#!/usr/bin/env luajit
package.path = "./?.lua;./?/init.lua;" .. package.path
require("tests.fixture_data.game3_items").install()
require("tests.fixture_data.game3_map_sections").install()
package.loaded["src.core.game3.rom_text"] = {
plain = function(key) return key end, box = function(key) return key end,
ascii = function(key) return key end, has = function() return true end,
key = function(n, i, j) return j and (n .. "[" .. i .. "][" .. j .. "]") or (n .. "[" .. i .. "]") end,
at = function(n, i, j) return j and (n .. "[" .. i .. "][" .. j .. "]") or (n .. "[" .. i .. "]") end,
count = function() return 0 end, list = function() return {} end,
lazy = function(map) return setmetatable({}, { __index = function(_, k) return map[k] end }) end,
}
local failed = 0
local function check(cond, msg)
if cond then
print("[ok] " .. msg)
else
failed = failed + 1
print("[FAIL] " .. msg)
end
end
local SE_SELECT, SE_WIN_OPEN, SE_EXIT, SE_FAILURE, SE_SHOP = 5, 6, 9, 26, 248
local played = {}
package.loaded["src.core.game3.audio"] = {
playSe = function(id) played[#played + 1] = id end,
playSong = function() end,
playFanfare = function() end,
stopAll = function() end,
waitSe = function(_, cb) if cb then cb() end end,
}
package.loaded["src.ui.game3.stack"] = {
push = function() end,
pop = function() end,
contains = function() return false end,
top = function() return nil end,
}
print("[test] 1. Start menu close plays SE_SELECT (start_menu.c:1005)")
local StartMenu = require("src.ui.game3.start_menu")
played = {}
StartMenu.close()
check(#played == 1 and played[1] == SE_SELECT,
"StartMenu.close -> SE_SELECT only (got " .. table.concat(played, ",") .. ")")
print("[test] 2. Backing out of the EXIT confirm with B is silent (menu.c:381)")
StartMenu._confirmExit = true
played = {}
StartMenu.cancel()
check(#played == 0, "StartMenu.cancel on confirm -> silence (got " .. table.concat(played, ",") .. ")")
print("[test] 3. Save dialog opens silently (start_menu.c:518)")
local SaveMenu = require("src.ui.game3.save_menu")
played = {}
SaveMenu.show({})
check(#played == 0, "SaveMenu.show -> silence (got " .. table.concat(played, ",") .. ")")
print("[test] 4. Save dialog NO plays SE_SELECT, B is silent (menu.c:373/381)")
SaveMenu.show({})
SaveMenu.cursor = 2
played = {}
SaveMenu.confirm()
check(#played == 1 and played[1] == SE_SELECT,
"SaveMenu NO -> SE_SELECT only (got " .. table.concat(played, ",") .. ")")
SaveMenu.show({})
played = {}
SaveMenu.cancel()
check(#played == 0, "SaveMenu B press -> silence (got " .. table.concat(played, ",") .. ")")
print("[test] 4b. Dismissing the saved message is silent (start_menu.c:583)")
StartMenu.open = true
SaveMenu.show({})
SaveMenu._phase = "saved"
played = {}
SaveMenu.confirm()
check(#played == 0, "SaveMenu saved-phase A -> silence (got " .. table.concat(played, ",") .. ")")
check(StartMenu.open == false, "saved-phase dismissal still tears the start menu down")
StartMenu.open = true
SaveMenu.show({})
SaveMenu._phase = "saved"
played = {}
SaveMenu.cancel()
check(#played == 0, "SaveMenu saved-phase B -> silence (got " .. table.concat(played, ",") .. ")")
StartMenu.open = false
print("[test] 5. Shop menu opens silently and quits with SE_SELECT (shop.c:213/265)")
local ShopMenu = require("src.ui.game3.shop_menu")
played = {}
ShopMenu.show({ items = {}, session = {} })
check(#played == 0, "ShopMenu.show -> silence (got " .. table.concat(played, ",") .. ")")
played = {}
ShopMenu.close()
check(#played == 0, "ShopMenu.close -> silence (the A/B press sounds, not the teardown)")
print("[test] 5b. Purchase YES plays SE_SELECT, SE_SHOP follows the message (shop.c:999)")
local function shop_input(map)
return { wasPressed = function(_, k) return map[k] == true end,
isDown = function(_, k) return map[k] == true end }
end
local Bag = require("src.core.game3.bag")
local shopSession = { money = 5000, bag = Bag.new() }
ShopMenu.show({ items = { 4 }, session = shopSession })
ShopMenu.mode = "buy_confirm"
ShopMenu._pending = { id = 4, price = 200, name = "POKe BALL" }
ShopMenu.qty = 1
ShopMenu.yesNoCursor = 1
played = {}
ShopMenu.handleInput(shop_input({ a = true }))
check(ShopMenu.mode == "buy_msg" and shopSession.money == 4800, "purchase committed")
check(#played == 1 and played[1] == SE_SELECT,
"YES frame -> SE_SELECT only (got " .. table.concat(played, ",") .. ")")
local waited = 0
while #played == 1 and waited < 600 do
ShopMenu.handleInput(shop_input({}))
waited = waited + 1
end
check(#played == 2 and played[2] == SE_SHOP and waited > 1,
"SE_SHOP lands " .. waited .. " frames later (got " .. table.concat(played, ",") .. ")")
played = {}
ShopMenu.handleInput(shop_input({ a = true }))
check(ShopMenu.mode == "buy" and #played == 1 and played[1] == SE_SELECT,
"message dismissal -> SE_SELECT only (got " .. table.concat(played, ",") .. ")")
ShopMenu.mode = "buy_confirm"
ShopMenu._pending = { id = 4, price = 200, name = "POKe BALL" }
ShopMenu.qty = 1
ShopMenu.yesNoCursor = 1
ShopMenu.handleInput(shop_input({ a = true }))
played = {}
ShopMenu.handleInput(shop_input({ a = true }))
check(ShopMenu.mode == "buy" and #played == 1 and played[1] == SE_SHOP,
"early dismissal still plays SE_SHOP exactly once (got " .. table.concat(played, ",") .. ")")
ShopMenu.close()
print("[test] 6. Generic yes/no cancel plays SE_SELECT (menu_helpers.c:57)")
local Choice = require("src.ui.game3.choice")
Choice.active = true
Choice.kind = "yesno"
Choice.options = { "YES", "NO" }
Choice.cursor = 1
Choice.ignoreBPress = false
Choice.done = nil
played = {}
Choice.cancel()
check(#played == 1 and played[1] == SE_SELECT,
"Choice.cancel -> SE_SELECT only (got " .. table.concat(played, ",") .. ")")
print("[test] 7. No game3 UI source plays SE_EXIT")
local UI_SOURCES = {
"src/ui/game3/start_menu.lua",
"src/ui/game3/save_menu.lua",
"src/ui/game3/shop_menu.lua",
"src/ui/game3/choice.lua",
"src/ui/game3/bag_menu.lua",
"src/ui/game3/party_menu.lua",
"src/ui/game3/berry_pouch.lua",
"src/ui/game3/tm_case.lua",
"src/ui/game3/box_storage_ui.lua",
"src/ui/game3/pc_menu.lua",
"src/ui/game3/summary_menu.lua",
"src/ui/game3/region_map.lua",
"src/ui/game3/release_seq.lua",
}
for _, path in ipairs(UI_SOURCES) do
local f = io.open(path, "r")
if not f then
check(false, "missing source " .. path)
else
local src = f:read("*a")
f:close()
local hits = {}
local lineNo = 0
for line in (src .. "\n"):gmatch("([^\n]*)\n") do
lineNo = lineNo + 1
local code = line:gsub("%-%-.*$", "")
if code:find("se%(9%)") or code:find("playSe%(%s*9%s*%)") or code:find("SE_EXIT") then
hits[#hits + 1] = tostring(lineNo)
end
end
check(#hits == 0, path .. " has no SE_EXIT site (lines " .. table.concat(hits, ",") .. ")")
end
end
print("[test] 8. SE_EXIT still owned by the warp/door path (field_fadetransition.c:541)")
for _, path in ipairs({ "src/core/game3/warp.lua", "src/core/game3/doors.lua" }) do
local f = assert(io.open(path, "r"))
local src = f:read("*a")
f:close()
check(src:find("SE_EXIT") ~= nil, path .. " still references SE_EXIT")
end
print("[test] 9. One-shot SEs bake to their natural end (battle_anim_special.c:1200)")
local captured = nil
package.loaded["src.core.game3.m4a_player"] = {
mixQuantum = function() return 256 end,
loadPack = function() return {} end,
songInfo = function(_, id) return { kind = "se", player = 2, hasGoto = false, id = id } end,
start = function() return true end,
bakeSlot = function(_, opts) captured = opts; return { 0 }, { 0 } end,
renderBuffered = function() return {}, {} end,
updateSlot = function() end,
}
package.loaded["src.core.game3.audio"] = nil
local Audio = require("src.core.game3.audio")
Audio._ready = true
Audio._pack = {}
Audio.playSe(319)
check(captured ~= nil, "playSe reached Player.bakeSlot")
check(captured and captured.maxSec and captured.maxSec >= 4.6,
"MUS_CAUGHT_INTRO bake ceiling covers the full cue (maxSec = "
.. tostring(captured and captured.maxSec) .. ")")
if failed > 0 then
print(string.format("\n%d check(s) failed", failed))
os.exit(1)
end
print("\nall menu SFX checks passed")