mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 08:21:02 +02:00
191 lines
8.1 KiB
Lua
191 lines
8.1 KiB
Lua
-- Parity test: the party-menu field moves print their text with the party
|
|
-- menu still on screen, and the GBPalWhiteOutWithDelay3 blink IS the menu
|
|
-- closing afterwards (engine/menus/start_sub_menus.asm .flash / .strength /
|
|
-- .surf, each PrintText -> GBPalWhiteOutWithDelay3 -> jp .goBackToMap,
|
|
-- whose RestoreScreenTilesAndReloadTilePatterns + CloseTextDisplay tear the
|
|
-- menu down only after the white-out). Popping the menu first left the
|
|
-- opaque WhiteFlash as the only state under the message, i.e. the white
|
|
-- screen in #385, and lit a dark cave under the FLASH text.
|
|
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 field move layering")
|
|
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 PartyMenu = require("src.ui.PartyMenu")
|
|
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()
|
|
|
|
-- tests/parity_bills_pc.lua swaps the OverworldController chunk's TextBox
|
|
-- upvalue for a stub and never puts it back, and run_tests.lua dofiles every
|
|
-- parity suite into one process: point it back at the real module so trySurf
|
|
-- pushes a real box here.
|
|
local function setUpvalue(fn, name, val)
|
|
local i = 1
|
|
while true do
|
|
local n = debug.getupvalue(fn, i)
|
|
if not n then return false end
|
|
if n == name then debug.setupvalue(fn, i, val); return true end
|
|
i = i + 1
|
|
end
|
|
end
|
|
setUpvalue(OW.trySurf, "TextBox", require("src.render.TextBox"))
|
|
|
|
local function frame(btns)
|
|
Input.pressed = {}
|
|
for _, b in ipairs(btns or {}) do Input.pressed[b] = true; Input.state[b] = true end
|
|
StateStack:update(1 / 60)
|
|
for _, b in ipairs(btns or {}) do Input.state[b] = false end
|
|
end
|
|
local function popAll() while Game.stack:top() do Game.stack:pop() end end
|
|
local function pushOW(mapId, x, y, facing)
|
|
popAll()
|
|
Game.stack:push(OW, mapId, x, y, facing)
|
|
return Game.stack:top()
|
|
end
|
|
local function mkMon(species, ...)
|
|
local m = Pokemon.new(Data, species, 20)
|
|
m.moves = {}
|
|
for _, id in ipairs({ ... }) do m.moves[#m.moves + 1] = { id = id, pp = 15 } end
|
|
return m
|
|
end
|
|
local function selectSubItem(pm, idx)
|
|
Game.stack:push(pm)
|
|
frame({ "a" })
|
|
for _ = 2, idx do frame({ "down" }) end
|
|
frame({ "a" })
|
|
end
|
|
|
|
local function isText(s) return s ~= nil and s.pages ~= nil end
|
|
-- Transition.whiteFlash keeps its remaining frame budget and draws a full
|
|
-- opaque rect; the overworld and the menu have no `frames`
|
|
local function isBlink(s)
|
|
return s ~= nil and s.pages == nil and s.isOpaque == true
|
|
and type(s.frames) == "number"
|
|
end
|
|
-- the state the compositor starts from: StateStack:draw walks up from the
|
|
-- highest opaque state, so this is literally what the player sees behind
|
|
-- the message
|
|
local function backdrop()
|
|
return Game.stack.states[Game.stack:visibleBase()]
|
|
end
|
|
-- dismiss exactly one box (an auto page advances on its own timer, a
|
|
-- prompt page on A), leaving whatever it pushed on top
|
|
local function drainOne()
|
|
local box = Game.stack:top()
|
|
local guard = 0
|
|
while Game.stack:top() == box and guard < 400 do
|
|
guard = guard + 1
|
|
frame({ "a" })
|
|
end
|
|
end
|
|
-- run frames until the stack is back on the map (the blink pops itself and
|
|
-- fires its onDone)
|
|
local function settle(ow)
|
|
local guard = 0
|
|
while Game.stack:top() ~= ow and guard < 240 do
|
|
guard = guard + 1
|
|
frame({})
|
|
end
|
|
end
|
|
|
|
check(PartyMenu.isOpaque == true, "PartyMenu is opaque, so it can be the backdrop")
|
|
|
|
-- =====================================================================
|
|
-- .strength: PrintStrengthText's two pages, then the blink
|
|
-- =====================================================================
|
|
Game.save.party = { mkMon("MACHOP", "STRENGTH") }
|
|
Game.save.inventory = { RAINBOWBADGE = true }
|
|
local ow = pushOW("SEAFOAM_ISLANDS_1F", 17, 10, "right")
|
|
local pmStr = PartyMenu.new(Game)
|
|
selectSubItem(pmStr, 1)
|
|
check(isText(Game.stack:top()), "STRENGTH opens _UsedStrengthText")
|
|
eq(backdrop(), pmStr, "the party menu is the backdrop of _UsedStrengthText")
|
|
drainOne()
|
|
check(isText(Game.stack:top()), "_CanMoveBouldersText follows")
|
|
eq(backdrop(), pmStr, "the party menu is still the backdrop of the second page")
|
|
drainOne()
|
|
check(isBlink(Game.stack:top()), "the blink comes after both pages")
|
|
eq(backdrop(), Game.stack:top(), "the blink is the top state, never under a message")
|
|
settle(ow)
|
|
eq(Game.stack:top(), ow, "STRENGTH ends on the map")
|
|
|
|
-- =====================================================================
|
|
-- .surf: ItemUseSurfboard's got-on text, then the blink that carries the
|
|
-- mount forward (#320 put the blink under the text, which is what left
|
|
-- the white rect as the backdrop)
|
|
-- =====================================================================
|
|
Game.save.party = { mkMon("SQUIRTLE", "SURF") }
|
|
Game.save.inventory = { SOULBADGE = true }
|
|
ow = pushOW("PALLET_TOWN", 4, 13, "down")
|
|
ow.player.surfing = false
|
|
local pmSurf = PartyMenu.new(Game)
|
|
selectSubItem(pmSurf, 1)
|
|
check(isText(Game.stack:top()), "SURF opens _SurfingGotOnText")
|
|
eq(backdrop(), pmSurf, "the party menu is the backdrop of _SurfingGotOnText")
|
|
drainOne()
|
|
check(isBlink(Game.stack:top()), "the blink follows the got-on text")
|
|
eq(ow.player.surfing, true, "the mount happens with the blink, not before the text")
|
|
settle(ow)
|
|
eq(Game.stack:top(), ow, "SURF ends on the map")
|
|
|
|
-- =====================================================================
|
|
-- .cannotStopSurfing: prints and still closes, same layering
|
|
-- =====================================================================
|
|
ow = pushOW("PALLET_TOWN", 4, 15, "down")
|
|
ow.player.surfing = true
|
|
local pmNoOff = PartyMenu.new(Game)
|
|
selectSubItem(pmNoOff, 1)
|
|
check(isText(Game.stack:top()), "a blocked dismount opens _SurfingNoPlaceToGetOffText")
|
|
eq(backdrop(), pmNoOff, "the party menu is the backdrop of the no-place message")
|
|
drainOne()
|
|
check(isBlink(Game.stack:top()), "the blink follows the no-place message")
|
|
settle(ow)
|
|
eq(Game.stack:top(), ow, "the blocked dismount ends on the map")
|
|
ow.player.surfing = false
|
|
|
|
-- =====================================================================
|
|
-- .flash: `xor a / ld [wMapPalOffset], a` is undone before PrintText in
|
|
-- the asm, but the map is not on screen then -- the party menu is -- so
|
|
-- the lit cave may only appear once the blink hands the screen back.
|
|
-- The lighting itself happens as the message closes the menu, ahead of
|
|
-- the blink like the asm: in ADVANCED that call rebakes every resident
|
|
-- map, and behind the blink's completion that cost was spent with a
|
|
-- blank white frame as the newest thing on screen (#610).
|
|
-- =====================================================================
|
|
Game.save.flashLit = false
|
|
Game.save.party = { mkMon("PIKACHU", "FLASH") }
|
|
Game.save.inventory = { BOULDERBADGE = true }
|
|
ow = pushOW("ROCK_TUNNEL_1F", 15, 4, "down")
|
|
eq(ow.dark, true, "ROCK_TUNNEL_1F loads dark before FLASH")
|
|
local pmFlash = PartyMenu.new(Game)
|
|
selectSubItem(pmFlash, 1)
|
|
check(isText(Game.stack:top()), "FLASH opens _FlashLightsAreaText")
|
|
eq(backdrop(), pmFlash, "the party menu is the backdrop of _FlashLightsAreaText")
|
|
eq(ow.dark, true, "the tunnel is still dark while the message is up")
|
|
drainOne()
|
|
check(isBlink(Game.stack:top()), "the blink follows the FLASH message")
|
|
-- lit before the blink is pushed, so no rebuild can run while the white
|
|
-- frame is the newest one presented (#610); the blink hides it either way
|
|
eq(ow.dark, false, "the tunnel is lit by the time the blink starts")
|
|
settle(ow)
|
|
eq(Game.stack:top(), ow, "FLASH ends on the map")
|
|
eq(ow.dark, false, "the tunnel is lit once the blink hands the map back")
|
|
eq(Game.save.flashLit, true, "FLASH is recorded on the save")
|
|
|
|
popAll()
|
|
S.finish()
|