From fa4e96e8799707acedbf72c1590a15e68ba781a6 Mon Sep 17 00:00:00 2001 From: thibautbus <310327033+thibautbus@users.noreply.github.com> Date: Thu, 3 Sep 2026 19:11:36 +0200 Subject: [PATCH] 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). --- src/ui/gen2/BattleTowerMenu.lua | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/ui/gen2/BattleTowerMenu.lua b/src/ui/gen2/BattleTowerMenu.lua index b47c0178..a121c249 100644 --- a/src/ui/gen2/BattleTowerMenu.lua +++ b/src/ui/gen2/BattleTowerMenu.lua @@ -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)