mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
215 lines
8.7 KiB
Lua
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")
|