Files
gen1recomp/tests/engine/overworld_field_faint_heal_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

140 lines
5.1 KiB
Lua

-- Two OverworldController.lua messages used to be bare Lua literals:
-- applyFieldPoison()'s "%s\nfainted!" (the third of three collapsed
-- fainted-message ROM strings, _PokemonFaintedText) and
-- useSoftboiledFieldMove()'s "It won't have\nany effect."/"%s's HP\nwas
-- restored!" (the same _ItemUseNoEffectText/_PotionText labels
-- ItemEffects.lua's real potion message already uses -- _PotionText's
-- second slot is the actual amount healed, which the old literal never
-- showed at all).
--
-- Uses the debug.setupvalue technique already established in
-- oaks_pc_flow.lua to fake the module-level Game/TextBox upvalues
-- ROM-free, without going through the heavy OverworldState:enter().
package.path = "./?.lua;./?/init.lua;" .. package.path
local T = require("tests.modkit")
local Data = T.fixtures.fresh()
local SaveData = require("src.core.SaveData")
local Pokemon = require("src.pokemon.Pokemon")
local OW = require("src.world.OverworldController")
local function setUpvalue(fn, name, val)
local i = 1
while true do
local n = debug.getupvalue(fn, i)
if not n then return false end
if n == name then debug.setupvalue(fn, i, val); return true end
i = i + 1
end
end
local pushed = {}
local textBoxStub = {
new = function(_, text, onDone, opts)
return { text = text, onDone = onDone, opts = opts }
end,
}
local realSound = package.loaded["src.core.Sound"]
package.loaded["src.core.Sound"] = { play = function() end, playCry = function() end }
local function mkGame()
local save = SaveData.newGame()
save.party = { Pokemon.new(Data, "FIXMON_A", 20) }
pushed = {}
return {
data = Data, save = save,
stack = { push = function(_, item) pushed[#pushed + 1] = item end },
}
end
for _, name in ipairs({ "applyFieldPoison", "useSoftboiledFieldMove" }) do
T.check(setUpvalue(OW[name], "Game", mkGame()), ("Game upvalue on %s"):format(name))
T.check(setUpvalue(OW[name], "TextBox", textBoxStub), ("TextBox upvalue on %s"):format(name))
end
local fakeSelf = setmetatable({}, { __index = OW })
-- ---- applyFieldPoison: _PokemonFaintedText ----
do
local game = mkGame()
setUpvalue(OW.applyFieldPoison, "Game", game)
local mon = game.save.party[1]
mon.status = "PSN"
mon.hp = 1 -- one poison tick (1 dmg by default) faints it
game.save.poisonSteps = 3 -- (3+1) % 4 == 0: this step ticks poison
Data.text._PokemonFaintedText = "FAKE {RAM:wNameBuffer} FAKE!"
fakeSelf:applyFieldPoison()
T.eq(pushed[1] and pushed[1].text, "FAKE " .. (mon.nickname or "FIXMON A") .. " FAKE!",
"a translated _PokemonFaintedText reaches the field-poison faint message")
Data.text._PokemonFaintedText = nil
end
-- vanilla: no catalog entry, English literal
do
local game = mkGame()
setUpvalue(OW.applyFieldPoison, "Game", game)
local mon = game.save.party[1]
mon.status = "PSN"
mon.hp = 1
game.save.poisonSteps = 3
fakeSelf:applyFieldPoison()
T.eq(pushed[1] and pushed[1].text,
(mon.nickname or "FIXMON A") .. "\nfainted!",
"no catalog entry falls back to the English fainted literal")
end
-- ---- useSoftboiledFieldMove: _ItemUseNoEffectText / _PotionText ----
do
local game = mkGame()
setUpvalue(OW.useSoftboiledFieldMove, "Game", game)
local user = Pokemon.new(Data, "FIXMON_A", 20)
local target = Pokemon.new(Data, "FIXMON_B", 20)
target.hp = target.stats.hp -- already full: no effect
Data.text._ItemUseNoEffectText = "FAKE-NOEFFECT!"
local ok = fakeSelf:useSoftboiledFieldMove(user, target)
T.check(ok == false, "a full-HP target reports no effect")
T.eq(pushed[1] and pushed[1].text, "FAKE-NOEFFECT!",
"a translated _ItemUseNoEffectText reaches the no-effect message")
Data.text._ItemUseNoEffectText = nil
end
do
local game = mkGame()
setUpvalue(OW.useSoftboiledFieldMove, "Game", game)
local user = Pokemon.new(Data, "FIXMON_A", 20)
local target = Pokemon.new(Data, "FIXMON_B", 20)
target.hp = target.stats.hp - 10 -- missing exactly 10 HP
Data.text._PotionText = "FAKE {RAM:wNameBuffer} healed {NUM:wHPBarHPDifference, 2, 3}!"
local ok = fakeSelf:useSoftboiledFieldMove(user, target)
T.check(ok == true, "a damaged target heals successfully")
T.eq(pushed[1] and pushed[1].text,
"FAKE " .. (target.nickname or "FIXMON B") .. " healed 10!",
"a translated _PotionText reaches the heal message, amount included")
Data.text._PotionText = nil
end
-- vanilla: no catalog entry, the fallback's single %s slot still fills
-- correctly (the amount is silently dropped by design, same as
-- ItemEffects.lua's own _PotionText fallback -- not a regression, this
-- matches the pre-fix literal's behavior exactly)
do
local game = mkGame()
setUpvalue(OW.useSoftboiledFieldMove, "Game", game)
local user = Pokemon.new(Data, "FIXMON_A", 20)
local target = Pokemon.new(Data, "FIXMON_B", 20)
target.hp = target.stats.hp - 10
local ok = fakeSelf:useSoftboiledFieldMove(user, target)
T.check(ok == true, "a damaged target heals successfully (vanilla)")
T.eq(pushed[1] and pushed[1].text,
(target.nickname or "FIXMON B") .. "'s HP\nwas restored!",
"no catalog entry falls back to the English literal (no amount shown)")
end
package.loaded["src.core.Sound"] = realSound
T.finish("overworld_field_faint_heal_romtext")