Files
gen1recomp/tests/gen2_evolution_anim_test.lua
T
thibautbus f07ebfe423 Route remaining Gen2 UI text through translations
CELADON_TM/MON and GOLDENROD_TM/MON stored each Game Corner row as one
hand-padded "NAME    COST" literal; a translated name of a different length
than the English original shifted or overlapped the price that used to be
right-aligned by the padding alone. Split each row into a translatable name
and the existing numeric cost field, print them as two separate calls (the
cost right-aligned against a fixed priceRight column per counter, the same
column-aware approach MartMenu.printPriceOpaque already uses), and clamp the
name to the tile budget before the price column with Font.split (glyph-aware,
so a <PK><MN> macro or multi-byte UTF-8 character -- including one whose
expansion straddles the clamp boundary -- is never cut mid-sequence).

TEXTS (SlotMachine.lua) kept a hand-typed English line array next to each
entry's Strings.source() template; derive the array from source once at load
instead, and cache localizedLines()'s parsed split per source table so the
bet/result screens do not re-run the same gmatch split every draw() call.

ContestMenu.TEXT.alreadyCaught's line split now uses the same gmatch loop
the rest of this file's line-parsing already uses (an anchored ^(.-)\n(.*)$
match assumed exactly one \n and left a nil hole when a translation merges
the two lines into one clause).

Four of PrizeMenu.TEXTS' Game Corner vendor messages (Celadon's and
Goldenrod's prize-vendor intros, Goldenrod's quit line, and the coin
vendor's no-COIN-CASE refusal) used \f where the real cart text
(poke-corpus GoldSilver, e.g. gs.CeladonGameCornerPrizeRoom.
CeladonPrizeRoom_PrizeVendorIntroText) ends in \v: a plain page clear
instead of a scroll, so the vendor's last line appeared alone with no
lead-in instead of continuing under the previous one. Found and confirmed
against the corpus while auditing this branch for the same class of bug as
CenterPcMenu.lua's \n-vs-\v fix.
2026-09-01 21:52:55 +02:00

111 lines
4.2 KiB
Lua

-- The evolution screen survives being pushed onto a stack.
--
-- luajit tests/gen2_evolution_anim_test.lua
--
-- `enter` is the stack's lifecycle hook: StateStack:push calls `state:enter(...)`
-- (src/core/StateStack.lua:18), and Gold runs that same stack
-- (src/core/Game2.lua:makeStack). EvolutionAnim used to define `enter(phase)`
-- as its own phase-transition method, so pushing it called that method with
-- no argument, set phase to nil,
-- and left update() falling through every branch -- decrementing its timer
-- forever while World:busy() stayed true. A post-battle evolution became an
-- unrecoverable hang; the Gold route bot found it the first time its starter
-- reached level 14, roughly two hours into a run.
--
-- The regression this pins is not the rename but the PROPERTY: after a push,
-- the screen still runs to completion.
package.path = "./?.lua;./?/init.lua;" .. package.path
local S = require("tests.harness").suite("gen2 evolution anim")
local check, eq = S.check, S.eq
local EvolutionAnim = require("src.ui.gen2.EvolutionAnim")
local Strings = require("src.core.Strings")
-- No screen may use the stack's hook name for its own purposes.
check(rawget(EvolutionAnim, "enter") == nil,
"EvolutionAnim does not define `enter` (the stack lifecycle hook)")
check(type(rawget(EvolutionAnim, "setPhase")) == "function",
"its phase transition has a name of its own")
do
Strings.load({ strings = {
["What? %s\nis evolving!"] = "Quoi ? %s\nevolue !",
} })
local mon = { species = "OLD", nickname = "SURNOM", moves = {} }
local anim = EvolutionAnim.new({ data = { audio = {} } }, {
mon = mon, entry = { into = "NEW" }, party = { mon },
})
eq(anim.lines[1], "Quoi ? SURNOM",
"evolution messages use a complete translated template")
eq(anim.lines[2], "evolue !", "the translated template is split afterwards")
Strings.load({})
end
local cache = os.getenv("GOLD_CACHE")
if not cache then
cache = (os.getenv("HOME") or "")
.. "/Library/Application Support/LOVE/gold-dev/gold"
end
local function loadGen(name)
local path = cache .. "/data/generated/" .. name
local fh = io.open(path, "r")
if not fh then return nil end
fh:close()
return assert(loadfile(path))()
end
local pokemon = loadGen("pokemon.lua")
if not pokemon then
check(true, "gold cache absent : name check only (SKIP the run-through)")
S.finish()
return
end
local Evolution = require("src.core.gen2.Evolution")
local data = { pokemon = pokemon, moves = loadGen("moves.lua"), audio = {},
gen2Palettes = loadGen("palettes.lua") }
-- CYNDAQUIL -> QUILAVA at 14, the exact evolution the bot wedged on.
local mon = { species = "CYNDAQUIL", level = 14, hp = 20, maxHp = 20,
moves = { { id = "EMBER", pp = 10, maxPp = 25 } } }
local save = { party = { mon }, pokedex = { seen = {}, caught = {} } }
local game = { data = data, save = save }
local plan = Evolution.plan(data, { mon }, { [1] = true }, { timeOfDay = 1 })
eq(#plan, 1, "CYNDAQUIL at 14 has an evolution to play")
local done = false
local anim = EvolutionAnim.new(game, {
mon = plan[1].mon, entry = plan[1].entry, index = plan[1].index,
party = save.party, save = save,
onDone = function() done = true end,
})
-- The push, exactly as Screens.push does it: no extra arguments. This is the
-- line that used to kill the screen.
-- StateStack is used as a singleton (Game calls StateStack:init()), so the
-- test drives it directly rather than inventing an instance it does not have.
local StateStack = require("src.core.StateStack")
StateStack:init()
StateStack:push(anim)
eq(anim.phase, "evolving", "being pushed does not clear the phase")
-- Mashing A is what a bot (or an impatient player) does; it must not stall it.
game.input = { wasPressed = function(_, b) return b == "a" end }
local frames = 0
for _ = 1, 5000 do
if done then break end
frames = frames + 1
check(anim.phase ~= nil, "phase stays set while the animation runs")
if anim.phase == nil then break end
anim:update(1 / 60)
end
check(done, "the evolution runs to completion and calls onDone")
check(frames < 2000, "and finishes promptly (" .. frames .. " frames)")
eq(save.party[1].species, "QUILAVA", "the party record is written back")
S.finish()