mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-15 15:51:17 +02:00
126 lines
4.6 KiB
Lua
126 lines
4.6 KiB
Lua
-- Warp resolution. A warp fires when:
|
|
-- * the player finishes a step onto a warp cell whose collision tile is a
|
|
-- door tile or warp tile (stairs, doors, mats, cave entrances), or
|
|
-- * the player stands on a warp cell and tries to walk off the map edge
|
|
-- (exit carpets at the bottom of interiors), or
|
|
-- * the player stands on a warp cell and the "extra" check passes -- on
|
|
-- arrival with the d-pad held, or on a blocked step (route-gate
|
|
-- doorways, the Vermilion dock entrance, ...).
|
|
-- This mirrors pokered's CheckWarpsNoCollision / CheckWarpsCollision /
|
|
-- ExtraWarpCheck (home/overworld.asm).
|
|
|
|
local Runtime = require("src.mods.Runtime")
|
|
|
|
local Warp = {}
|
|
|
|
-- Returns the warp entry to take when arriving at (cx,cy), or nil.
|
|
function Warp.onArrive(map, cx, cy)
|
|
local w = map:warpAtCell(cx, cy)
|
|
if w and map:isWarpTileCell(cx, cy) then
|
|
return w
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function inList(list, v)
|
|
for _, x in ipairs(list) do
|
|
if x == v then return true end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- ExtraWarpCheck: may the player standing at (cx,cy) facing dir warp
|
|
-- without a door/warp tile underfoot? On the carpet maps/tilesets the
|
|
-- tile in FRONT of the player must be a warp-carpet tile for the facing
|
|
-- direction (IsWarpTileInFrontOfPlayer; SS_ANNE_BOW tests one hardcoded
|
|
-- tile instead); everywhere else the player must face the map edge
|
|
-- (IsPlayerFacingEdgeOfMap). carpets = field.warpCarpets.
|
|
function Warp.extraCheck(map, carpets, cx, cy, dir)
|
|
local Collision = require("src.world.Collision")
|
|
local facingEdge =
|
|
(dir == "up" and cy == 0)
|
|
or (dir == "down" and cy == map.heightCells - 1)
|
|
or (dir == "left" and cx == 0)
|
|
or (dir == "right" and cx == map.widthCells - 1)
|
|
if not carpets then return facingEdge end
|
|
-- the map exceptions are tested before the tileset (ExtraWarpCheck)
|
|
local useCarpet
|
|
if inList(carpets.edgeMaps, map.id) then
|
|
useCarpet = false
|
|
elseif inList(carpets.function2Maps, map.id) then
|
|
useCarpet = true
|
|
else
|
|
useCarpet = inList(carpets.function2Tilesets, map.def.tileset)
|
|
end
|
|
if not useCarpet then return facingEdge end
|
|
local tx, ty = Collision.target(cx, cy, dir)
|
|
local front = map:cellTile(tx, ty)
|
|
if map.id == carpets.ssAnneBow.map then
|
|
return front == carpets.ssAnneBow.tile
|
|
end
|
|
return inList(carpets.tiles[dir], front)
|
|
end
|
|
|
|
-- Returns the warp entry when standing on (cx,cy) and the extra check
|
|
-- passes toward dir (fired from a blocked step, or on arrival with the
|
|
-- d-pad held).
|
|
function Warp.onCollision(map, carpets, cx, cy, dir)
|
|
local w = map:warpAtCell(cx, cy)
|
|
if w and Warp.extraCheck(map, carpets, cx, cy, dir) then
|
|
return w
|
|
end
|
|
return nil
|
|
end
|
|
|
|
-- Returns the warp entry when standing on (cx,cy) and moving toward dir
|
|
-- takes the player out of bounds.
|
|
function Warp.onEdge(map, cx, cy, dir)
|
|
local w = map:warpAtCell(cx, cy)
|
|
if not w then return nil end
|
|
local Collision = require("src.world.Collision")
|
|
local tx, ty = Collision.target(cx, cy, dir)
|
|
if not map:inBounds(tx, ty) then
|
|
return w
|
|
end
|
|
return nil
|
|
end
|
|
|
|
-- Resolve a warp's destination to map id + cell. LAST_MAP destinations
|
|
-- (returning from an interior) resolve against the remembered outdoor
|
|
-- map; the landing cell is that map's warp entry named by the warp id
|
|
-- (wDestinationWarpID placement -- two-sided route gates land you on
|
|
-- the side you exit, not where you entered).
|
|
local function resolve(data, warpDef, lastMap)
|
|
local destMap = warpDef.destMap
|
|
if destMap == "LAST_MAP" then
|
|
assert(lastMap, "LAST_MAP warp with no remembered outdoor map")
|
|
destMap = lastMap.id
|
|
local destDef = data.maps[destMap]
|
|
local dw = destDef and destDef.warps[warpDef.destWarp]
|
|
if dw then
|
|
return destMap, dw.x, dw.y
|
|
end
|
|
-- out-of-range data: fall back to where the player entered
|
|
return destMap, lastMap.x, lastMap.y
|
|
end
|
|
local destDef = data.maps[destMap]
|
|
assert(destDef, "warp to unknown map " .. tostring(destMap))
|
|
local dw = destDef.warps[warpDef.destWarp]
|
|
assert(dw, ("warp to %s#%d out of range"):format(destMap, warpDef.destWarp))
|
|
return destMap, dw.x, dw.y
|
|
end
|
|
|
|
-- the resolved destination passes through warp.destination, so a mod can
|
|
-- reroute one door without owning the warp table (ctx carries the warp
|
|
-- record and the remembered outdoor side the resolution used)
|
|
local function warped(mapId, x, y) return mapId, x, y end
|
|
|
|
function Warp.destination(data, warpDef, lastMap)
|
|
local destMap, x, y = resolve(data, warpDef, lastMap)
|
|
if not Runtime.wantsHook("warp.destination") then return destMap, x, y end
|
|
return Runtime.call("warp.destination", warped, destMap, x, y,
|
|
{ warp = warpDef, lastMap = lastMap, data = data })
|
|
end
|
|
|
|
return Warp
|