mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-29 15:08:50 -04:00
8621d024dc
- Implement roaming legendary beast system (Raikou, Entei, Suicune) matching pokefirered (src/roamer.c), including 31-cap IV fix, Roar despawn fix, battle flee logic, and trapping interactions (Mean Look, Shadow Tag, Arena Trap, Wrap). - Implement Seagallop ferry crossing cutscene and transit between Sevii Islands and Kanto. - Implement field weather engine, weather extraction, and particle rendering. - Implement renewable hidden items and step events. - Fix poison step sound effect (SE_FIELD_POISON). - Fix encounter cooldown lookup for grass/cave terrain. - Add comprehensive test suites for roamers, weather, Seagallop animations, and ladders.
582 lines
20 KiB
Lua
582 lines
20 KiB
Lua
-- Game3 map loader. Owns Sevii enter: player, collision, EventObjects, Space scripts.
|
|
-- Talk/interact is Field.interact. Do not call host setMap/warpToMapId here —
|
|
-- MAPSETUP.WARP races ON_FRAME and wipes applymovement tracks (Bill intro).
|
|
|
|
local MapIds = require("src.core.game3.map_ids")
|
|
local ModRuntime = require("src.mods.Runtime")
|
|
local Map = {}
|
|
|
|
Map.current = nil
|
|
Map._announced = nil
|
|
Map.neighbors = {}
|
|
Map._loadedLayouts = {}
|
|
Map._def = nil
|
|
Map._currentDef = nil
|
|
-- pokefirered/include/overworld.h:46
|
|
Map.MUSIC_DISABLE_OFF, Map.MUSIC_DISABLE_STOP, Map.MUSIC_DISABLE_KEEP = 0, 1, 2
|
|
-- pokefirered/src/overworld.c:103
|
|
Map.disableMusicChange = 0
|
|
|
|
function Map.currentDef()
|
|
return Map._def or Map._currentDef
|
|
end
|
|
|
|
local function host_map_def(game, mapId)
|
|
local data = game and game.data
|
|
return data and data.maps and data.maps[mapId]
|
|
end
|
|
|
|
local function host_world(game)
|
|
return game and (game.overworld or game.world)
|
|
end
|
|
|
|
function Map.loadNeighborsDepth1(game, primaryDef)
|
|
Map.neighbors = {}
|
|
if not primaryDef or type(primaryDef.connections) ~= "table" then
|
|
return Map.neighbors
|
|
end
|
|
local data = game and game.data and game.data.maps
|
|
if not data then return Map.neighbors end
|
|
for dir, conn in pairs(primaryDef.connections) do
|
|
local mid = type(conn) == "table" and conn.map or conn
|
|
if type(mid) == "string" and data[mid] then
|
|
local def = data[mid]
|
|
Map.ensureMidLayout(game, mid, def)
|
|
Map.neighbors[dir] = {
|
|
map = mid,
|
|
mapId = mid,
|
|
def = def,
|
|
offset = type(conn) == "table" and (conn.offset or 0) or 0,
|
|
}
|
|
Map._loadedLayouts[mid] = true
|
|
end
|
|
end
|
|
return Map.neighbors
|
|
end
|
|
|
|
local function cell_size(def)
|
|
local L = def and def.midLayout
|
|
if L and L.width and L.height then return L.width or 0, L.height or 0 end
|
|
return (tonumber(def and def.width) or 0) * 2, (tonumber(def and def.height) or 0) * 2
|
|
end
|
|
|
|
Map.world = {}
|
|
Map._worldRoot = nil
|
|
Map._worldReachW = -1
|
|
Map._worldReachH = -1
|
|
Map.WORLD_HOPS = 2
|
|
|
|
function Map.computeWorld(maps, rootId, hops, reachW, reachH, ensure)
|
|
local out = {}
|
|
local rootDef = maps and maps[rootId]
|
|
if not rootDef then return out end
|
|
ensure = ensure or function() end
|
|
ensure(rootId, rootDef)
|
|
local rootW, rootH = cell_size(rootDef)
|
|
local placed = { [rootId] = true }
|
|
local queue = { { id = rootId, def = rootDef, ox = 0, oy = 0, hops = 0 } }
|
|
local qi = 1
|
|
local function inReach(def, ox, oy)
|
|
if not (reachW and reachH) then return false end
|
|
local w, h = cell_size(def)
|
|
return ox + w > -reachW and ox < rootW + reachW
|
|
and oy + h > -reachH and oy < rootH + reachH
|
|
end
|
|
while queue[qi] do
|
|
local cur = queue[qi]
|
|
qi = qi + 1
|
|
local curW, curH = cell_size(cur.def)
|
|
for dir, conn in pairs(cur.def.connections or {}) do
|
|
local destId = type(conn) == "table" and conn.map or conn
|
|
local destDef = type(destId) == "string" and maps[destId] or nil
|
|
if destDef and destDef ~= rootDef and not placed[destId] then
|
|
local offset = (type(conn) == "table" and tonumber(conn.offset) or 0) or 0
|
|
ensure(destId, destDef)
|
|
local destW, destH = cell_size(destDef)
|
|
local ox, oy
|
|
if dir == "north" or dir == "up" then
|
|
ox, oy = offset, -destH
|
|
elseif dir == "south" or dir == "down" then
|
|
ox, oy = offset, curH
|
|
elseif dir == "west" or dir == "left" then
|
|
ox, oy = -destW, offset
|
|
elseif dir == "east" or dir == "right" then
|
|
ox, oy = curW, offset
|
|
end
|
|
if ox then
|
|
ox, oy = cur.ox + ox, cur.oy + oy
|
|
local reach = inReach(destDef, ox, oy)
|
|
if cur.hops + 1 <= (hops or 0) or reach then
|
|
placed[destId] = true
|
|
out[#out + 1] = { id = destId, def = destDef, ox = ox, oy = oy }
|
|
if cur.hops + 1 < (hops or 0) or reach then
|
|
queue[#queue + 1] = {
|
|
id = destId, def = destDef, ox = ox, oy = oy, hops = cur.hops + 1,
|
|
}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return out
|
|
end
|
|
|
|
function Map.refreshWorld(game, reachW, reachH, rootId)
|
|
rootId = rootId or Map.current
|
|
local maps = game and game.data and game.data.maps
|
|
if not (rootId and maps) then
|
|
Map.world = {}
|
|
Map._worldRoot = nil
|
|
return Map.world
|
|
end
|
|
reachW = math.floor(tonumber(reachW) or 0)
|
|
reachH = math.floor(tonumber(reachH) or 0)
|
|
if Map._worldRoot == rootId
|
|
and Map._worldReachW == reachW and Map._worldReachH == reachH then
|
|
return Map.world
|
|
end
|
|
Map.world = Map.computeWorld(maps, rootId, Map.WORLD_HOPS, reachW, reachH,
|
|
function(id, def)
|
|
Map.ensureMidLayout(game, id, def)
|
|
end)
|
|
for _, entry in ipairs(Map.world) do
|
|
Map._loadedLayouts[entry.id] = true
|
|
end
|
|
Map._worldRoot = rootId
|
|
Map._worldReachW = reachW
|
|
Map._worldReachH = reachH
|
|
local FieldView = package.loaded["src.core.game3.field_view"]
|
|
if FieldView then FieldView._nativeDirty = true end
|
|
return Map.world
|
|
end
|
|
|
|
function Map.overscanSlices()
|
|
local slices = {}
|
|
for dir, n in pairs(Map.neighbors) do
|
|
slices[#slices + 1] = { dir = dir, mapId = n.map or n.mapId, offset = n.offset }
|
|
end
|
|
return slices
|
|
end
|
|
|
|
local function neighborFor(cardinal)
|
|
local n = Map.neighbors
|
|
if not n then return nil end
|
|
-- Dataset keys are north/south/west/east; some callers use up/down/left/right.
|
|
if cardinal == "north" or cardinal == "up" then
|
|
return n.north or n.up
|
|
elseif cardinal == "south" or cardinal == "down" then
|
|
return n.south or n.down
|
|
elseif cardinal == "west" or cardinal == "left" then
|
|
return n.west or n.left
|
|
elseif cardinal == "east" or cardinal == "right" then
|
|
return n.east or n.right
|
|
end
|
|
return n[cardinal]
|
|
end
|
|
|
|
--- Resolve a cell in current-map space, sampling connected neighbors when OOB
|
|
-- (pret VMap connection fill). Returns mid, sourcePair. OOB with no neighbor
|
|
-- falls through to primary border tiling.
|
|
function Map.worldMidAt(cx, cy, primaryDef)
|
|
local layout = primaryDef and primaryDef.midLayout
|
|
if not layout then return 0, nil end
|
|
local w, h = layout.width or 0, layout.height or 0
|
|
local primaryPair = layout.pair or primaryDef.pair
|
|
|
|
local function fromNeighbor(n, nx, ny)
|
|
if not n or not n.def then return nil end
|
|
local L = n.def.midLayout
|
|
if not L then return nil end
|
|
if nx < 0 or ny < 0 or nx >= (L.width or 0) or ny >= (L.height or 0) then
|
|
return nil
|
|
end
|
|
local pair = L.pair or n.def.pair
|
|
return L:midAt(nx, ny), pair or primaryPair
|
|
end
|
|
|
|
if cx >= 0 and cy >= 0 and cx < w and cy < h then
|
|
return layout:midAt(cx, cy), primaryPair
|
|
end
|
|
|
|
if cy < 0 then
|
|
local n = neighborFor("north")
|
|
if n then
|
|
local offset = tonumber(n.offset) or 0
|
|
local L = n.def and n.def.midLayout
|
|
local nh = L and L.height or 0
|
|
local mid, pair = fromNeighbor(n, cx - offset, nh + cy)
|
|
if mid ~= nil then return mid, pair end
|
|
end
|
|
elseif cy >= h then
|
|
local n = neighborFor("south")
|
|
if n then
|
|
local offset = tonumber(n.offset) or 0
|
|
local mid, pair = fromNeighbor(n, cx - offset, cy - h)
|
|
if mid ~= nil then return mid, pair end
|
|
end
|
|
end
|
|
|
|
if cx < 0 then
|
|
local n = neighborFor("west")
|
|
if n then
|
|
local offset = tonumber(n.offset) or 0
|
|
local L = n.def and n.def.midLayout
|
|
local nw = L and L.width or 0
|
|
local mid, pair = fromNeighbor(n, nw + cx, cy - offset)
|
|
if mid ~= nil then return mid, pair end
|
|
end
|
|
elseif cx >= w then
|
|
local n = neighborFor("east")
|
|
if n then
|
|
local offset = tonumber(n.offset) or 0
|
|
local mid, pair = fromNeighbor(n, cx - w, cy - offset)
|
|
if mid ~= nil then return mid, pair end
|
|
end
|
|
end
|
|
|
|
for _, entry in ipairs(Map.world) do
|
|
local L = entry.def ~= primaryDef and entry.def and entry.def.midLayout
|
|
if L then
|
|
local nx, ny = cx - entry.ox, cy - entry.oy
|
|
if nx >= 0 and ny >= 0 and nx < (L.width or 0) and ny < (L.height or 0) then
|
|
return L:midAt(nx, ny), L.pair or entry.def.pair or primaryPair
|
|
end
|
|
end
|
|
end
|
|
|
|
return layout:midAt(cx, cy), primaryPair, true
|
|
end
|
|
|
|
--- Ensure mapDef.midLayout is bound (lazy; Dataset.hydrate usually did this).
|
|
function Map.ensureMidLayout(game, mapId, def)
|
|
def = def or host_map_def(game, mapId)
|
|
if not def then return nil end
|
|
if def.midLayout then return def.midLayout end
|
|
local Dataset = require("src.core.game3.dataset")
|
|
if Dataset.attachMidLayouts and game and game.data and game.data.maps then
|
|
Dataset.attachMidLayouts({ [mapId] = def })
|
|
end
|
|
return def.midLayout
|
|
end
|
|
|
|
--- Load a Sevii map under game3 ownership (pret enter order).
|
|
-- 1) Bind game3 player + collision + EventObjects
|
|
-- 2) Objects.loadMap then Space.runEnterScripts (ON_TRANSITION → ON_FRAME)
|
|
function Map.load(mod, game, mapId, opts)
|
|
opts = opts or {}
|
|
if not MapIds.isGame3Map(mapId) then
|
|
return nil, "not a game3 map"
|
|
end
|
|
-- pret RestartWildEncounterImmunitySteps on LoadMap / LoadMapFromWarp: every
|
|
-- map entry restarts the wild encounter grace period. Unconditional, so the
|
|
-- seamless connection crossing between two routes resets it too.
|
|
do
|
|
local okE, Encounters = pcall(require, "src.core.game3.encounters")
|
|
if okE and Encounters and Encounters.resetRateModifiers then
|
|
Encounters.resetRateModifiers()
|
|
end
|
|
local okR, Roamer = pcall(require, "src.core.game3.roamer")
|
|
if okR and Roamer and Roamer.move then
|
|
local okRt, Runtime = pcall(require, "src.core.game3.runtime")
|
|
local session = okRt and Runtime and Runtime.getSession and Runtime.getSession()
|
|
if session and session.roamer and session.roamer.active then
|
|
local fromMapId = Map._announced
|
|
if fromMapId ~= nil and fromMapId ~= mapId then
|
|
local moveReason = (opts.teleport or opts.fly or opts.whiteout) and "warp_random" or "map_transition"
|
|
Roamer.move(session, moveReason)
|
|
elseif opts.teleport or opts.fly or opts.whiteout then
|
|
Roamer.move(session, "warp_random")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
local Ghosts = require("src.core.game3.ghosts")
|
|
local fromMapId = Map._announced
|
|
if Map.current and Map.current ~= mapId then
|
|
Ghosts.capture(Map.current)
|
|
end
|
|
if fromMapId and fromMapId ~= mapId and ModRuntime.wants("map.exited") then
|
|
ModRuntime.emit("map.exited", { mapId = fromMapId, toMapId = mapId })
|
|
end
|
|
Map._announced = mapId
|
|
Map.current = mapId
|
|
Map._loadedLayouts = { [mapId] = true }
|
|
-- overworld.c:792, overworld.c:759
|
|
Map._worldRoot = nil
|
|
|
|
local def = host_map_def(game, mapId)
|
|
if not def then
|
|
local okD, Dataset = pcall(require, "src.core.game3.dataset")
|
|
if okD and Dataset and Dataset.map then
|
|
def = Dataset.map(mapId)
|
|
end
|
|
end
|
|
Map.ensureMidLayout(game, mapId, def)
|
|
Map._def = def
|
|
Map._currentDef = def
|
|
-- pokefirered/src/overworld.c:878 GetAdjustedInitialTransitionFlags
|
|
local keepBike = false
|
|
do
|
|
local Player = require("src.core.game3.player")
|
|
if Player.biking then
|
|
local allowed = def and def.bikingAllowed
|
|
if allowed ~= nil then
|
|
-- pokefirered/src/overworld.c:948 Overworld_IsBikingAllowed
|
|
keepBike = (tonumber(allowed) or 0) ~= 0
|
|
else
|
|
local pair = def and (def.pair or (def.midLayout and def.midLayout.pair))
|
|
keepBike = type(pair) == "string" and pair:find("outdoor", 1, true) ~= nil
|
|
end
|
|
Player.biking = keepBike
|
|
end
|
|
end
|
|
if opts.depth1Connections ~= false then
|
|
Map.loadNeighborsDepth1(game, def)
|
|
else
|
|
Map.neighbors = {}
|
|
end
|
|
|
|
local world = host_world(game)
|
|
local x = tonumber(opts.x) or 0
|
|
local y = tonumber(opts.y) or 0
|
|
local facing = opts.facing or "down"
|
|
|
|
local Runtime = require("src.core.game3.runtime")
|
|
if not Runtime.isActive or not Runtime.isActive() then
|
|
if Runtime.ensureActiveForMap then
|
|
Runtime.ensureActiveForMap(mod, game, mapId)
|
|
end
|
|
end
|
|
|
|
local session = Runtime.getSession and Runtime.getSession()
|
|
if session then
|
|
session.map = mapId
|
|
session.x = x
|
|
session.y = y
|
|
session.facing = facing
|
|
end
|
|
|
|
-- Keep save.position current for ferry exit / host save without setMap.
|
|
local save = game and game.save
|
|
if save then
|
|
save.position = save.position or {}
|
|
save.position.map = mapId
|
|
save.position.x = x
|
|
save.position.y = y
|
|
save.position.facing = facing
|
|
end
|
|
|
|
local Player = require("src.core.game3.player")
|
|
if opts.seamless then
|
|
-- Connection remap (pret LoadMapFromCameraTransition): keep mid-step motion.
|
|
-- Caller parks one cell before landing and sets target toward landing.
|
|
Player.cellX = x
|
|
Player.cellY = y
|
|
Player.px = x * 16
|
|
Player.py = y * 16
|
|
Player.facing = facing
|
|
else
|
|
Player.reset(x, y, facing)
|
|
end
|
|
-- pokefirered/src/overworld.c:2145 SetPlayerAvatarTransitionFlags
|
|
Player.biking = keepBike
|
|
Player.syncSavePosition(game)
|
|
|
|
local okFv, FieldView = pcall(require, "src.core.game3.field_view")
|
|
if okFv and FieldView then FieldView._nativeDirty = true end
|
|
|
|
-- Scripts/events before spawn so Objects.loadMap sees mapDef.objects.
|
|
local Space = package.loaded["src.core.game3.scripting.space"]
|
|
or require("src.core.game3.scripting.space")
|
|
if Space.ensureBundle then Space.ensureBundle(mod or Runtime._mod) end
|
|
if def and Space.attachEventsToMaps and game and game.data and game.data.maps then
|
|
Space.attachEventsToMaps({ [mapId] = def }, Space.bundle)
|
|
end
|
|
|
|
-- pokefirered/src/fieldmap.c:93
|
|
require("src.core.game3.field").clearMetatiles(def and def.midLayout)
|
|
local Collision = require("src.core.game3.collision")
|
|
if def then
|
|
Collision.bindMap(game, mapId, def)
|
|
else
|
|
-- No def for this id. Keeping the previous map's grid bound would validate
|
|
-- movement against the map we just left; unbind so canEnter falls back to
|
|
-- the host map (collision.lua: "Prefer owned grid; fall back to host map").
|
|
Collision.clear()
|
|
end
|
|
|
|
-- pret GroundEffect_SpawnOnTallGrass when warping onto grass.
|
|
if not opts.seamless then
|
|
local onGrass = Collision.isGrass and Collision.isGrass(Player.cellX, Player.cellY)
|
|
local okE, Encounters = pcall(require, "src.core.game3.encounters")
|
|
if okE and Encounters and Encounters.noteGrass then
|
|
Encounters.noteGrass(onGrass)
|
|
end
|
|
local okFx, FieldEffects = pcall(require, "src.core.game3.field_effects")
|
|
if okFx and FieldEffects then
|
|
if onGrass and FieldEffects.tallGrassAt then
|
|
FieldEffects.tallGrassAt(Player.cellX, Player.cellY, true)
|
|
elseif FieldEffects.clearTallGrass then
|
|
FieldEffects.clearTallGrass()
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Activate scripts (flag store) → spawn destination NPCs → ON_TRANSITION.
|
|
-- pret order: never run setobjectxyperm against the previous map's localIds
|
|
-- (Pallet sign-lady localId=1 was teleporting Mom on FR_PLAYERS_HOUSE_1F).
|
|
local Field = require("src.core.game3.field")
|
|
if not opts.seamless then
|
|
Field.lock()
|
|
end
|
|
|
|
local Objects = require("src.core.game3.objects")
|
|
-- pokefirered/src/overworld.c:800
|
|
if fromMapId and (fromMapId ~= mapId or opts.heal) then
|
|
require("src.core.game3.vs_seeker").mapReset(session)
|
|
end
|
|
if Space and Space.activate then
|
|
Space.activate(mod or Runtime._mod, mapId, game, world)
|
|
end
|
|
|
|
-- pokefirered/src/overworld.c:805
|
|
local savedFlash = session and tonumber(session.flashLevel)
|
|
if okFv and FieldView and FieldView.setDefaultFlashLevel then
|
|
FieldView.setDefaultFlashLevel(game, mapId)
|
|
-- pokefirered/src/overworld.c:1691 CB2_ContinueSavedGame
|
|
if savedFlash and (opts.enterVia or Map._nextEnterVia) == "continue" then
|
|
FieldView.setFlashLevel(savedFlash)
|
|
end
|
|
end
|
|
|
|
if def then
|
|
Objects.loadMap(game, mapId, def)
|
|
Ghosts.adopt(mapId)
|
|
end
|
|
-- pokefirered/src/overworld.c:771 / :808 TryRegenerateRenewableHiddenItems
|
|
local okRen, Renewable = pcall(require, "src.core.game3.renewable_hidden_items")
|
|
if okRen and Renewable and Renewable.tryRegenerate then
|
|
Renewable.tryRegenerate(session, def and (def.group or (def.pair and def.pair[1])), def and (def.num or (def.pair and def.pair[2])), mapId)
|
|
end
|
|
-- pokefirered/src/overworld.c:809 SetCurrentAndNextWeather
|
|
if def and def.weather ~= nil then
|
|
local Weather = require("src.core.game3.weather")
|
|
Weather.apply(def.weather)
|
|
end
|
|
-- pokefirered/src/overworld.c:769
|
|
-- pokefirered/src/overworld.c:806
|
|
require("src.core.game3.audio").setSavedSong(nil)
|
|
if fromMapId == mapId then
|
|
if opts.reason and ModRuntime.wants("map.reloaded") then
|
|
ModRuntime.emit("map.reloaded", { mapId = mapId, reason = opts.reason or "reload" })
|
|
end
|
|
elseif ModRuntime.wants("map.entered") then
|
|
ModRuntime.emit("map.entered", {
|
|
mapId = mapId, map = def, fromMapId = fromMapId,
|
|
via = opts.via or (opts.seamless and "connection")
|
|
or (opts.heal and "respawn") or (fromMapId and "warp" or "boot"),
|
|
})
|
|
end
|
|
-- pokefirered/src/overworld.c:1717
|
|
local enterVia = opts.enterVia or Map._nextEnterVia
|
|
Map._nextEnterVia = nil
|
|
if Space and Space.runEnterScripts then
|
|
Space.runEnterScripts(mod or Runtime._mod, mapId, game, world,
|
|
{ seamless = opts.seamless, enterVia = enterVia })
|
|
elseif Space and Space.onMapEnter then
|
|
Space.onMapEnter(mod or Runtime._mod, mapId, game, world)
|
|
end
|
|
|
|
-- Map BGM from extract index / header music.
|
|
do
|
|
local Audio = require("src.core.game3.audio")
|
|
local music = def and def.music
|
|
if not music and Audio._pack and Audio._pack.index and Audio._pack.index.mapSongs then
|
|
music = Audio._pack.index.mapSongs[mapId]
|
|
end
|
|
-- pokefirered/src/overworld.c:1063
|
|
if Map.disableMusicChange == Map.MUSIC_DISABLE_STOP then
|
|
Audio.playSong(0)
|
|
music = nil
|
|
elseif Map.disableMusicChange == Map.MUSIC_DISABLE_KEEP then
|
|
music = nil
|
|
end
|
|
if music and music ~= 0xFFFF then
|
|
local id
|
|
if opts.seamless then
|
|
-- pokefirered/src/overworld.c:1075
|
|
local fromDef = fromMapId and host_map_def(game, fromMapId)
|
|
if Audio._currentSong and Audio._currentSong.id == Audio.MUS_SURF then
|
|
Audio.setMapSong(music)
|
|
elseif Audio.specialMapSong(fromDef and fromDef.regionMapSectionId) == Audio.MUS_SURF then
|
|
id = Audio.MUS_SURF
|
|
else
|
|
id = music
|
|
end
|
|
else
|
|
-- pokefirered/src/overworld.c:1039
|
|
id = Audio._savedSong or (Audio.specialMapSong(def and def.regionMapSectionId) == Audio.MUS_SURF
|
|
and Audio.MUS_SURF) or music
|
|
end
|
|
if id then Audio.playMapSong(id, { mapSong = music }) end
|
|
end
|
|
end
|
|
|
|
-- Location change overlay (pokefirered/src/overworld.c:785, 1687, 1922)
|
|
-- Strict arbiter: gMapHeader.showMapName == TRUE. If 0/false, strictly suppress popup.
|
|
-- overworld.c:1913 gives a changed map section with a FOREST preview screen
|
|
-- precedence over the popup; pret gates that on a real warp, not a connection.
|
|
do
|
|
local currSec = def and (def.regionMapSectionId or def.region_map_section_id)
|
|
local lastSec = Map._lastSectionId
|
|
local showFlag = def and (def.showMapName or def.show_map_name)
|
|
local previewed = false
|
|
|
|
if currSec and not opts.seamless and lastSec ~= currSec then
|
|
local okPreview, MapPreviewScreen = pcall(require, "src.ui.game3.map_preview_screen")
|
|
if okPreview and MapPreviewScreen then
|
|
MapPreviewScreen.dismiss()
|
|
previewed = MapPreviewScreen.show(currSec) == true
|
|
end
|
|
end
|
|
|
|
local okPop, MapNamePopup = pcall(require, "src.ui.game3.map_name_popup")
|
|
if okPop and MapNamePopup then
|
|
if previewed then
|
|
MapNamePopup.dismiss()
|
|
elseif showFlag == 0 or showFlag == false then
|
|
MapNamePopup.dismiss()
|
|
elseif showFlag == 1 or showFlag == true then
|
|
if lastSec == nil or lastSec ~= currSec or not opts.seamless then
|
|
MapNamePopup.show(def)
|
|
end
|
|
end
|
|
end
|
|
if def and def.regionMapSectionId then
|
|
Map._lastSectionId = def.regionMapSectionId
|
|
end
|
|
end
|
|
|
|
if not opts.seamless then
|
|
if not Space._pendingOnFrame
|
|
and not (Space.vm and Space.vm.isRunning and Space.vm:isRunning()) then
|
|
Field.unlock()
|
|
end
|
|
end
|
|
|
|
return {
|
|
mapId = mapId,
|
|
neighbors = Map.neighbors,
|
|
overscan = Map.overscanSlices(),
|
|
}
|
|
end
|
|
|
|
function Map.loadedLayoutCount()
|
|
local n = 0
|
|
for _ in pairs(Map._loadedLayouts) do n = n + 1 end
|
|
return n
|
|
end
|
|
|
|
return Map
|