Route Battle Tower Menu messages through Strings()

BattleTowerMenu:drawPanel() shows self.message via Chrome.printWrapped
directly, with no Strings() lookup of its own -- same shape as
Game2:say()/TextBox.new(), which also expect their caller to have
already resolved the text. Every assignment to self.message in this
file used the raw Strings.source() return value (an identity/harvest
marker, not a lookup) instead: the level-picker prompt, the "quit your
challenge?" confirmation, the level-cap refusal, and the Uber-clause
refusal (which additionally called Lua's own string.format() on the
untranslated source instead of Strings(UBER_TEXT, name)). The YES/NO
confirmation labels and the CANCEL row label had the identical bug one
level down: declared with Strings.source() but printed via
Chrome.print() with no Strings() call around them either. The whole
Battle Tower level-picker menu, including its yes/no prompt, stayed in
English regardless of any translation catalog a mod supplied.

Found via a systematic trace of every TextBox.new() (143) and
Chrome.print()/Chrome.printWrapped() call site in the engine back to
its text source, cross-checked against the real tools/modkit.py
harvester and an independent review of the branch. That same review
also flagged src/world/OverworldController.lua:3314
(Strings.source("%s's PC"):format(playerName)) as the same bug shape,
and an earlier revision of this branch "fixed" it to
Strings("%s's PC", playerName) -- but that call site is not a bug:
openPC() deliberately builds every PC row's label from a stable
English Strings.source() value first, so the ui.pc.items mod hook can
match/reorder rows by their vanilla English text, and only translates
via a second translatedLabels[playerPcLabel] pass after the hook has
run (openPC()'s own comment: "Hooks identify the vanilla rows by their
English source labels. Delay localization until after ui.pc.items has
inspected/reordered/replaced them"). Translating playerPcLabel early
broke that contract -- confirmed by tests/engine/rby_translation_runtime_test.lua,
which already covered this exact case and failed with
"ui.pc.items sees the stable player-PC source label (got PC DE RED,
want RED's PC)" once that change landed. Reverted; the label was
already being translated correctly by the existing second pass, which
this branch never needed to touch.

A second independent review round, after the fixes above, found one
more real gap in the same family: ItemEffects.lua's VITAMIN_LABEL
table (folded into the previous commit once found), and confirmed
everything else on the branch clean.

Verified with tools/modkit.py's harvest_engine_strings that all seven
BattleTowerMenu.lua literals are still discovered from their
Strings.source() declarations, luajit tests/run_gen2.lua: 144/145 (the
one unrelated pre-existing failure noted in the previous commit), and
luajit tests/run_engine.lua: 533/533 (including the PC-label
regression test above, now passing).
This commit is contained in:
thibautbus
2026-09-03 19:11:36 +02:00
parent 9401445c0f
commit fa4e96e879
+11 -11
View File
@@ -76,7 +76,7 @@ function BattleTowerMenu.new(game, opts)
-- `ld a, $1 / ld [wcd4f], a` (../pokecrystal/mobile/mobile_46.asm:1152-1153)
self.cursor = 1
self.phase = "pick"
self.message = PICK_TEXT
self.message = Strings(PICK_TEXT)
return self
end
@@ -116,17 +116,17 @@ function BattleTowerMenu:confirm()
if not row then
-- ../pokecrystal/mobile/mobile_46.asm:1291-1303 `.asm_118a3c`
self.phase = "quit"
self.message = QUIT_TEXT
self.message = Strings(QUIT_TEXT)
self.yes = true
return
end
if BattleTower.levelCheck(self.party, row.group) then
return self:refuse(TOPS_TEXT)
return self:refuse(Strings(TOPS_TEXT))
end
local uber = BattleTower.ubersCheck(self.party, row.group)
if uber then
local name = (self.monName and self.monName(uber)) or uber
return self:refuse(string.format(UBER_TEXT, name))
return self:refuse(Strings(UBER_TEXT, name))
end
self:finish(row.group)
end
@@ -147,7 +147,7 @@ function BattleTowerMenu:updatePick()
elseif input:wasPressed("b") then
self:playSfx("Sfx_ReadText2")
self.phase = "quit"
self.message = QUIT_TEXT
self.message = Strings(QUIT_TEXT)
self.yes = true
end
end
@@ -165,12 +165,12 @@ function BattleTowerMenu:updateQuit()
self:playSfx("Sfx_ReadText2")
if self.yes then return self:finish(nil) end
self.phase = "pick"
self.message = PICK_TEXT
self.message = Strings(PICK_TEXT)
self.cursor = 1
elseif input:wasPressed("b") then
self:playSfx("Sfx_ReadText2")
self.phase = "pick"
self.message = PICK_TEXT
self.message = Strings(PICK_TEXT)
self.cursor = 1
end
end
@@ -181,7 +181,7 @@ function BattleTowerMenu:update(_dt)
self.wait = (self.wait or 0) - 1
if self.wait > 0 then return end
self.phase = "pick"
self.message = PICK_TEXT
self.message = Strings(PICK_TEXT)
self.cursor = 1
return
end
@@ -198,15 +198,15 @@ function BattleTowerMenu:drawPanel()
end
if self.phase == "quit" then
Chrome.box(YN_X, YN_Y, YN_W, YN_H)
Chrome.print(YES_LABEL, YN_TEXT_X, YES_Y)
Chrome.print(NO_LABEL, YN_TEXT_X, NO_Y)
Chrome.print(Strings(YES_LABEL), YN_TEXT_X, YES_Y)
Chrome.print(Strings(NO_LABEL), YN_TEXT_X, NO_Y)
Chrome.cursor(YN_TEXT_X - 1, self.yes and YES_Y or NO_Y)
love.graphics.setColor(1, 1, 1, 1)
return
end
Chrome.box(PICK_X, PICK_Y, PICK_W, PICK_H)
local row = self.rows[self.cursor]
Chrome.print(row and BattleTowerMenu.levelLabel(row.group) or CANCEL_LABEL,
Chrome.print(row and BattleTowerMenu.levelLabel(row.group) or Strings(CANCEL_LABEL),
ROW_X, ROW_Y)
Chrome.print(UP_ARROW, ARROW_X, UP_Y)
Chrome.print(DOWN_ARROW, ARROW_X, DOWN_Y)