Files
gen1recomp/tests/engine/game3_naming_update_contract_test.lua
T
Shane McGovern 2c356f28ea fix(game3): save, item, map, script and importer bugs found in review
The rest of the Gen 3 review fixes, each with a gated suite in tests/engine/.
ROM semantics were checked against pret/pokefirered.

Scripts
- givemon carried the wrong operand layout, found earlier; four more layout
  desyncs came out of pret asm/macros/event.inc: comparestat is {byte,word},
  and setptr / loadbytefromptr / setptrbyte each carry a leading byte plus a
  word. A wrong size mis-decodes every instruction after the bad one, so
  Versions.CACHE_VERSION moves to 113 and existing caches re-import.
- handlers for previously handler-less verbs: comparestat,
  bufferitemnameplural, setmonmove, setmonmetlocation, the modern
  fateful-encounter pair, the script-locals family (copylocal, setptr,
  loadbytefromptr, setptrbyte, copybyte, compare_local_to_* and
  compare_ptr_to_*), the RAM-script family (setvaddress, vgoto, vcall,
  vgoto_if, vcall_if, vmessage, vbuffermessage, vbufferstring, endram,
  returnram) and the same-map forms of the *at verbs.
- setdooropen / setdoorclosed read their coordinates through VarGet.

Battles
- Knock Off and Thief / Trick persist the item change instead of only
  touching the in-battle copy.
- knocked-off party slots are tracked in a bitmask, so a slot reused later
  does not inherit the flag.

Field and UI
- Player.reset restores facing and clears the surf flags.
- a definition-less Map.load no longer leaves collision unbound.
- an unresolved region-map section no longer reports PALLET TOWN.
- the naming screen splits input from the timer, so update(dt) stops
  indexing a number.
- the hall of fame commits through the engine save path and serializes its
  fields.

Persistence
- gameStats, the link-battle records and the trainer card are serialized.
- the PC deposit refuses at the 999 cap instead of destroying the overflow.

Robustness
- Data.load runs cached modules sandboxed.
- the file browser quotes shell arguments.
- .meta dimensions are bounds-checked and mids.idx validates its header.
2026-09-22 03:28:39 +01:00

66 lines
2.4 KiB
Lua

-- The naming screen must follow the stack's update convention.
--
-- G4 regression: Hud.update ticks the stack top with `pcall(top.mod.update, dt)`,
-- but Naming's signature is `Naming.update(input, dt)` -- so the delta arrived as
-- `input` and `input.wasPressed` raised on a number, every frame the naming
-- screen was on top. Hud's pcall swallowed it, so the fault was invisible and
-- input never reached the screen through Hud (Runtime passed it correctly, which
-- is why only Hud's path was broken).
--
-- Convention (Hud.update_top_menu prefers it): menus expose handleInput(input)
-- and update(dt). This pins both halves.
-- luajit tests/engine/game3_naming_update_contract_test.lua
package.path = "./?.lua;./?/init.lua;" .. package.path
local T = require("tests.harness")
local check, eq = T.check, T.eq
love = love or require("tests.love_stub")
local Stack = require("src.ui.game3.stack")
local Naming = require("src.ui.game3.naming")
local Hud = require("src.ui.game3.hud")
local function open_naming()
local st = {
title = "YOUR NAME?", maxLen = 7, name = "", page = 1,
row = 1, col = 1, btn = 1, blink = 0, swapT = nil,
}
Naming._state = st
Naming.openFlag = true
Stack.push("naming", Naming, { hideBelow = true })
return st
end
local function input(pressed)
return {
wasPressed = function(_, k) return pressed[k] == true end,
isDown = function() return false end,
}
end
-- 1. update takes the delta alone (the arity Hud uses).
local st = open_naming()
local ok, err = pcall(Naming.update, 1 / 60)
check(ok, "Naming.update(dt) is callable with just a delta (" .. tostring(err) .. ")")
eq(st.blink, 1 / 60, "update advances the blink timer")
-- 2. Input reaches the screen through Hud's stack tick.
local st2 = open_naming()
st2.row = 1
Hud.update({ input = input({ down = true }) }, 1 / 60)
eq(st2.row, 2, "a 'down' press reaches naming through Hud.update")
-- 3. handleInput is the input entry point and accepts the frame input.
local st3 = open_naming()
check(pcall(Naming.handleInput, input({})), "Naming.handleInput accepts an input object")
-- 4. The swap-animation guard still suppresses input (the original early return).
local st4 = open_naming()
st4.row = 1
st4.swapT = 0
pcall(Naming.handleInput, input({ down = true }))
eq(st4.row, 1, "input is ignored while the page swap is running")
T.finish("game3_naming_update_contract_test")