Files
gen1recomp/tests/engine/battle_ball_dodge_romtext.lua
T
thibautbus 72592665d7 Cover the romText fixes with targeted regression tests
Every existing test around these callsites only ever runs with an
empty/fixture Data.text, so none of them could tell a properly-wired
romText/t._X call apart from a literal that never looked at the
catalog at all -- every assertion passed either way, the same coverage
gap the museum ticket clerk and status abbreviation fixes hit earlier.

Seven new tests fake the real label for each fix and assert the pushed
or queued message uses the translated value, plus a vanilla case per
fix confirming the no-catalog fallback still matches the original
English literal exactly:
- box_release_confirmation_romtext.lua (BoxMenu _OnceReleasedText, via
  a TextBox.new spy so real pagination/choice behavior stays intact)
- slot_machine_lined_up_romtext.lua (SlotMachine _LinedUpText, symbol
  concatenated in front of the translated suffix)
- battle_fainted_message_romtext.lua (BattleState onFaint, both
  _PlayerMonFaintedText and _EnemyMonFaintedText, confirming the raw
  name reaches each without a duplicated "Enemy")
- battle_catch_messages_romtext.lua (BattleState storeCaughtMon,
  _ItemUseBallText06 plus both _ItemUseBallText07/08 branches on
  EVENT_MET_BILL)
- battle_ball_dodge_romtext.lua (BattleState throwBall,
  _ItemUseBallText00's \f-merge collapsing to exactly one queued
  message)
- overworld_field_faint_heal_romtext.lua (OverworldController
  applyFieldPoison's _PokemonFaintedText, and
  useSoftboiledFieldMove's _ItemUseNoEffectText/_PotionText including
  the recovered-amount slot the old literal never showed)
- overworld_hidden_item_romtext.lua (OverworldController
  tryHiddenObject's _FoundHiddenItemText, both the {PLAYER} token and
  the item name landing in the right slots)

Confirmed several of these fail against the pre-fix code and pass
against the current code, not just reasoned about it. Not every one of
the 15 fixed callsites has its own dedicated test -- the ShopMenu,
LinkBattle, trainer-withdraw/sent-out and the normal (non-hidden)
found-item sites share the same romText mechanism already proven
correct by the seven tests above, and building the heavier fixtures
each would need (a full mart flow, a link session, a trainer AI
switch, an object_event NPC) wasn't judged worth it for what would be
the same assertion shape again.
2026-08-20 09:18:56 +02:00

88 lines
3.4 KiB
Lua

-- BattleState:throwBall()'s can't-be-caught path used to queue two
-- separate Strings() literals ("It dodged the\nthrown BALL!" then "This
-- POKéMON\ncan't be caught!"). The real ROM label _ItemUseBallText00
-- combines both as one \f-paged string. TextBox.new() would split \f
-- itself, but throwBall() queues through self:sayNext(), which goes
-- through the battle queue's own BattleState:startMessage() -- and that
-- one only splits on \n/\v, not \f (confirmed live: the \f landed
-- mid-line and the second sentence overflowed off the box instead of
-- starting a fresh page). The fix resolves the label once, then splits
-- it the same way TextBox.lua does and queues one sayNext per page. This
-- test fakes the label and checks the two pages reach the queue as two
-- separate messages, in order, not merged into one with a raw \f still
-- inside it.
package.path = "./?.lua;./?/init.lua;" .. package.path
local T = require("tests.modkit")
local Data = T.fixtures.fresh()
require("src.render.Font").load(Data)
local BattleState = require("src.battle.BattleState")
local Pokemon = require("src.pokemon.Pokemon")
local SaveData = require("src.core.SaveData")
local function mkbattle()
local save = SaveData.newGame()
save.party = { Pokemon.new(Data, "FIXMON_A", 10) }
local game = { data = Data, save = save,
stack = { top = function() return nil end, push = function() end } }
local battle = BattleState.newWild(game, "FIXMON_C", 8)
battle.ghost = true -- forces the can't-be-caught path
return battle
end
-- throwBall defers its message-queuing work into self.queue via one
-- top-level self:act(fn); run only that one function to reach the
-- say() calls the fix touches. It's the last entry throwBall itself
-- appends (after the immediate sayAuto), and it further queues its own
-- self:act(function() self:executeAction(...) end) for the actual enemy
-- turn -- deliberately NOT run here (out of scope, and running the queue
-- generically after mutation risks looping into a real turn simulation)
local function runThrowBallAct(battle)
for i = #battle.queue, 1, -1 do
if battle.queue[i].fn then
battle.queue[i].fn()
return
end
end
end
local function textEntries(battle)
local out = {}
for _, entry in ipairs(battle.queue) do
if entry.text then out[#out + 1] = entry.text end
end
return out
end
-- translated: the faked label's two \f-separated pages reach the queue
-- as two separate messages, in order, and neither one still contains a
-- raw \f (which would mean the battle queue's own renderer has to deal
-- with it, and it can't)
do
local battle = mkbattle()
Data.text._ItemUseBallText00 = "FAKE-DODGE!\fFAKE-CANTCATCH!"
battle:throwBall("FIX_BALL")
runThrowBallAct(battle)
local texts = textEntries(battle)
T.eq(texts[1], "FAKE-DODGE!", "page 1 reaches the queue on its own")
T.eq(texts[2], "FAKE-CANTCATCH!", "page 2 follows right after, still in order")
for _, t in ipairs(texts) do
T.check(not t:find("\f", 1, true), "no queued message still carries a raw \\f")
end
Data.text._ItemUseBallText00 = nil
end
-- vanilla: no catalog entry still falls back to the two English pages,
-- split the same way
do
local battle = mkbattle()
battle:throwBall("FIX_BALL")
runThrowBallAct(battle)
local texts = textEntries(battle)
T.eq(texts[1], "It dodged the\nthrown BALL!", "vanilla page 1")
T.eq(texts[2], "This POKéMON\ncan't be caught!", "vanilla page 2")
end
T.finish("battle_ball_dodge_romtext")