mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-25 23:11:15 +02:00
1905261c5b
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
279 lines
11 KiB
Lua
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
|