mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-10-01 07:40:29 -04:00
267 lines
10 KiB
Lua
267 lines
10 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 function eq(a, b, msg)
|
|
check(a == b, string.format("%s (%s == %s)", msg, tostring(a), tostring(b)))
|
|
end
|
|
|
|
local RegionMap = require("src.ui.game3.region_map")
|
|
local Flags = require("src.core.game3.scripting.flags")
|
|
local Space = require("src.core.game3.scripting.space")
|
|
|
|
local SECTYPE = RegionMap.MAPSECTYPE
|
|
|
|
Space.store = Flags.newStore()
|
|
local function setWorldMapFlag(name)
|
|
Flags.setFlag(Space.store, nil, name, true)
|
|
end
|
|
|
|
local function press(btn)
|
|
RegionMap.handleInput({
|
|
wasPressed = function(_, k) return k == btn end,
|
|
isDown = function() return false end,
|
|
})
|
|
end
|
|
|
|
print("[test] 1. mapsec types follow the FLAG_WORLD_MAP_* flags")
|
|
eq(RegionMap.mapsecType("MAPSEC_PALLET_TOWN"), SECTYPE.NOT_VISITED,
|
|
"Pallet Town is NOT_VISITED on a fresh store")
|
|
eq(RegionMap.mapsecType("MAPSEC_ROUTE_1"), SECTYPE.ROUTE, "Route 1 is a route")
|
|
eq(RegionMap.mapsecType(nil), SECTYPE.NONE, "an empty cell is NONE")
|
|
setWorldMapFlag("FLAG_WORLD_MAP_PALLET_TOWN")
|
|
setWorldMapFlag("FLAG_WORLD_MAP_VIRIDIAN_CITY")
|
|
eq(RegionMap.mapsecType("MAPSEC_PALLET_TOWN"), SECTYPE.VISITED,
|
|
"Pallet Town is VISITED once its world map flag is set")
|
|
eq(RegionMap.mapsecType("MAPSEC_PEWTER_CITY"), SECTYPE.NOT_VISITED,
|
|
"Pewter City is still NOT_VISITED")
|
|
|
|
print("[test] 2. viewer mode is unchanged")
|
|
do
|
|
local closedWith, closedCalls = "unset", 0
|
|
RegionMap.show({
|
|
session = { map = "PALLET_TOWN", gender = 0 },
|
|
onClose = function(v) closedWith = v; closedCalls = closedCalls + 1 end,
|
|
})
|
|
check(RegionMap.isFlyMode() == false, "show() with no mode is the viewer")
|
|
check(RegionMap.hasFlyDestinations() == false, "the viewer has no fly destinations")
|
|
eq(RegionMap.mapsecType("MAPSEC_ROUTE_4_POKECENTER"), SECTYPE.NONE,
|
|
"the Route 4 Pokemon Center cell is NONE off the fly map")
|
|
eq(#RegionMap.flyTargets(), 0, "the viewer places no fly icons")
|
|
press("up")
|
|
press("up")
|
|
press("up")
|
|
eq(RegionMap.cursorY, 8, "cursor walked up to Viridian City")
|
|
press("up")
|
|
press("up")
|
|
-- pokefirered/src/region_map.c:1266
|
|
press("a")
|
|
eq(RegionMap.previewDungeon, nil, "an unvisited dungeon refuses the GUIDE preview")
|
|
setWorldMapFlag("FLAG_WORLD_MAP_VIRIDIAN_FOREST")
|
|
press("a")
|
|
eq(RegionMap.previewDungeon, "MAPSEC_VIRIDIAN_FOREST", "A still opens the GUIDE preview")
|
|
press("b")
|
|
press("b")
|
|
check(RegionMap.isOpen() == false, "B closes the viewer")
|
|
eq(closedCalls, 1, "onClose ran once")
|
|
eq(closedWith, nil, "onClose was handed nil")
|
|
end
|
|
|
|
print("[test] 3. fly mode picks only flagged mapsecs")
|
|
do
|
|
local picked, pickedId, pickCalls, closedCalls = "unset", "unset", 0, 0
|
|
RegionMap.show({
|
|
mode = "fly",
|
|
mapType = 1,
|
|
session = { map = "PALLET_TOWN", gender = 0 },
|
|
onPick = function(sec, id) picked = sec; pickedId = id; pickCalls = pickCalls + 1 end,
|
|
onClose = function() closedCalls = closedCalls + 1 end,
|
|
})
|
|
check(RegionMap.isFlyMode() == true, "mode = fly is the fly map")
|
|
eq(RegionMap.mapsecType("MAPSEC_ROUTE_4_POKECENTER"), SECTYPE.NOT_VISITED,
|
|
"the Route 4 Pokemon Center cell exists on the fly map")
|
|
eq(RegionMap.cursorX .. "," .. RegionMap.cursorY, "4,11", "the cursor starts on the player")
|
|
|
|
local targets = RegionMap.flyTargets()
|
|
eq(#targets, 2, "two towns carry a fly icon")
|
|
local seen = {}
|
|
for _, t in ipairs(targets) do seen[t.sec] = t.x .. "," .. t.y end
|
|
eq(seen["MAPSEC_PALLET_TOWN"], "4,11", "Pallet Town has a fly icon")
|
|
eq(seen["MAPSEC_VIRIDIAN_CITY"], "4,8", "Viridian City has a fly icon")
|
|
eq(seen["MAPSEC_PEWTER_CITY"], nil, "unvisited Pewter City has none")
|
|
|
|
press("up")
|
|
eq(RegionMap.cursorY, 10, "the cursor moves onto Route 1")
|
|
check(RegionMap.canFlyToCursor() == false, "a route is not a fly target")
|
|
press("a")
|
|
eq(pickCalls, 0, "A on a route picks nothing")
|
|
check(RegionMap.isOpen() == true, "A on a route leaves the map open")
|
|
|
|
press("up")
|
|
press("up")
|
|
check(RegionMap.canFlyToCursor() == true, "Viridian City is a fly target")
|
|
press("a")
|
|
eq(pickCalls, 1, "A on a visited town calls onPick once")
|
|
eq(picked, "MAPSEC_VIRIDIAN_CITY",
|
|
"onPick receives the symbolic mapsec, the key FLY_DESTINATIONS is built on")
|
|
eq(pickedId, 89, "the numeric mapsec follows as the second argument")
|
|
check(RegionMap.isOpen() == false, "the fly map closed on the pick")
|
|
eq(closedCalls, 0, "onClose does not also fire on a pick")
|
|
check(RegionMap.isFlyMode() == false, "the module falls back to viewer mode")
|
|
|
|
-- pokefirered/src/region_map.c:4022
|
|
local Field = require("src.core.game3.field")
|
|
local dest = Field.FLY_DESTINATIONS[picked]
|
|
check(dest ~= nil, "the picked mapsec is a key of the field's fly destination table")
|
|
eq(dest and dest.map, "FR_VIRIDIAN_CITY", "and it resolves to Viridian City")
|
|
end
|
|
|
|
print("[test] 3b. a failing consumer still releases the field")
|
|
do
|
|
local closedCalls = 0
|
|
RegionMap.show({
|
|
mode = "fly",
|
|
mapType = 1,
|
|
session = { map = "PALLET_TOWN", gender = 0 },
|
|
onPick = function() error("consumer blew up") end,
|
|
onClose = function() closedCalls = closedCalls + 1 end,
|
|
})
|
|
press("up")
|
|
press("up")
|
|
press("up")
|
|
check(RegionMap.canFlyToCursor() == true, "the cursor is on a fly target")
|
|
press("a")
|
|
eq(closedCalls, 1, "onPick raising an error falls back to onClose")
|
|
check(RegionMap.isOpen() == false, "the fly map still closed")
|
|
end
|
|
|
|
print("[test] 4. an unvisited town refuses, B cancels")
|
|
do
|
|
local pickCalls, closedCalls, closedWith = 0, 0, "unset"
|
|
RegionMap.show({
|
|
mode = "fly",
|
|
mapType = 1,
|
|
session = { map = "PALLET_TOWN", gender = 0 },
|
|
onPick = function() pickCalls = pickCalls + 1 end,
|
|
onClose = function(v) closedCalls = closedCalls + 1; closedWith = v end,
|
|
})
|
|
for _ = 1, 7 do press("up") end
|
|
eq(RegionMap.cursorY, 4, "the cursor reached Pewter City")
|
|
check(RegionMap.canFlyToCursor() == false, "an unvisited town is not selectable")
|
|
press("a")
|
|
eq(pickCalls, 0, "A on an unvisited town picks nothing")
|
|
check(RegionMap.isOpen() == true, "the map stays open")
|
|
press("b")
|
|
check(RegionMap.isOpen() == false, "B closes the fly map")
|
|
eq(pickCalls, 0, "B picked nothing")
|
|
eq(closedCalls, 1, "B ran onClose once")
|
|
eq(closedWith, nil, "B handed onClose nil")
|
|
end
|
|
|
|
print("[test] 5. fly-map quirks: SELECT, the switch button, the GUIDE")
|
|
do
|
|
local pickCalls = 0
|
|
RegionMap.show({
|
|
mode = "fly",
|
|
mapType = 1,
|
|
session = { map = "PALLET_TOWN", gender = 0 },
|
|
onPick = function() pickCalls = pickCalls + 1 end,
|
|
})
|
|
press("select")
|
|
check(RegionMap.isOpen() == true, "SELECT does not cancel the fly map")
|
|
for _ = 1, 5 do press("up") end
|
|
eq(RegionMap.cursorY, 6, "the cursor reached Viridian Forest")
|
|
press("a")
|
|
eq(RegionMap.previewDungeon, nil, "the fly map never opens the GUIDE")
|
|
eq(pickCalls, 0, "a dungeon cell is not a fly target")
|
|
press("start")
|
|
eq(RegionMap.cursorX .. "," .. RegionMap.cursorY, "21,13", "START snapped to CANCEL")
|
|
press("a")
|
|
check(RegionMap.isOpen() == false, "A on CANCEL closes the fly map")
|
|
eq(pickCalls, 0, "A on CANCEL picks nothing")
|
|
end
|
|
|
|
print("[test] 6. indoors and underground refuse the destination")
|
|
do
|
|
local pickCalls, closedCalls = 0, 0
|
|
RegionMap.show({
|
|
mode = "fly",
|
|
mapType = 8,
|
|
session = { map = "PALLET_TOWN", gender = 0 },
|
|
onPick = function() pickCalls = pickCalls + 1 end,
|
|
onClose = function() closedCalls = closedCalls + 1 end,
|
|
})
|
|
check(RegionMap.flyBlockedByMapType() == true, "MAP_TYPE_INDOOR blocks the warp")
|
|
check(RegionMap.canFlyToCursor() == true, "the target itself is still valid")
|
|
press("a")
|
|
eq(pickCalls, 0, "A indoors picks nothing")
|
|
eq(closedCalls, 1, "A indoors drops back through onClose")
|
|
check(RegionMap.isOpen() == false, "the fly map closed")
|
|
|
|
RegionMap.show({ mode = "fly", mapType = 4, session = { map = "PALLET_TOWN" } })
|
|
check(RegionMap.flyBlockedByMapType() == true, "MAP_TYPE_UNDERGROUND blocks the warp too")
|
|
RegionMap.close()
|
|
RegionMap.show({ mode = "fly", mapType = 3, session = { map = "PALLET_TOWN" } })
|
|
check(RegionMap.flyBlockedByMapType() == false, "MAP_TYPE_ROUTE does not block")
|
|
RegionMap.close()
|
|
end
|
|
|
|
print("[test] 7. the fly icon blinks frame 0 then frame 1")
|
|
do
|
|
RegionMap.show({ mode = "fly", mapType = 1, session = { map = "PALLET_TOWN" } })
|
|
eq(RegionMap.flyIconFrame(), 0, "the icon starts on frame 0")
|
|
for _ = 1, 29 do press("x") end
|
|
eq(RegionMap.flyIconFrame(), 0, "frame 0 holds for 30 ticks")
|
|
press("x")
|
|
eq(RegionMap.flyIconFrame(), 1, "frame 1 takes over at tick 30")
|
|
for _ = 1, 59 do press("x") end
|
|
eq(RegionMap.flyIconFrame(), 1, "frame 1 holds for 60 ticks")
|
|
press("x")
|
|
eq(RegionMap.flyIconFrame(), 0, "the 90 tick cycle wraps to frame 0")
|
|
RegionMap.close()
|
|
end
|
|
|
|
print("[test] 8. dungeon markers carry a visited frame")
|
|
do
|
|
Space.store = Flags.newStore()
|
|
eq(RegionMap.dungeonMapsecType("MAPSEC_VIRIDIAN_FOREST"), SECTYPE.NOT_VISITED,
|
|
"Viridian Forest is NOT_VISITED on a fresh store")
|
|
eq(RegionMap.dungeonIconFrame("MAPSEC_VIRIDIAN_FOREST"), 0, "it draws marker frame 0")
|
|
setWorldMapFlag("FLAG_WORLD_MAP_VIRIDIAN_FOREST")
|
|
eq(RegionMap.dungeonMapsecType("MAPSEC_VIRIDIAN_FOREST"), SECTYPE.VISITED,
|
|
"the world map flag marks it VISITED")
|
|
eq(RegionMap.dungeonIconFrame("MAPSEC_VIRIDIAN_FOREST"), 1, "it draws marker frame 1")
|
|
eq(RegionMap.dungeonIconFrame("MAPSEC_MT_MOON"), 0, "Mt. Moon is still on frame 0")
|
|
eq(RegionMap.dungeonIconFrame(nil), 0, "an empty cell asks for frame 0")
|
|
check(RegionMap.dungeonIconVisitedImage() == nil,
|
|
"the second frame is absent, so the draw degrades to frame 0")
|
|
|
|
eq(RegionMap.dungeonIconOffset(4, 14), 2,
|
|
"the Pokemon Mansion marker is offset into Cinnabar Island's corner")
|
|
eq(RegionMap.dungeonIconOffset(18, 3), 0,
|
|
"the Rock Tunnel marker on the Route 10 Pokemon Center is not offset")
|
|
eq(RegionMap.dungeonIconOffset(9, 3), 0, "the Mt. Moon marker on Route 4 is not offset")
|
|
eq(RegionMap.dungeonIconOffset(12, 12), 2, "the Safari Zone marker sits in Fuchsia City")
|
|
|
|
eq(RegionMap.dungeonSecAt(14, 3), nil, "Cerulean Cave is hidden without the RS link")
|
|
setWorldMapFlag("FLAG_SYS_CAN_LINK_WITH_RS")
|
|
eq(RegionMap.dungeonSecAt(14, 3), "MAPSEC_CERULEAN_CAVE",
|
|
"the RS link reveals the Cerulean Cave marker")
|
|
end
|
|
|
|
if failed > 0 then
|
|
print(failed .. " CHECK(S) FAILED")
|
|
os.exit(1)
|
|
end
|
|
print("ALL REGION MAP FLY TESTS PASSED")
|