Files
gen1recomp/tests/drivers/gold_battle_bg_bug1709_test.lua
T
bryanthaboi 1905261c5b Spider Car Unleashed
CLOSES #1483, CLOSES #1610, CLOSES #1615, CLOSES #1646, CLOSES #1649, CLOSES #1651, CLOSES #1653, CLOSES #1656, CLOSES #1683, CLOSES #1685, CLOSES #1686, CLOSES #1687, CLOSES #1688, CLOSES #1689, CLOSES #1690, CLOSES #1693, CLOSES #1694, CLOSES #1695, CLOSES #1696, CLOSES #1702, CLOSES #1704, CLOSES #1705, CLOSES #1706, CLOSES #1707, CLOSES #1708, CLOSES #1710, CLOSES #1711, CLOSES #1712, CLOSES #1713, CLOSES #1716, CLOSES #1717, CLOSES #1718, CLOSES #1719, CLOSES #1720, CLOSES #1721, CLOSES #1725, CLOSES #1732, CLOSES #1745, CLOSES #1748, CLOSES #1749, CLOSES #1751, CLOSES #1754
2026-08-24 07:52:05 -04:00

279 lines
11 KiB
Lua

-- #1709: BATTLE BG on Gold, the WHITE / BLACK void around the battle screen,
-- and whether it survives a menu opened over the battle. The cart has no such
-- setting; maps/Route29.asm:432 is only where this parks the player.
--
-- POKEPORT_IDENTITY=gold-dev POKEPORT_GAME=gold POKEPORT_TOUCH=0 \
-- POKEPORT_DRIVER=tests/drivers/gold_battle_bg_bug1709_test.lua \
-- POKEPORT_SHOT_DIR=/tmp/gold-battle-bg love .
--
-- No POKEPORT_SPEED: the shots land a fixed number of frames after a
-- transition, and a logic clock running ahead of the render moves them.
local U = require("tests.drivers.util")
local Chrome = require("src.ui.gen2.Chrome")
local Mon = require("src.battle.gen2.Mon")
local OptionsMenu = require("src.ui.gen2.OptionsMenu")
local Permissions = require("src.world.gen2.Permissions")
local Save = require("src.core.gen2.Save")
return function(game)
local out = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/gold-battle-bg"
local failures = 0
local function ok(label, condition, detail)
if condition then
print("[battlebg] ok " .. label)
else
failures = failures + 1
print("[battlebg] FAIL " .. label .. " " .. tostring(detail))
end
end
local function tap(button, frames)
game.input.pressQueue[#game.input.pressQueue + 1] = button
game.input.state[button] = true
U.wait(2)
game.input.state[button] = false
U.wait(frames or 6)
end
local function shot(path)
if not U.shot(game, path) then failures = failures + 1 end
end
U.wait(45)
local world = game.world
assert(world and world.map, "gold world did not boot")
-- ---- what the eye cannot check -----------------------------------------
-- Each of these fails the same way the bug did: the void just stays white.
ok("battleBg has a default in the Gold options table",
Save.DEFAULT_OPTIONS.battleBg == "white", Save.DEFAULT_OPTIONS.battleBg)
local bgRow
for i, row in ipairs(OptionsMenu.ROWS) do
if row.label == "BATTLE BG" then bgRow = i end
end
ok("OPTION carries a BATTLE BG row", bgRow ~= nil, bgRow)
if bgRow then
ok("and it is the last row before CANCEL",
OptionsMenu.ROWS[bgRow + 1] and OptionsMenu.ROWS[bgRow + 1].cancel == true,
bgRow)
end
local battleModule = require("src.ui.gen2.BattleState")
ok("the Gold battle screen answers bgMode",
type(battleModule.bgMode) == "function", type(battleModule.bgMode))
ok("and Game2 owns the repaint, not the battle screen",
type(game.paintBattleSurround) == "function",
type(game.paintBattleSurround))
-- At the letterbox size there is no void, so nothing below would show.
if love.window and love.window.setMode then
love.window.setMode(1280, 840, { resizable = true })
U.wait(6)
end
local winW, winH = love.graphics.getDimensions()
local scale = Chrome.fitScale(winW, winH)
local ox, oy = Chrome.fitOrigin(winW, winH, scale)
ok(("the window leaves a void to look at (%dx%d, panel at %d,%d x%d)")
:format(winW, winH, ox, oy, scale), ox > 8 and oy > 8, ox .. "," .. oy)
local player = Mon.new(game.data, "CYNDAQUIL", 12)
local wild = Mon.new(game.data, "PIDGEY", 4)
ok("CYNDAQUIL builds from the extracted tables",
player ~= nil and #player.moves > 0, player and #player.moves)
ok("and so does the wild PIDGEY", wild ~= nil and #wild.moves > 0,
wild and #wild.moves)
game.save.party = { player }
game.save.inventory = { POKE_BALL = 5, POTION = 3 }
-- maps/Route29.asm:432, the teacher's WALK_LEFT_RIGHT lane, so (15,11) and
-- its neighbours are floor; a map edit that moves it falls back below.
assert(world:setMap("ROUTE_29", 15, 11, "down"), "setMap ROUTE_29 failed")
U.wait(8)
if not Permissions.isWalkable(world:playerCollision()) then
for _, step in ipairs({ { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 },
{ 2, 0 }, { -2, 0 } }) do
if world:setMap("ROUTE_29", 15 + step[1], 11 + step[2], "down")
and Permissions.isWalkable(world:playerCollision()) then
break
end
end
U.wait(8)
end
ok("the player is standing on floor, not in a wall",
Permissions.isWalkable(world:playerCollision()),
tostring(world:playerCollision()))
print(failures == 0
and "[battlebg] preflight PASS -- the shots below are worth looking at"
or ("[battlebg] preflight FAIL (%d) -- fix these before judging a pixel")
:format(failures))
-- ---- the run -----------------------------------------------------------
-- Start from WHITE whatever the gold-dev identity was left on, so the first
-- half of the run is the same every time.
game.options.battleBg = "white"
shot(out .. "/00-route29.png")
-- OPTION, walked to off the START menu the way a player gets there.
tap("start")
local startMenu = game.stack:top()
ok("START opened the menu", startMenu ~= nil and startMenu.items ~= nil,
startMenu)
if startMenu and startMenu.list then
for _ = 1, #startMenu.items do
local item = startMenu.items[startMenu.list.index]
if item and item.value == "option" then break end
tap("down", 3)
end
local landed = startMenu.items[startMenu.list.index]
ok("the cursor found OPTION", landed and landed.value == "option",
landed and landed.value)
end
tap("a", 10)
local options = game.stack:top()
ok("the OPTION screen is up", options ~= nil and options.rows ~= nil, options)
if options then
-- UP from the first row wraps onto CANCEL, so BATTLE BG is two presses
-- away however many rows the build has.
for _ = 1, #options.rows do
if options:row() and options:row().key == "battleBg" then break end
tap("up", 3)
end
local row = options:row()
ok("the cursor is on BATTLE BG", row and row.key == "battleBg",
row and row.label)
U.log("01: the OPTION screen, cursor on BATTLE BG. it should read WHITE,")
U.log("sitting under MAX FPS and above CANCEL.")
shot(out .. "/01-option-white.png")
tap("right", 6)
ok("right stored black", game.options.battleBg == "black",
game.options.battleBg)
U.log("02: same row after one press right. it should read BLACK, and no")
U.log("other row on screen should have changed.")
shot(out .. "/02-option-black.png")
end
-- Gold plays with an empty stack: the world is not a state, so "back on the
-- map" is nothing on top rather than the overworld being on top.
tap("b", 10)
for _ = 1, 20 do
if game.stack:top() == nil then break end
tap("b", 4)
end
ok("the menus closed", game.stack:top() == nil, game.stack:top())
U.log("03: Route 29 with BATTLE BG on BLACK. the overworld is not a battle,")
U.log("so there must be NO black bars here -- the surround around the map is")
U.log("whatever VOID FILL draws, exactly as it was before.")
shot(out .. "/03-route29-black-set.png")
-- ---- the battle --------------------------------------------------------
assert(world:startBattle({ wild = wild }), "startBattle failed")
local battle
for _ = 1, 900 do
local top = game.stack:top()
if top and top.battle then battle = top break end
U.wait(1)
end
ok("the battle screen came up after the transition", battle ~= nil, battle)
if not battle then
print(("[battlebg] FAIL no battle to shoot (%d)"):format(failures))
while true do coroutine.yield() end
end
for _ = 1, 150 do
if battle.phase == "menu" then break end
tap("a", 2)
end
ok("the battle reached the FIGHT menu", battle.phase == "menu", battle.phase)
ok("and it reports the black surround",
battle:bgMode() == "black", battle:bgMode())
U.wait(10)
U.log("04: the battle on BLACK. the void on all four sides of the GB screen")
U.log("is solid black; the battle's own field, HUD and message box stay")
U.log("white. black creeping over the panel edge -- a dark frame eating a")
U.log("row of the HUD or the box border -- is the band maths being wrong.")
shot(out .. "/04-battle-black.png")
-- The 2x2 is FIGHT, <PK><MN> / PACK, RUN. Clamp onto FIGHT first and step
-- from there: one cell off is RUN, which ends the battle instead of a menu.
local function pointAt(index)
tap("left", 3)
tap("up", 3)
if index == 2 or index == 4 then tap("right", 3) end
if index == 3 or index == 4 then tap("down", 3) end
return battle.menuIndex == index
end
ok("the cursor is on PKMN", pointAt(2), battle.menuIndex)
tap("a", 12)
ok("the party list opened over the battle", battle.phase == "submenu",
battle.phase)
U.log("05: the party list over the same battle. this is the one that used to")
U.log("go wrong: the surround must still be black. white returning the")
U.log("instant the list goes up means the repaint is on the wrong layer.")
shot(out .. "/05-party-over-black.png")
tap("b", 12)
ok("the cursor is on PACK", pointAt(3), battle.menuIndex)
tap("a", 14)
U.log("06: the PACK over the battle, same rule -- still black behind it.")
shot(out .. "/06-pack-over-black.png")
tap("b", 12)
for _ = 1, 20 do
if battle.phase == "menu" then break end
tap("b", 4)
end
-- SCREEN POS moves the panel off centre, which is the other way the bands
-- can stop lining up with where the panel actually landed.
local positions = {
{ 7, "upper", "the panel sits high, so the black above it is thin and",
"the band below is deep. both stop dead at the panel edge." },
{ 8, "top", "the panel is flush with the top, so there is no black",
"above it at all. a band up there means the lift was ignored." },
}
for _, pos in ipairs(positions) do
game.options.screenPos = pos[2]
game:applyOptions()
U.wait(8)
local px, py = Chrome.fitOrigin(love.graphics.getDimensions())
U.log(("0%d: SCREEN POS %s, panel at %d,%d."):format(
pos[1], pos[2], px, py))
U.log(pos[3])
U.log(pos[4])
shot(out .. ("/0%d-screenpos-%s.png"):format(pos[1], pos[2]))
end
game.options.screenPos = "center"
game:applyOptions()
U.wait(8)
-- The no-regression half: WHITE has to look exactly like it always did.
game.options.battleBg = "white"
U.wait(6)
ok("and back to white", battle:bgMode() == "white", battle:bgMode())
U.log("09: the same battle flipped back to WHITE. paper white all the way to")
U.log("the window edge, no seam where the panel ends -- compare it with 04.")
shot(out .. "/09-battle-white.png")
ok("back on PKMN for the white pair", pointAt(2), battle.menuIndex)
tap("a", 12)
U.log("10: the party list over the white battle, for the same comparison.")
shot(out .. "/10-party-over-white.png")
tap("b", 12)
game.options.battleBg = "black"
U.wait(6)
print(failures == 0 and "[battlebg] PASS gold_battle_bg_bug1709"
or ("[battlebg] FAIL gold_battle_bg_bug1709 (%d)"):format(failures))
U.log("the battle is still up on BLACK and the controls are yours. leaving")
U.log("the OPTION screen wrote BLACK into the gold-dev options.lua, so the")
U.log("next boot starts there; set it back from OPTION if you want white.")
while true do coroutine.yield() end
end