-- 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()