Files
gen1recomp/tests/game3_map_onload_test.lua
bryanthaboi 605654a086 CLOSES #2310
2026-09-20 13:21:16 -04:00

215 lines
8.7 KiB
Lua

#!/usr/bin/env luajit
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 Space = require("src.core.game3.scripting.space")
print("[test] 1. Space exposes an ON_LOAD entry point")
check(type(Space.runOnLoad) == "function", "Space.runOnLoad exists")
local Cache = require("tests.game3_cache")
local cacheRoot = Cache.mount("scripts/events.lua", { native = true })
if not cacheRoot then
print("[skip] game3_map_onload_test: " .. tostring(Cache.reason))
if failed > 0 then
print("[test] FAILED " .. failed)
os.exit(1)
end
print("[test] all passed")
os.exit(0)
end
print("[info] FireRed cache at " .. cacheRoot)
local ExtractScripts = require("src.import.gba.extract_scripts")
local Objects = require("src.core.game3.objects")
local Field = require("src.core.game3.field")
Space.bundle = ExtractScripts.loadBundle(Cache.cache(), cacheRoot, { allowIncomplete = true })
check(Space.bundle ~= nil, "script bundle loads")
print("[test] 2. MAP_SCRIPT_ON_LOAD is carried through import")
local withLoad, withTransition, withResume, withReturn, withWarpInto = {}, 0, 0, 0, 0
for mapId, ev in pairs(Space.bundle.events or {}) do
local ms = ev.mapScripts or {}
if type(ms.onLoad) == "string" then withLoad[#withLoad + 1] = mapId end
if type(ms.onTransition) == "string" then withTransition = withTransition + 1 end
if type(ms.onResume) == "string" then withResume = withResume + 1 end
if type(ms.onReturnToField) == "string" then withReturn = withReturn + 1 end
if type(ms.onWarpIntoMap) == "table" and #ms.onWarpIntoMap > 0 then
withWarpInto = withWarpInto + 1
end
end
table.sort(withLoad)
print("[info] maps with ON_LOAD: " .. #withLoad)
check(#withLoad >= 20, "the cache carries ON_LOAD for many maps (" .. #withLoad .. ")")
check(withTransition > 0, "ON_TRANSITION still extracted (" .. withTransition .. ")")
check(withResume > 0, "ON_RESUME extracted as a script key (" .. withResume .. ")")
check(withReturn > 0, "ON_RETURN_TO_FIELD extracted (" .. withReturn .. ")")
check(withWarpInto > 0, "ON_WARP_INTO_MAP_TABLE extracted (" .. withWarpInto .. ")")
print("[test] 3. the puzzle maps this unblocks all have their ON_LOAD")
local SPOT_CHECK = {
"FR_VERMILION_CITY_GYM",
"FR_SILPH_CO_2F",
"FR_POKEMON_MANSION_1F",
"FR_SEAFOAM_ISLANDS_B4F",
"FR_ROCKET_HIDEOUT_B1F",
"FR_VICTORY_ROAD_1F",
}
for _, mapId in ipairs(SPOT_CHECK) do
local ev = Space.bundle.events[mapId]
local key = ev and ev.mapScripts and ev.mapScripts.onLoad
check(type(key) == "string", mapId .. " carries an ON_LOAD script key")
if type(key) == "string" then
check(Space.bundle.scripts[key] ~= nil, mapId .. " ON_LOAD body decoded (" .. key .. ")")
end
end
print("[test] 4. runEnterScripts runs ON_LOAD after ON_TRANSITION")
local GYM = "FR_VERMILION_CITY_GYM"
local gym = Space.bundle.events[GYM]
local FLAG_FOUND_BOTH = 0x264
local Flags = require("src.core.game3.scripting.flags")
if gym then
local Dataset = require("src.core.game3.dataset")
local Runtime = require("src.core.game3.runtime")
local Map = require("src.core.game3.map")
local Collision = require("src.core.game3.collision")
local CITY = "FR_VERMILION_CITY"
local game = { data = {} }
Dataset.hydrate(game)
local session = { map = CITY, x = 12, y = 20, facing = "up", flags = {}, vars = {} }
game.session = session
Runtime.start(nil, game, session, { reason = "new_game" })
local function warpTo(mapId, x, y)
Map.load(nil, game, mapId, { x = x, y = y, facing = "up" })
for _ = 1, 256 do
if not Space.vm:isRunning() then break end
Space.vm:tick()
end
end
local function beamCells()
local n = 0
for y = 6, 7 do
for x = 3, 7 do
if Field.metatileOverrideAt and Field.metatileOverrideAt(GYM, x, y) then n = n + 1 end
end
end
return n
end
Flags.setFlag(Space.store, Space.vm.ctx, FLAG_FOUND_BOTH, true)
warpTo(GYM, 5, 18)
check(beamCells() == 10,
"ON_LOAD wrote the 10 beams-off metatiles on map enter, got " .. beamCells())
check(Collision.canEnter(game, 5, 6) == true and Collision.canEnter(game, 5, 7) == true,
"the solved gym is walkable through (5,6)/(5,7) on entry")
print("[test] 5. Space.runOnLoad is idempotent and re-runnable")
check(type(Space.runOnLoad) == "function" and Space.runOnLoad(GYM) == true,
"Space.runOnLoad(GYM) reports it ran")
check(beamCells() == 10, "a second ON_LOAD rewrites the same 10 cells in place, got " .. beamCells())
check(Collision.canEnter(game, 5, 6) == true, "the rewrite keeps (5,6) open")
print("[test] 6. a map load rebuilds the layout before ON_LOAD")
local layout = game.data.maps[GYM].midLayout
local openMid = layout:midAt(5, 6)
warpTo(CITY, 12, 20)
Flags.setFlag(Space.store, Space.vm.ctx, FLAG_FOUND_BOTH, false)
warpTo(GYM, 5, 18)
check(beamCells() == 0, "the unsolved gym has no script-written cells, got " .. beamCells())
check(layout:midAt(5, 6) ~= openMid, "(5,6) is the layout's own beam metatile again")
check(Collision.canEnter(game, 5, 6) ~= true, "the unsolved gym's beam is solid at (5,6)")
print("[test] 7. a script-opened door keeps its warp (Six Island Ruin Valley)")
local RUIN = "FR_SIX_ISLAND_RUIN_VALLEY"
local FLAG_USED_CUT_ON_RUIN_VALLEY_BRAILLE = 0x2E3
warpTo(RUIN, 24, 26)
local ruinLayout = game.data.maps[RUIN].midLayout
local closedMid = ruinLayout:midAt(24, 24)
check(Collision.warpAt(24, 24) == nil, "the closed Dotted Hole door has no live warp")
check(Collision.canEnter(game, 24, 24) ~= true, "the closed Dotted Hole door is solid")
warpTo(CITY, 12, 20)
Flags.setFlag(Space.store, Space.vm.ctx, FLAG_USED_CUT_ON_RUIN_VALLEY_BRAILLE, true)
warpTo(RUIN, 24, 26)
check(ruinLayout:midAt(24, 24) ~= closedMid, "ON_LOAD wrote the open door metatile at (24,24)")
check(Collision.canEnter(game, 24, 24) == true, "the open door cell can be entered")
check(Collision.warpAt(24, 24) ~= nil, "the Dotted Hole warp at (24,24) is live once the door is open")
print("[test] 8. every metatile a script can place is in the native pack")
local NativePack = require("src.import.gba.native_pack")
local manifest = dofile(cacheRoot .. "/native/manifest.lua")
local function pairOf(mapId)
local info = manifest.layouts and manifest.layouts[mapId]
return info and info.pair
end
local wanted = NativePack.scriptMidsByPair and NativePack.scriptMidsByPair(
Space.bundle.scripts, Space.bundle.events, pairOf) or nil
check(type(wanted) == "table" and next(wanted) ~= nil, "setmetatile rows resolve to tileset pairs")
local missing, pairsSeen = 0, 0
for pairName, mids in pairs(wanted or {}) do
pairsSeen = pairsSeen + 1
local f = io.open(cacheRoot .. "/native/" .. pairName .. "/mids.idx", "rb")
local packed = {}
if f then
local idx = NativePack.decodeIdx(f:read("*a"))
f:close()
for _, mid in ipairs(idx and idx.midIds or {}) do packed[mid] = true end
end
for mid in pairs(mids) do
if not packed[mid] then missing = missing + 1 end
end
end
check(pairsSeen > 0 and missing == 0,
"no script-placed metatile is missing from its pair's atlas, missing=" .. missing)
local gymPair = pairOf(GYM)
check(wanted and wanted[gymPair] and wanted[gymPair][layout:midAt(5, 6)] == true,
"the gym's own beam metatile is among the script-placed ids")
print("[test] 9. every General-primary pair packs the void-fill border metatiles")
local VoidFill = require("src.core.game3.void_fill")
local borderMids = {}
for _, mapId in pairs(VoidFill.SOURCES) do
local src = game.data.maps[mapId] and game.data.maps[mapId].midLayout
for _, mid in ipairs(src and src.borderMids or {}) do borderMids[mid] = true end
end
check(next(borderMids) ~= nil, "the void-fill source maps carry border metatiles")
local generalPairs, voidMissing = 0, 0
for pairName in pairs(manifest.pairs or {}) do
if VoidFill.primaryFor(pairName) == VoidFill.PRIMARY then
generalPairs = generalPairs + 1
local f = io.open(cacheRoot .. "/native/" .. pairName .. "/mids.idx", "rb")
local packed = {}
if f then
local idx = NativePack.decodeIdx(f:read("*a"))
f:close()
for _, mid in ipairs(idx and idx.midIds or {}) do packed[mid] = true end
end
for mid in pairs(borderMids) do
if not packed[mid] then voidMissing = voidMissing + 1 end
end
end
end
check(generalPairs > 0 and voidMissing == 0,
("all %d General-primary pairs pack the void-fill mids, missing=%d"):format(generalPairs, voidMissing))
else
check(false, "the Vermilion gym is in the bundle")
end
if failed > 0 then
print("[test] FAILED " .. failed)
os.exit(1)
end
print("[test] all passed")