mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-26 07:21:22 +02:00
Merge branch 'dev' into spidercar2
This commit is contained in:
+15
-2
@@ -114,12 +114,25 @@ function MapLoader.invalidateAll()
|
||||
lru = {}
|
||||
end
|
||||
|
||||
-- Eagerly release every resident map's TileRenderer GPU objects. Only safe
|
||||
-- when nothing live holds map instances (session end after game:reset).
|
||||
function MapLoader.releaseAll()
|
||||
local ids = {}
|
||||
for id in pairs(cache) do ids[#ids + 1] = id end
|
||||
for _, id in ipairs(ids) do MapLoader.evict(id) end
|
||||
end
|
||||
|
||||
-- kept as the pre-v2 name
|
||||
MapLoader.clearCache = MapLoader.invalidateAll
|
||||
|
||||
-- the cached Map objects own the per-map TileRenderer instances, so a flush
|
||||
-- that skipped this one would leave live SpriteBatches built from the old
|
||||
-- search path (14 cache-invalidation contract, rows 1 and 3)
|
||||
Assets.register(MapLoader.invalidateAll)
|
||||
-- search path (14 cache-invalidation contract, rows 1 and 3). invalidateAll
|
||||
-- deliberately does NOT release GPU (hot reload / live overworld); releaseAll
|
||||
-- is the session-end path only.
|
||||
Assets.register({
|
||||
invalidate = MapLoader.invalidateAll,
|
||||
release = MapLoader.releaseAll,
|
||||
})
|
||||
|
||||
return MapLoader
|
||||
|
||||
@@ -246,6 +246,11 @@ function OverworldState.computeNeighbors(maps, rootId, hops, reachW, reachH)
|
||||
return out
|
||||
end
|
||||
|
||||
function OverworldState:exit()
|
||||
self.map = nil
|
||||
self.neighbors = nil
|
||||
end
|
||||
|
||||
function OverworldState:enter(mapId, x, y, facing, opts)
|
||||
Game = require("src.core.Game")
|
||||
Game.overworld = self
|
||||
@@ -2125,6 +2130,10 @@ function OverworldState:pushableAtCell(cx, cy)
|
||||
return nil
|
||||
end
|
||||
|
||||
-- world.talk's fallthrough, hoisted so the A press does not build a closure
|
||||
-- on every press just to have one to hand a hook nobody may have wrapped
|
||||
local function vanillaTalk(ow, target) ow:talkTo(target) end
|
||||
|
||||
-- what the A press resolved to, for world.interacted's listeners
|
||||
local function interacted(self, fx, fy, kind, target)
|
||||
Runtime.emit("world.interacted", { mapId = self.map.id, x = fx, y = fy,
|
||||
@@ -2154,7 +2163,12 @@ function OverworldState:interact()
|
||||
-- talk() lands the follower on its cell first.
|
||||
require("src.world.PikachuFollower").talk(Game, self, npc)
|
||||
elseif not npc.moving then
|
||||
self:talkTo(npc)
|
||||
-- world.talk: the A press on an object, before the map's text tables
|
||||
-- get it. A runtime object a mod spawned (WorldAPI:spawnNpc) carries
|
||||
-- no TEXT_* id, so the vanilla path has nothing to say for it; a mod
|
||||
-- that owns the object wraps this and simply does not call next().
|
||||
-- Everything else falls straight through to talkTo as before.
|
||||
Runtime.call("world.talk", vanillaTalk, self, npc)
|
||||
end
|
||||
interacted(self, fx, fy, "npc", npc)
|
||||
return
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
-- quiet no-op, never a crash. Reaching into OverworldState internals
|
||||
-- stays unsupported; anything a mod legitimately needs belongs here.
|
||||
|
||||
local Collision = require("src.world.Collision")
|
||||
local Logger = require("src.core.Logger")
|
||||
local FieldDefaults = require("src.world.FieldDefaults")
|
||||
local Map = require("src.world.Map")
|
||||
@@ -117,6 +118,47 @@ function WorldAPI:current()
|
||||
facing = p and p.facing }
|
||||
end
|
||||
|
||||
local function validBlockCoordinate(value)
|
||||
return type(value) == "number" and value == value
|
||||
and value ~= math.huge and value ~= -math.huge
|
||||
and value == math.floor(value)
|
||||
end
|
||||
|
||||
-- Read one block from the active Gen 1 map without exposing the mutable block
|
||||
-- array. Requiring the expected map id makes a stale signature fail closed
|
||||
-- if a warp or reload moved the player before the caller completed its check.
|
||||
-- Block coordinates are zero-based, matching replaceBlock.
|
||||
function WorldAPI:activeBlockAt(mapId, bx, by)
|
||||
local ow = self:overworld()
|
||||
if not ow or not ow.map then return nil, NO_OVERWORLD end
|
||||
local map = ow.map
|
||||
if map.id ~= mapId then return nil, "map is not active" end
|
||||
if not validBlockCoordinate(bx) or not validBlockCoordinate(by) then
|
||||
return nil, "invalid block coordinates"
|
||||
end
|
||||
local def = map.def
|
||||
if not def or not validBlockCoordinate(def.width) or def.width <= 0
|
||||
or not validBlockCoordinate(def.height) or def.height <= 0 then
|
||||
return nil, "block unavailable"
|
||||
end
|
||||
if bx < 0 or by < 0 or bx >= def.width or by >= def.height then
|
||||
return nil, "block coordinates out of bounds"
|
||||
end
|
||||
if type(def.blocks) ~= "table" or type(map.blockAt) ~= "function" then
|
||||
return nil, "block unavailable"
|
||||
end
|
||||
local stored = def.blocks[by * def.width + bx + 1]
|
||||
if not validBlockCoordinate(stored) or stored < 0 then
|
||||
return nil, "block unavailable"
|
||||
end
|
||||
local ok, blockId = pcall(map.blockAt, map, bx, by)
|
||||
if not ok or not validBlockCoordinate(blockId) or blockId < 0
|
||||
or blockId ~= stored then
|
||||
return nil, "block unavailable"
|
||||
end
|
||||
return blockId
|
||||
end
|
||||
|
||||
-- Companion UIs may offer party ordering while the player is in free roam.
|
||||
-- The same guard that makes opening a menu safe keeps scripts, transitions,
|
||||
-- movement and screens above the overworld from observing a mid-action swap.
|
||||
@@ -407,6 +449,72 @@ function Handle:position()
|
||||
return self.npc.cellX, self.npc.cellY
|
||||
end
|
||||
|
||||
-- Walk one tile starting NOW, outside the scripted-movement queue.
|
||||
--
|
||||
-- scriptMove queues onto OverworldState.scriptMoves, and a non-empty
|
||||
-- scriptMoves is how the overworld knows a cutscene is running -- it gates
|
||||
-- handleInput (OverworldController "local scripted = ... #self.scriptMoves >
|
||||
-- 0"), so an actor animated that way freezes the player's controls for as
|
||||
-- long as it walks. That is right for Oak marching to his lab and wrong for
|
||||
-- an actor that moves on its own schedule: a networked player's ghost, an
|
||||
-- ambient walker. This is the same per-tile state scriptMove sets, minus
|
||||
-- the queue and therefore minus the lockout.
|
||||
--
|
||||
-- Collision is deliberately not checked. The caller is replaying a move
|
||||
-- that was already decided somewhere else (validated on the peer's machine,
|
||||
-- or authored), and re-judging it here would let the two copies disagree
|
||||
-- about where the actor is. Use canStep first if you want the check.
|
||||
function Handle:stepNow(dir)
|
||||
local npc = self.npc
|
||||
if not Collision.DELTA[dir] then return nil, "bad direction: " .. tostring(dir) end
|
||||
if npc.moving then return nil, "already moving" end
|
||||
npc.facing = dir
|
||||
npc.targetX, npc.targetY = Collision.target(npc.cellX, npc.cellY, dir)
|
||||
npc.moving = true
|
||||
npc.progress = 0
|
||||
return true
|
||||
end
|
||||
|
||||
-- Would stepNow land somewhere legal? Exposed separately so a caller that
|
||||
-- does want the map's opinion can ask for it without giving up the "replay
|
||||
-- verbatim" default above.
|
||||
function Handle:canStep(dir)
|
||||
local ow = self.ow
|
||||
if not (ow and ow.map) then return false end
|
||||
return Collision.canMove(ow.map, ow.entities, self.npc, dir) and true or false
|
||||
end
|
||||
|
||||
-- Snap to a cell with no animation: a warp arrival, or a resync that has
|
||||
-- drifted too far to walk off. Clears any step in flight so the entity
|
||||
-- cannot land on its old target a frame later.
|
||||
function Handle:placeAt(x, y, facing)
|
||||
local npc = self.npc
|
||||
npc.moving = false
|
||||
npc.marching = false
|
||||
npc.targetX, npc.targetY = nil, nil
|
||||
npc.progress = 0
|
||||
npc.cellX, npc.cellY = x, y
|
||||
npc.px, npc.py = x * 16, y * 16
|
||||
if facing then npc.facing = facing end
|
||||
return true
|
||||
end
|
||||
|
||||
-- True while a step is still animating, so a driver can pace itself rather
|
||||
-- than stomping a move in flight.
|
||||
function Handle:isMoving()
|
||||
return self.npc.moving and true or false
|
||||
end
|
||||
|
||||
-- Whether the player may walk through this object (Collision.occupied skips
|
||||
-- passable entities -- Yellow's companion Pikachu is the engine's own user
|
||||
-- of the flag). It still draws and can still be talked to; it just stops
|
||||
-- being an obstacle, which is what a dynamic actor wants when standing in a
|
||||
-- doorway would otherwise wall someone in.
|
||||
function Handle:setPassable(passable)
|
||||
self.npc.passable = passable and true or false
|
||||
return true
|
||||
end
|
||||
|
||||
function WorldAPI:npc(mapId, indexOrName)
|
||||
local ow = self:overworld()
|
||||
if not ow then return nil, NO_OVERWORLD end
|
||||
|
||||
@@ -5470,6 +5470,37 @@ function World:dropMapImages(mapId)
|
||||
if self.connectionMaps then self.connectionMaps[mapId] = nil end
|
||||
end
|
||||
|
||||
local function safeRelease(obj)
|
||||
if obj and obj ~= false and obj.release then pcall(obj.release, obj) end
|
||||
end
|
||||
|
||||
-- Eagerly free session-owned GPU caches. Assets.image-backed atlases are
|
||||
-- nilled without release; unique bakes, strips, and roof composites are released.
|
||||
function World:release()
|
||||
if self.mapImages then
|
||||
for _, img in pairs(self.mapImages) do safeRelease(img) end
|
||||
self.mapImages = {}
|
||||
end
|
||||
if self.scrollStrips then
|
||||
for _, strip in pairs(self.scrollStrips) do safeRelease(strip) end
|
||||
self.scrollStrips = {}
|
||||
end
|
||||
safeRelease(self.tiltCanvas)
|
||||
self.tiltCanvas = nil
|
||||
if self.grassAtlases then
|
||||
for _, atlas in pairs(self.grassAtlases) do safeRelease(atlas) end
|
||||
self.grassAtlases = {}
|
||||
end
|
||||
if self.atlasCache then
|
||||
for key, atlas in pairs(self.atlasCache) do
|
||||
if key:find("|", 1, true) then safeRelease(atlas) end
|
||||
end
|
||||
self.atlasCache = {}
|
||||
end
|
||||
self.animQuads = nil
|
||||
self.connectionMaps = nil
|
||||
end
|
||||
|
||||
-- LoadMapAttributes' refill, for every map the session has edited. Neighbour
|
||||
-- strips share the same buffer on the cart, so a connection crossing reloads
|
||||
-- them too: this runs on any setMap, seamless or not.
|
||||
|
||||
Reference in New Issue
Block a user