Files
gen1recomp/tests/gen2_dig_warp_test.lua
T
bryanthaboi ae6cac89e1 G2 support
2026-08-11 11:53:30 -04:00

197 lines
8.7 KiB
Lua

-- ESCAPE ROPE and DIG have to come out of the door that was walked in, not the
-- last door that happened to bank a triple.
--
-- GOLD_CACHE=".../gold" luajit tests/gen2_dig_warp_test.lua
--
-- home/map.asm EnterMapWarp calls `.SaveDigWarp` on EVERY warp: if the map
-- being left is outdoor (CheckOutdoorMap -- ROUTE or TOWN) and the one being
-- entered is indoor (CheckIndoorMap -- INDOOR, CAVE, DUNGEON or GATE), the
-- warp stepped on and the map left go into wDigWarpNumber / wDigMapGroup /
-- wDigMapNumber, and EscapeRopeOrDig's `.CheckCanDig` reads them straight back.
-- MOUNT_MOON_SQUARE and TIN_TOWER_ROOF are the routine's own exceptions:
-- outdoor maps sitting inside indoor ones, which the rope must never land on.
--
-- The port had only the -1 writer (the POKECENTER_2F / elevator contract in
-- tests/gen2_pokecenter_stairs_test.lua), so a cave entered by an ordinary
-- door banked nothing and the rope paid out to whatever was banked last. DARK
-- CAVE has a Route 31 mouth and a Route 46 mouth, which is exactly the case
-- that tells the two apart.
package.path = "./?.lua;./?/init.lua;" .. package.path
local S = require("tests.harness").suite("gen2 dig warp")
local check, eq = S.check, S.eq
local World = require("src.world.gen2.World")
local Map = require("src.world.gen2.Map")
local cache = os.getenv("GOLD_CACHE")
if not cache then
local home = os.getenv("HOME") or ""
cache = home .. "/Library/Application Support/LOVE/gold-dev/gold"
end
local probe = io.open(cache .. "/data/generated/maps.lua", "r")
if not probe then
check(true, "gold cache absent (SKIP)")
S.finish()
return
end
probe:close()
local function loadLua(rel) return assert(loadfile(cache .. "/" .. rel))() end
local maps = loadLua("data/generated/maps.lua")
local tilesets = loadLua("data/generated/tilesets.lua")
-- The map data the whole rule reads: the environments the two Check routines
-- compare against, straight out of the extracted headers.
eq(maps.ROUTE_31.environment, "ROUTE", "Route 31 is an outdoor map")
eq(maps.DARK_CAVE_VIOLET_ENTRANCE.environment, "CAVE",
"and DARK CAVE's Violet side is a cave")
eq(maps.ROUTE_31.warps[3].destMap, "DARK_CAVE_VIOLET_ENTRANCE",
"Route 31's third warp is the cave mouth")
eq(maps.ROUTE_46.warps[3].destMap, "DARK_CAVE_VIOLET_ENTRANCE",
"and Route 46's third warp is the other one")
-- A World over the real defs. setMap is recorded rather than run (baking a
-- map image needs a graphics device); everything around it -- takeWarp,
-- recordWarpBackup, escapeRopeTarget, runEscapeWarp -- is the shipped code.
local function world(mapId)
local game = { data = { audio = { sfxOrder = {} } }, save = { player = {} } }
local w = World.new(game)
w.maps, w.tilesets = maps, tilesets
w.map = Map.new(maps[mapId], tilesets[maps[mapId].tileset])
w.player = { cellX = 0, cellY = 0, facing = "up", moving = false }
w.loaded = nil
w.setMap = function(self, id, cx, cy, facing)
self.loaded = { id = id, x = cx, y = cy, facing = facing }
-- The stub stands in for the load, so the map the world believes it is
-- standing on has to move with it or the next take reads the old warps.
self.map = Map.new(maps[id], tilesets[maps[id].tileset])
return true
end
return w
end
-- MAPSETUP_DOOR fades out before the load, so the take is parked in
-- world.mapSetup; this drains it the way World:step does.
local function pump(w)
for _ = 1, 64 do
if not w.mapSetup then return end
w:updateMapSetup()
end
end
local function walk(w, mapId, warpIndex)
local taken = w:takeWarp(maps[mapId].warps[warpIndex])
pump(w)
return taken
end
-- ---- the door actually used ----------------------------------------------
do
local w = world("ROUTE_31")
check(walk(w, "ROUTE_31", 3), "into DARK CAVE off Route 31")
eq(w.loaded and w.loaded.id, "DARK_CAVE_VIOLET_ENTRANCE", "landing in the cave")
check(w.backupWarp ~= nil, "an outdoor-to-cave door banks the dig triple")
eq(w.backupWarp.map, "ROUTE_31", "naming the route outside")
eq(w.backupWarp.warp, 3, "and the mouth stepped on")
-- EscapeRopeOrDig's .CheckCanDig, then the warp its script runs.
local destMapId, destWarp = w:escapeRopeTarget()
eq(destMapId, "ROUTE_31", "the rope resolves back to Route 31")
check(w:runEscapeWarp(destMapId, destWarp), "and the rope warp runs")
pump(w)
eq(w.loaded.id, "ROUTE_31", "landing on Route 31")
eq(w.loaded.x, maps.ROUTE_31.warps[3].x, "on the cave mouth's own x")
eq(w.loaded.y, maps.ROUTE_31.warps[3].y, "and y")
end
-- The same cave, entered from the other side: the triple is re-banked on every
-- qualifying door, so the rope follows the player rather than the map.
do
local w = world("ROUTE_46")
check(walk(w, "ROUTE_46", 3), "into DARK CAVE off Route 46 instead")
eq(w.backupWarp.map, "ROUTE_46", "the banked map is Route 46 now")
eq(w.backupWarp.warp, 3, "with Route 46's own mouth")
local destMapId, destWarp = w:escapeRopeTarget()
eq(destMapId, "ROUTE_46", "so the rope comes out on Route 46")
eq(destWarp.x, maps.ROUTE_46.warps[3].x, "at that mouth's x")
eq(destWarp.y, maps.ROUTE_46.warps[3].y, "and y")
end
-- ---- what CheckOutdoorMap / CheckIndoorMap refuse -------------------------
do
-- Walking OUT of the cave is indoor-to-outdoor: `.SaveDigWarp` returns at
-- its first test and the banked triple is left standing.
local w = world("ROUTE_31")
check(walk(w, "ROUTE_31", 3), "in through the Route 31 mouth")
local banked = w.backupWarp
check(walk(w, "DARK_CAVE_VIOLET_ENTRANCE", 1), "and back out again")
eq(w.loaded.id, "ROUTE_31", "out on Route 31")
eq(w.backupWarp, banked, "leaving the dig triple exactly as it was")
-- A GATE counts as indoor, so a route gate banks like any other door.
check(walk(w, "ROUTE_31", 1), "into the Violet gate")
eq(w.backupWarp.map, "ROUTE_31", "a GATE is CheckIndoorMap's fourth arm")
eq(w.backupWarp.warp, 1, "banking the gate door")
-- Gate to town is outdoor-bound: nothing banks.
local townBanked = w.backupWarp
check(walk(w, "ROUTE_31_VIOLET_GATE", 1), "out of the gate into Violet")
eq(w.loaded.id, "VIOLET_CITY", "into Violet City")
eq(w.backupWarp, townBanked, "with the triple untouched")
-- Town to building is the ordinary case, and it banks.
check(walk(w, "VIOLET_CITY", 5), "into the Violet Pokemon Center")
eq(w.backupWarp.map, "VIOLET_CITY", "banking the town outside")
eq(w.backupWarp.warp, 5, "and the centre's door")
end
-- ---- the two maps the routine excludes ------------------------------------
do
local w = world("ROUTE_31")
check(walk(w, "ROUTE_31", 3), "bank a real triple first")
local banked = w.backupWarp
-- MOUNT_MOON_SQUARE is a ROUTE map inside MOUNT_MOON, so the door out of it
-- passes both environment tests and is refused by name anyway.
eq(maps.MOUNT_MOON_SQUARE.environment, "ROUTE",
"MOUNT MOON SQUARE really is an outdoor map")
eq(maps.MOUNT_MOON.environment, "CAVE", "inside a cave")
w.map = Map.new(maps.MOUNT_MOON_SQUARE,
tilesets[maps.MOUNT_MOON_SQUARE.tileset])
check(walk(w, "MOUNT_MOON_SQUARE", 1), "step off the square into the cave")
eq(w.loaded.id, "MOUNT_MOON", "into MOUNT MOON")
eq(w.backupWarp, banked,
"the square is excluded by name: no dig warp is banked there")
-- TIN_TOWER_ROOF is the other one, and it is a ROUTE map inside a DUNGEON.
eq(maps.TIN_TOWER_ROOF.environment, "ROUTE", "so is the TIN TOWER roof")
eq(maps.TIN_TOWER_9F.environment, "DUNGEON", "over a dungeon floor")
w.map = Map.new(maps.TIN_TOWER_ROOF, tilesets[maps.TIN_TOWER_ROOF.tileset])
check(walk(w, "TIN_TOWER_ROOF", 1), "down off the roof")
eq(w.loaded.id, "TIN_TOWER_9F", "onto 9F")
eq(w.backupWarp, banked, "and the roof banks nothing either")
end
-- ---- the -1 contract is untouched -----------------------------------------
do
-- The staircase writer still wins on its own arm: an INDOOR-to-INDOOR warp
-- banks nothing under the dig rule, and everything about POKECENTER_2F has
-- to keep working (tests/gen2_pokecenter_stairs_test.lua pins the rest).
local w = world("CHERRYGROVE_POKECENTER_1F")
check(walk(w, "CHERRYGROVE_POKECENTER_1F", 3), "up the centre's stairs")
eq(w.loaded.id, "POKECENTER_2F", "onto the shared 2F")
eq(w.backupWarp.map, "CHERRYGROVE_POKECENTER_1F",
"the -1 arrival banked the centre")
eq(w.backupWarp.warp, 3, "and its staircase")
-- And a dig triple banked outdoors does not make the rope work in a room
-- that is not a cave: .CheckCanDig gates on the CURRENT map's environment.
local town = world("CHERRYGROVE_CITY")
check(walk(town, "CHERRYGROVE_CITY", 2), "into the Cherrygrove centre")
eq(town.backupWarp.map, "CHERRYGROVE_CITY", "banking the town door")
eq(town:escapeRopeTarget(), nil,
"but a rope indoors still refuses: the map is not a cave or a dungeon")
end
S.finish()