Files
gen1recomp/tests/drivers/gold_naming_and_trade_item.lua
bryanthaboi ae6cac89e1 G2 support
2026-08-11 11:53:30 -04:00

170 lines
6.8 KiB
Lua

-- Two things only a human can see, in one run.
--
-- POKEPORT_IDENTITY=gold-dev POKEPORT_GAME=gold \
-- POKEPORT_DRIVER=tests/drivers/gold_naming_and_trade_item.lua \
-- perl -e 'alarm 300; exec @ARGV' \
-- python3 -c "import pty; pty.spawn(['love','.'])"
--
-- Only the gold-dev identity has a complete Gold cache; another one is missing
-- data/generated/marts.lua, RomImporter never reports ready, and the driver
-- coroutine is simply never resumed (a silent hang with no output).
--
-- 1. The naming keyboard. Typing the LAST character does not close the screen:
-- `.a` is `call NamingScreen_TryAddCharacter / ret nc`, and
-- AdvanceCursor_CheckEndOfString answers CARRY once the buffer is full, so
-- the handler falls through into `.start` and parks the cursor on END with
-- the keyboard still up (engine/menus/naming_screen.asm:401-410). Only
-- `.end` stores the entry. The blank cells are typeable too: the NameInput*
-- rows are written into the tilemap and GetLastCharacter reads the tile
-- under the cursor back out, so the trailing spaces of "S T U V W X Y Z "
-- are real characters (data/text/name_input_chars.asm).
--
-- 2. Kyle's Onix (VioletKylesHouse, NPC_TRADE_KYLE) arrives holding
-- BITTER_BERRY. NPCTRADE_ITEM is an item id BYTE in the table
-- (data/events/npc_trades.asm:15) and DoNPCTrade copies it into
-- wPartyMon1Item; the port names it, so the summary's green page prints
-- BITTER BERRY and TAKE drops a real BITTER BERRY into the bag instead of
-- killing the game in Bag.isBadge.
--
-- Screenshots land in POKEPORT_SHOT_DIR (default /tmp/gold-naming-trade):
-- keyboard-parked-on-end.png full buffer, cursor bracketing END
-- keyboard-on-blank-cell.png the cursor sitting on the blank after Z
-- keyboard-typed-a-space.png a name with a space in the middle of it
-- onix-summary-item.png the green page's ITEM field
-- onix-item-taken.png "TOOK BITTER BERRY from ROCKY."
local U = require("tests.drivers.util")
local Mon = require("src.battle.gen2.Mon")
local NpcTrade = require("src.core.gen2.NpcTrade")
local Screens = require("src.ui.Screens")
local SummaryMenu = require("src.ui.gen2.SummaryMenu")
return function(game)
local out = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/gold-naming-trade"
local fails = 0
local function ok(cond, msg)
if cond then print("[naming] ok " .. msg)
else fails = fails + 1 print("[naming] FAIL " .. msg) end
return cond
end
local function tap(btn) U.tap(game, btn) U.wait(3) end
local function top() return game.stack:top() end
U.wait(45)
ok(game.world and game.world.map, "gold world booted")
-- ---------------------------------------------------------- the keyboard
local typed
Screens.push(game, "Gen2NamingScreen", {
type = "rival",
menuGfx = game.data.gen2MenuGfx,
-- The pop is the caller's, exactly as World:nameRival does it.
onDone = function(name) game.stack:pop() typed = name end,
})
U.wait(10)
local keyboard = top()
if not ok(keyboard and keyboard.text == "", "the rival keyboard opened") then
error("gold naming: no keyboard, cannot continue")
end
-- Seven A presses on the A key fill a 7-character rival name.
for _ = 1, keyboard.maxLength do tap("a") end
ok(top() == keyboard, "a full buffer leaves the keyboard up")
ok(typed == nil, "and hands nothing back yet")
ok(keyboard:cursorCharacter() == "END", "the cursor parked itself on END")
U.shot(game, out .. "/keyboard-parked-on-end.png")
-- The blank cell after Z types a space. Back off two characters so the
-- buffer has room for a space AND a letter after it (a name with a gap in
-- the middle is the only way to see the space at all), then walk up out of
-- the bottom row to row 2, column 8.
tap("b")
tap("b")
tap("up")
tap("up")
tap("right")
tap("right")
ok(keyboard:cursorCharacter() == " ", "the cell after Z is a space")
U.shot(game, out .. "/keyboard-on-blank-cell.png")
tap("a")
ok(keyboard.text:sub(-1) == " ", "and A types it into the name")
-- Up twice from (8,2) is the I key, so the field ends up reading "AAAAA I".
tap("up")
tap("up")
tap("a")
ok(keyboard.text == "AAAAA I", "the space really is in the stored name")
U.shot(game, out .. "/keyboard-typed-a-space.png")
-- Only A on END ends entry.
tap("start")
tap("a")
ok(typed ~= nil and #typed == keyboard.maxLength,
"A on END is what stores the entry")
U.wait(5)
-- ------------------------------------------------------- Kyle's Onix
-- data/generated/events.lua, which World loads as its own eventTables.
local row = NpcTrade.row(game.world.eventTables, 1)
if not ok(row and row.get == "ONIX", "the cache carries NPC_TRADE_KYLE") then
error("gold naming: no trade row, cannot continue")
end
local save = game.save
save.party = { Mon.new(game.data, "BELLSPROUT", 12) }
local _, onix = NpcTrade.perform(game.data, save, row, 1)
ok(onix and onix.nickname == "ROCKY", "the trade handed ROCKY over")
ok(onix and onix.item == "BITTER_BERRY",
"wearing a NAMED BITTER_BERRY, not the table's byte 83")
-- START > POKeMON > A > STATS, then the green page.
tap("start")
local menu = top()
for _ = 1, 10 do
if menu.list:current().value == "pokemon" then break end
tap("down")
end
tap("a")
local party = top()
if not ok(party and party.screenId == "Gen2PartyMenu",
"the party list opened") then
error("gold naming: no party list, cannot continue")
end
tap("a")
tap("a") -- STATS leads the submenu
local summary = top()
ok(summary and summary.screenId == "Gen2SummaryMenu", "STATS opened STATS")
tap("right") -- page 1 (pink) -> page 2 (green), which is the ITEM page
U.wait(5)
local placed = summary and summary:placements()
local text = {}
for _, p in ipairs(placed or {}) do text[#text + 1] = tostring(p.text or "") end
ok(table.concat(text, "|"):find("BITTER BERRY", 1, true) ~= nil,
"the green page prints BITTER BERRY, not 83")
U.shot(game, out .. "/onix-summary-item.png")
tap("b")
-- ITEM > TAKE. This is the press that used to crash in Bag.isBadge.
tap("a")
local submenu = party and party.submenu
for _ = 1, 8 do
if submenu and submenu.items[submenu.index]
and submenu.items[submenu.index].id == "ITEM" then break end
tap("down")
submenu = party and party.submenu
end
ok(submenu and submenu.items[submenu.index]
and submenu.items[submenu.index].id == "ITEM", "the cursor found ITEM")
tap("a")
local held = top()
ok(held and held.screenId == "Gen2HeldItemMenu", "GIVE / TAKE opened")
tap("down")
tap("a")
U.wait(5)
ok(save.party[1].item == nil, "TAKE pulled the berry off")
ok((save.inventory or {}).BITTER_BERRY == 1, "and it landed in the bag")
U.shot(game, out .. "/onix-item-taken.png")
print(("[naming] %d failure(s)"):format(fails))
love.event.quit()
end