mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
253 lines
9.2 KiB
Lua
253 lines
9.2 KiB
Lua
-- The PACK's TM/HM rows (#1695), the list cursor's colour (#1694) and the rows
|
|
-- a message prints on in the description box (#1725, #1957).
|
|
-- engine/items/tmhm.asm:355-403 and engine/gfx/cgb_layouts.asm:723-726 (pokegold).
|
|
--
|
|
-- POKEPORT_IDENTITY=gold-dev POKEPORT_GAME=gold POKEPORT_TOUCH=0 \
|
|
-- POKEPORT_DRIVER=tests/drivers/gold_pack_rows_bug1695_test.lua \
|
|
-- POKEPORT_SHOT_DIR=/tmp/gold-pack-rows love .
|
|
--
|
|
-- No POKEPORT_SPEED: it scales the logic clock only, so every shot here would
|
|
-- race the keypress that redrew the list.
|
|
local U = require("tests.drivers.util")
|
|
|
|
local Bag = require("src.inventory.Bag")
|
|
local GbcPalette = require("src.render.GbcPalette")
|
|
local PackMenu = require("src.ui.gen2.PackMenu")
|
|
|
|
-- ../pokecrystal/maps/NewBarkTown.asm:287 -- the tile in front of the player's
|
|
-- own front door. An outdoor map is what makes the ESCAPE ROPE below print
|
|
-- OakThisIsntTheTimeText instead of warping the PACK shut.
|
|
local HOME = { map = "NEW_BARK_TOWN", x = 13, y = 6 }
|
|
|
|
-- One-digit and two-digit counts side by side, both HM ends of the pocket, and
|
|
-- the two key items whose submenus reach a SEL and a USE.
|
|
local SEED = {
|
|
{ "POTION", 5 },
|
|
{ "SUPER_POTION", 50 },
|
|
{ "ESCAPE_ROPE", 3 },
|
|
{ "POKE_BALL", 7 },
|
|
{ "GREAT_BALL", 12 },
|
|
{ "ITEMFINDER", 1 },
|
|
{ "BICYCLE", 1 },
|
|
{ "TM_DYNAMICPUNCH", 1 },
|
|
{ "TM_HEADBUTT", 3 },
|
|
{ "TM_THUNDER", 24 },
|
|
{ "HM_CUT", 1 },
|
|
{ "HM_SURF", 1 },
|
|
{ "HM_WATERFALL", 1 },
|
|
}
|
|
|
|
-- TM/HM pocket order is wTMsHMs order (engine/items/tmhm.asm:341), and each row
|
|
-- carries the number the cart prints at column 5 and the move name at column 8.
|
|
local TMHM = {
|
|
{ "TM_DYNAMICPUNCH", "01", "DYNAMICPUNCH" },
|
|
{ "TM_HEADBUTT", "02", "HEADBUTT" },
|
|
{ "TM_THUNDER", "25", "THUNDER" },
|
|
{ "HM_CUT", "H1", "CUT" },
|
|
{ "HM_SURF", "H3", "SURF" },
|
|
{ "HM_WATERFALL", "H7", "WATERFALL" },
|
|
}
|
|
|
|
return function(game)
|
|
local out = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/gold-pack-rows"
|
|
local failed = 0
|
|
|
|
local function pass(ok, line)
|
|
if not ok then failed = failed + 1 end
|
|
U.log((ok and "PASS " or "FAIL ") .. line)
|
|
end
|
|
|
|
local function shot(name)
|
|
U.wait(4)
|
|
U.shot(game, ("%s/%s.png"):format(out, name))
|
|
end
|
|
|
|
local function tap(button, frames)
|
|
U.tap(game, button)
|
|
U.wait(frames or 5)
|
|
end
|
|
|
|
U.wait(45)
|
|
assert(game.world and game.world.map, "gold world did not boot")
|
|
|
|
local world, save = game.world, game.save
|
|
|
|
-- A later map edit must not park the player against a wall: try the door
|
|
-- tile, then any free neighbour of it.
|
|
local placed = world:setMap(HOME.map, HOME.x, HOME.y, "down")
|
|
if not placed then
|
|
for _, step in ipairs({ { 0, -1 }, { 1, 0 }, { -1, 0 }, { 0, 1 } }) do
|
|
placed = world:setMap(HOME.map, HOME.x + step[1], HOME.y + step[2], "down")
|
|
if placed then break end
|
|
end
|
|
end
|
|
U.wait(12)
|
|
|
|
local mapId = world.map and world.map.id
|
|
local env = world.map and world.map.def and world.map.def.environment
|
|
pass(placed and env ~= "CAVE" and env ~= "DUNGEON",
|
|
("standing in %s (environment %s), where an ESCAPE ROPE has nowhere to " ..
|
|
"pay out to"):format(tostring(mapId), tostring(env)))
|
|
|
|
save.inventory = {}
|
|
save.bagOrder = {}
|
|
for _, entry in ipairs(SEED) do
|
|
local id, count = entry[1], entry[2]
|
|
if game.data.items and game.data.items[id] then
|
|
save.inventory[id] = count
|
|
table.insert(save.bagOrder, id)
|
|
else
|
|
pass(false, id .. " is not in this cache, so its row will be missing")
|
|
end
|
|
end
|
|
Bag.order(save, { items = game.data.items })
|
|
|
|
-- A probe pack for the checks, so the one on screen opens on a clean cursor.
|
|
local probe = PackMenu.new(game, { save = save, world = world,
|
|
pocket = "TM_HM" })
|
|
|
|
for i, want in ipairs(TMHM) do
|
|
local row = probe.rows[i]
|
|
pass(row and row.id == want[1] and row.tmhmLabel == want[2],
|
|
("row %d is %s and its number reads %q (got %s / %s)"):format(
|
|
i, want[1], want[2], tostring(row and row.id),
|
|
tostring(row and row.tmhmLabel)))
|
|
pass(row and row.teaches == want[3],
|
|
("row %d prints the move name %q rather than the item name %s"):format(
|
|
i, want[3], tostring(row and row.name)))
|
|
end
|
|
|
|
pass(probe.gfx and probe.gfx:available(),
|
|
"this cache has the PACK's own tiles, so the screen is the cart's chrome " ..
|
|
"and not the fallback boxes")
|
|
pass(GbcPalette.available(),
|
|
"the palette shader is loaded, so a coloured cursor is drawable at all")
|
|
pass(GbcPalette.mode == "gbc",
|
|
("colour mode is %q; anything else draws the cursor in DMG greys and " ..
|
|
"looks exactly like the bug"):format(tostring(GbcPalette.mode)))
|
|
|
|
local cursorPal = probe.gfx and probe.gfx:available()
|
|
and probe.gfx:colorsAt(7, 2)
|
|
local ink = cursorPal and cursorPal[4]
|
|
pass(ink and ink[1] == 255 and ink[2] == 0 and ink[3] == 0,
|
|
("the cursor column (7,2) resolves to a palette whose colour 3 is " ..
|
|
"255,0,0 (got %s)"):format(ink and table.concat(ink, ",") or "nil"))
|
|
local lastPal = probe.gfx and probe.gfx:available()
|
|
and probe.gfx:colorsAt(7, 10)
|
|
pass(lastPal == cursorPal,
|
|
"and the fifth list row's cursor cell (7,10) is in the same 1x9 zone")
|
|
|
|
local finder = game.data.items and game.data.items.ITEMFINDER
|
|
pass(finder and finder.canSelect == true and world.registerItem ~= nil,
|
|
"the ITEMFINDER is registerable, so SEL prints \"Registered the\" and " ..
|
|
"not the refusal")
|
|
local rope = game.data.items and game.data.items.ESCAPE_ROPE
|
|
pass(rope and rope.fieldMenu ~= "ITEMMENU_NOUSE",
|
|
"the ESCAPE ROPE's submenu carries a USE row to reach OAK's two-page " ..
|
|
"message with")
|
|
|
|
if failed > 0 then
|
|
U.log(("%d check(s) failed above, so the shots below cannot be read as " ..
|
|
"a pass."):format(failed))
|
|
end
|
|
|
|
game.packCursor = nil
|
|
local pack = PackMenu.new(game, { save = save, world = world,
|
|
onClose = function() game.stack:pop() end })
|
|
game.stack:push(pack)
|
|
|
|
U.log("00-items: the arrow beside POTION should be the same red as the")
|
|
U.log("pocket plaque on the left, not black. black in every pocket means")
|
|
U.log("the palette never reached it (#1694).")
|
|
shot("00-items")
|
|
|
|
tap("select")
|
|
tap("down")
|
|
U.log("01-items-select: row 1 now carries the hollow arrow and row 2 the")
|
|
U.log("solid one, both red. \"Where should this\" / \"be moved to?\" sit on")
|
|
U.log("rows 14 and 16 with a blank row between them (#1725).")
|
|
U.log("switching row is " .. tostring(pack.switching) ..
|
|
", cursor on " .. tostring(pack.index))
|
|
shot("01-items-select")
|
|
tap("b")
|
|
tap("up")
|
|
|
|
tap("right")
|
|
shot("02-balls")
|
|
tap("right")
|
|
shot("03-key-items")
|
|
U.log("02 and 03: same red arrow in POKe BALLS and KEY ITEMS.")
|
|
|
|
tap("right")
|
|
U.log("04-tmhm-top: the rows read \"01 DYNAMICPUNCH\", \"02 HEADBUTT\",")
|
|
U.log("\"25 THUNDER\", \"H1 CUT\", \"H3 SURF\". the number is hard against the")
|
|
U.log("left edge of the item area where the blue pattern column ends, the")
|
|
U.log("arrow one tile later, the move name from column 8. the word TM or HM")
|
|
U.log("printed anywhere, or a right-aligned number, is the bug (#1695).")
|
|
U.log("pocket is " .. tostring(pack:pocket().id) ..
|
|
" with " .. tostring(#pack.rows) .. " rows")
|
|
shot("04-tmhm-top")
|
|
|
|
for _ = 1, 5 do tap("down") end
|
|
U.log("05-tmhm-hms: scrolled to the last HM. \"H7 WATERFALL\" reads with a")
|
|
U.log("single digit and no count; \"H07\" would mean the TM branch took it.")
|
|
U.log("the three TM rows above it still carry their xNN on the line below.")
|
|
shot("05-tmhm-hms")
|
|
|
|
tap("left")
|
|
tap("left")
|
|
tap("left")
|
|
tap("a")
|
|
tap("down")
|
|
tap("down")
|
|
tap("a")
|
|
U.log("06-toss-how-many: \"Throw away how\" on row 14 and \"many?\" on row 16,")
|
|
U.log("with a full blank row between them and the first line clear of the")
|
|
U.log("box's top border. adjacent lines mean the message path is still")
|
|
U.log("single-spaced (#1725).")
|
|
U.log("message is " ..
|
|
(pack.message and table.concat(pack.message, " / ") or "nil"))
|
|
shot("06-toss-how-many")
|
|
|
|
tap("b")
|
|
tap("down")
|
|
tap("down")
|
|
tap("a")
|
|
tap("a")
|
|
U.log("07-oak-page1: OakThisIsntTheTimeText's first page. \"OAK: GOLD!\" on")
|
|
U.log("row 14 and \"This isn't the\" on row 16, the same pair every other")
|
|
U.log("message in this box uses. anything on row 13 or 15, or all three")
|
|
U.log("lines at once, is the bug (#1957).")
|
|
U.log("message is " ..
|
|
(pack.message and table.concat(pack.message, " / ") or "nil") ..
|
|
", page " .. tostring(pack.messagePage))
|
|
shot("07-oak-page1")
|
|
|
|
tap("a")
|
|
U.log("08-oak-page2: `cont` prompts then scrolls twice, so \"This isn't the\"")
|
|
U.log("moves up to row 14, \"time to use that!\" lands on row 16 and OAK's")
|
|
U.log("greeting is gone. the press dismissing the message outright is #1957.")
|
|
U.log("page is " .. tostring(pack.messagePage))
|
|
shot("08-oak-page2")
|
|
|
|
tap("b")
|
|
tap("right")
|
|
tap("right")
|
|
tap("a")
|
|
tap("down")
|
|
tap("a")
|
|
U.log("09-registered: the case the report filed. \"Registered the\" on row 14")
|
|
U.log("and \"ITEMFINDER.\" on row 16, lining up with the item description")
|
|
U.log("that shares this box. row 13 plus row 14 is the old behaviour; row 15")
|
|
U.log("plus a clipped row 17 means the offset went the other way.")
|
|
U.log("message is " ..
|
|
(pack.message and table.concat(pack.message, " / ") or "nil"))
|
|
shot("09-registered")
|
|
|
|
U.log("shots in " .. out .. "; the PACK is open on KEY ITEMS, controls yours")
|
|
|
|
while true do
|
|
coroutine.yield()
|
|
end
|
|
end
|