mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 16:31:05 +02:00
132 lines
6.3 KiB
Lua
132 lines
6.3 KiB
Lua
-- Parity test (#805): ESCAPE ROPE / DIG / TELEPORT must land on an outdoor
|
|
-- fly-warp cell, and must re-point the LAST_MAP memory at it.
|
|
--
|
|
-- pret: ItemUseEscapeRope (engine/items/item_effects.asm) sets BIT_FLY_WARP
|
|
-- and BIT_ESCAPE_WARP, and LoadSpecialWarpData's .usedFlyWarp path
|
|
-- (engine/overworld/special_warps.asm) warps to wLastBlackoutMap with the
|
|
-- landing cell read from FlyWarpDataPtr. wLastBlackoutMap is ALWAYS an
|
|
-- outdoor map: SetLastBlackoutMap (engine/events/set_blackout_map.asm)
|
|
-- copies wLastMap, and WarpFound2 (home/overworld.asm) only writes wLastMap
|
|
-- when CheckIfInOutsideMap passes. PrepareForSpecialWarp
|
|
-- (engine/overworld/special_warps.asm) then does `ld [wLastMap], a` with
|
|
-- that destination for every fly/escape warp that is not a dungeon warp.
|
|
--
|
|
-- The port broke both halves. A .sav import stamps lastHeal from wherever
|
|
-- the cartridge was saved (src/save_convert/SaveConvert.lua mergeDefaults,
|
|
-- which records no outdoor), so a save made inside Seafoam Islands made
|
|
-- ESCAPE ROPE warp the player back into that cave; and the teleport branch
|
|
-- skipped rememberOutdoor, so the first LAST_MAP exit after the rope still
|
|
-- resolved against the dungeon door walked in through.
|
|
--
|
|
-- Self-contained; run via `luajit tests/parity_escape_rope_bug805.lua`.
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
if not _G.love then _G.love = require("tests.love_stub") end
|
|
local Data = require("src.core.Data")
|
|
if not (Data.maps and Data.maps.PALLET_TOWN) then Data:load() end
|
|
local S = require("tests.harness").suite("parity escape rope #805")
|
|
local check, eq = S.check, S.eq
|
|
|
|
require("src.render.Font").load(Data)
|
|
local Game = require("src.core.Game")
|
|
local Input = require("src.core.Input")
|
|
local StateStack = require("src.core.StateStack")
|
|
local Renderer = require("src.render.Renderer")
|
|
local SaveData = require("src.core.SaveData")
|
|
local Pokemon = require("src.pokemon.Pokemon")
|
|
local Map = require("src.world.Map")
|
|
local FieldDefaults = require("src.world.FieldDefaults")
|
|
local OW = require("src.world.OverworldController")
|
|
|
|
Game.data = Data
|
|
Game.input = Input; Input:init()
|
|
Game.renderer = Renderer; Renderer:init()
|
|
Game.stack = StateStack; StateStack:init()
|
|
Game.save = SaveData.newGame()
|
|
Game.save.party = { Pokemon.new(Data, "SQUIRTLE", 20) }
|
|
|
|
-- The player is deep in a cave, having walked in from the outdoor door
|
|
-- cell the LAST_MAP exits still point at.
|
|
Game.stack:push(OW, "SEAFOAM_ISLANDS_B2F", 5, 5, "down")
|
|
local ow = Game.stack:top()
|
|
Game.overworld = ow
|
|
|
|
-- Capture the warp target instead of running the real Transition.
|
|
local dest
|
|
local realStart = ow.startWarpTo
|
|
ow.startWarpTo = function(self, mapId, x, y, facing, onDone, opts)
|
|
dest = { map = mapId, x = x, y = y }
|
|
self.arriveWarp = nil
|
|
self.transitioning = false
|
|
end
|
|
|
|
local outsideTilesets = FieldDefaults.field(Data, "outsideTilesets")
|
|
local function isOutside(mapId)
|
|
local def = Data.maps[mapId]
|
|
return def ~= nil and Map.isOutside(def, outsideTilesets)
|
|
end
|
|
|
|
local flyWarps = Data.field.flyWarps or {}
|
|
local bootHeal = SaveData.defaultHeal(Data.field.boot)
|
|
|
|
-- --------------------------------------------------------------- 1. healthy
|
|
-- A save healed by a nurse records the outdoor town alongside the interior
|
|
-- heal cell; the rope lands on that town's FlyWarpDataPtr cell.
|
|
Game.save.lastHeal = { map = "VIRIDIAN_POKECENTER", x = 3, y = 3,
|
|
outdoor = { id = "VIRIDIAN_CITY", x = 23, y = 27 } }
|
|
ow:rememberOutdoor("ROUTE_23", 8, 60) -- the Victory Road door walked in from
|
|
dest = nil
|
|
ow:warpToHealPoint(nil, { arrive = "teleport" })
|
|
|
|
check(flyWarps.VIRIDIAN_CITY ~= nil, "Viridian City has a fly warp cell")
|
|
eq(dest.map, "VIRIDIAN_CITY", "healthy heal record: rope lands on the town")
|
|
eq(dest.x, flyWarps.VIRIDIAN_CITY.x, "rope lands on the FlyWarpDataPtr x")
|
|
eq(dest.y, flyWarps.VIRIDIAN_CITY.y, "rope lands on the FlyWarpDataPtr y")
|
|
|
|
-- 3. PrepareForSpecialWarp: the destination becomes the new wLastMap, so a
|
|
-- LAST_MAP exit taken after the rope resolves against the town just landed
|
|
-- in, not the dungeon door from before.
|
|
eq(Game.save.lastOutdoor.id, "VIRIDIAN_CITY",
|
|
"teleport warp re-points wLastMap at the destination (#805)")
|
|
eq(Game.save.lastOutdoor.x, dest.x, "wLastMap x follows the landing cell")
|
|
eq(Game.save.lastOutdoor.y, dest.y, "wLastMap y follows the landing cell")
|
|
|
|
-- --------------------------------------------------------------- 2. imported
|
|
-- Exactly what SaveConvert stamps for a cartridge save made in a cave: the
|
|
-- player's own cell, no outdoor town. wLastBlackoutMap can never name an
|
|
-- indoor map, so this record is unusable and falls back to the boot heal
|
|
-- town (vanilla's zero-filled wLastBlackoutMap is map 0, Pallet Town).
|
|
Game.save.lastHeal = { map = "SEAFOAM_ISLANDS_B2F", x = 5, y = 5 }
|
|
ow:rememberOutdoor("ROUTE_23", 8, 60)
|
|
dest = nil
|
|
ow:warpToHealPoint(nil, { arrive = "teleport" })
|
|
|
|
check(not isOutside("SEAFOAM_ISLANDS_B2F"),
|
|
"Seafoam Islands B2F is not an outside map")
|
|
check(dest.map ~= "SEAFOAM_ISLANDS_B2F",
|
|
"imported heal record does not dump the rope back in the cave (#805)")
|
|
check(isOutside(dest.map), "escape-warp destination is always an outside map")
|
|
eq(dest.map, bootHeal.map, "unusable heal record falls back to the boot town")
|
|
eq(dest.x, bootHeal.x, "boot-town fallback keeps its landing x")
|
|
eq(dest.y, bootHeal.y, "boot-town fallback keeps its landing y")
|
|
eq(Game.save.lastOutdoor.id, bootHeal.map,
|
|
"fallback landing is remembered as wLastMap too")
|
|
|
|
-- --------------------------------------------------------------- 3. blackout
|
|
-- A blackout (no opts) still lands on the interior heal cell and re-points
|
|
-- LAST_MAP exits at the remembered town door: HandleBlackOut never sets
|
|
-- BIT_FLY_WARP, so it is not a special warp destination of its own.
|
|
Game.save.lastHeal = { map = "VIRIDIAN_POKECENTER", x = 3, y = 3,
|
|
outdoor = { id = "VIRIDIAN_CITY", x = 23, y = 27 } }
|
|
ow:rememberOutdoor("ROUTE_23", 8, 60)
|
|
dest = nil
|
|
ow:warpToHealPoint()
|
|
|
|
eq(dest.map, "VIRIDIAN_POKECENTER", "blackout still lands at the heal cell")
|
|
eq(Game.save.lastOutdoor.id, "VIRIDIAN_CITY",
|
|
"blackout re-points wLastMap at the remembered town door")
|
|
eq(Game.save.lastOutdoor.x, 23, "blackout keeps the recorded door x")
|
|
eq(Game.save.lastOutdoor.y, 27, "blackout keeps the recorded door y")
|
|
|
|
ow.startWarpTo = realStart
|
|
S.finish()
|