mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 08:21:02 +02:00
feat(mods): expose safe overworld party reordering
This commit is contained in:
@@ -45,6 +45,14 @@ optional visual `tileRows` at 2x resolution, and optional `tileDetailRows` at
|
||||
`warp`, visible `item`, and untaken `hidden` locations. All fields are
|
||||
read-only snapshots; mods choose which layers to render.
|
||||
|
||||
## Party ordering
|
||||
|
||||
Companion UIs and alternate party screens can call
|
||||
`mod.world:canReorderParty()` before offering a reorder action, then
|
||||
`mod.world:reorderParty(fromSlot, toSlot)` with one-based party slots. The
|
||||
operation is accepted only during idle overworld play; menus, movement,
|
||||
scripts, battles, and transitions leave the party untouched.
|
||||
|
||||
## Rendering pipelines
|
||||
|
||||
Most registries hand the engine *content*. `render_pipelines` hands it
|
||||
|
||||
@@ -69,6 +69,22 @@ local function mapTileRows(map)
|
||||
return rows, detailRows
|
||||
end
|
||||
|
||||
local function acceptsMenuInput(game, ow)
|
||||
local stack = game and game.stack
|
||||
local runner = ow and ow.runner
|
||||
return ow and stack and stack.top and stack:top() == ow
|
||||
and not ow.transitioning and not ow.flyAnim and not ow.teleportOut
|
||||
and not ow.engaging and not ow.emote and not ow.pikaHop and not ow.healAnim
|
||||
and not (ow.player and (ow.player.moving or ow.player.inputLocked))
|
||||
and not (runner and runner.isRunning and runner:isRunning())
|
||||
and #(ow.scriptMoves or {}) == 0
|
||||
end
|
||||
|
||||
local function validPartySlot(party, slot)
|
||||
return type(slot) == "number" and slot == math.floor(slot)
|
||||
and party[slot] ~= nil
|
||||
end
|
||||
|
||||
function WorldAPI.new(game, modId)
|
||||
return setmetatable({ game = game, modId = modId }, WorldAPI)
|
||||
end
|
||||
@@ -98,6 +114,31 @@ function WorldAPI:current()
|
||||
facing = p and p.facing }
|
||||
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.
|
||||
function WorldAPI:canReorderParty()
|
||||
local game, ow = self.game, self:overworld()
|
||||
local party = game and game.save and game.save.party or {}
|
||||
return #party > 1 and not not acceptsMenuInput(game, ow)
|
||||
end
|
||||
|
||||
function WorldAPI:reorderParty(fromSlot, toSlot)
|
||||
local game, ow = self.game, self:overworld()
|
||||
if not ow then return nil, NO_OVERWORLD end
|
||||
if not acceptsMenuInput(game, ow) then return nil, "world is busy" end
|
||||
local party = game.save and game.save.party or {}
|
||||
if not validPartySlot(party, fromSlot)
|
||||
or not validPartySlot(party, toSlot) then
|
||||
return nil, "invalid party slot"
|
||||
end
|
||||
if fromSlot ~= toSlot then
|
||||
party[fromSlot], party[toSlot] = party[toSlot], party[fromSlot]
|
||||
require("src.core.Sound").play(game.data, "Swap")
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- A compact, read-only view of the active map for minimaps and companion UIs.
|
||||
-- `rows` describes collision terrain; optional `tileRows` reduces each real
|
||||
-- 8x8 map tile to its average Game Boy shade ("0" lightest, "3" darkest).
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
-- Public party-ordering contract over an idle overworld fixture. No ROM data
|
||||
-- is needed, so companion UIs exercise this seam in the normal mod-SDK tier.
|
||||
|
||||
package.path = "./?.lua;./?/init.lua;" .. package.path
|
||||
love = love or require("tests.love_stub")
|
||||
|
||||
local T = require("tests.harness").suite("mod world party reorder")
|
||||
local StateStack = require("src.core.StateStack")
|
||||
local WorldAPI = require("src.world.WorldAPI")
|
||||
|
||||
local first = { species = "BULBASAUR" }
|
||||
local second = { species = "CHARMANDER" }
|
||||
local runner = { running = false }
|
||||
function runner:isRunning() return self.running end
|
||||
|
||||
local ow = {
|
||||
isOverworld = true,
|
||||
map = { id = "PALLET_TOWN" },
|
||||
player = { moving = false, inputLocked = false },
|
||||
runner = runner,
|
||||
scriptMoves = {},
|
||||
}
|
||||
local stack = setmetatable({ states = { ow } }, { __index = StateStack })
|
||||
local game = {
|
||||
data = {},
|
||||
save = { party = { first, second } },
|
||||
stack = stack,
|
||||
overworld = ow,
|
||||
}
|
||||
local api = WorldAPI.new(game, "fixture")
|
||||
|
||||
T.check(api:canReorderParty(), "idle free roam allows party reordering")
|
||||
|
||||
local Sound = require("src.core.Sound")
|
||||
local realPlay, played = Sound.play
|
||||
Sound.play = function(_, name) played = name end
|
||||
T.check(api:reorderParty(1, 2) == true, "valid slots reorder")
|
||||
Sound.play = realPlay
|
||||
T.check(game.save.party[1] == second and game.save.party[2] == first,
|
||||
"the live party is swapped")
|
||||
T.eq(played, "Swap", "the normal party swap sound is used")
|
||||
|
||||
local value, err = api:reorderParty(1.5, 2)
|
||||
T.check(value == nil and err == "invalid party slot",
|
||||
"non-integer slots are rejected")
|
||||
value, err = api:reorderParty("1", 2)
|
||||
T.check(value == nil and err == "invalid party slot",
|
||||
"string slots are rejected")
|
||||
|
||||
stack:push({ screenId = "SomeMenu" })
|
||||
T.check(not api:canReorderParty(), "a screen above the world blocks reordering")
|
||||
value, err = api:reorderParty(1, 2)
|
||||
T.check(value == nil and err == "world is busy",
|
||||
"reordering refuses while another screen owns input")
|
||||
stack:pop()
|
||||
|
||||
ow.player.moving = true
|
||||
T.check(not api:canReorderParty(), "movement blocks reordering")
|
||||
ow.player.moving = false
|
||||
runner.running = true
|
||||
T.check(not api:canReorderParty(), "scripts block reordering")
|
||||
runner.running = false
|
||||
ow.transitioning = true
|
||||
T.check(not api:canReorderParty(), "map transitions block reordering")
|
||||
|
||||
T.finish()
|
||||
Reference in New Issue
Block a user