Make the rose! messages' stat name harvestable by the mod catalog tool

Strings(stat:upper()) is a dynamic argument -- tools/modkit.py's
STRINGS_CALL harvester only matches a literal string right after
Strings(/Strings.source(, so it can't discover "ATTACK"/"DEFENSE"/etc.
from these call sites. Translation coverage happened to still work
only because the same literals are independently harvested from
unrelated call sites (MoveEffects.lua's STAT_LABEL, BattleState.lua's
literal Strings("ATTACK") calls) -- real but fragile, found in review.

Reuse the codebase's existing pattern for exactly this situation
(MoveEffects.lua's STAT_LABEL): a local table built at require time
with Strings.source(...), which the harvester can see, resolved to a
translated label at use time with Strings(TABLE[key]). Adds one such
table to ItemEffects.lua (covering its X-item and vitamin call sites,
including "hp") and one to TrainerAI.lua.
This commit is contained in:
thibautbus
2026-08-16 20:48:28 +02:00
parent c280119d03
commit 9423337bcc
2 changed files with 22 additions and 3 deletions
+10 -1
View File
@@ -33,6 +33,15 @@ end
local HEAL_AMOUNT = { POTION = 20, SUPER_POTION = 50, HYPER_POTION = 200 }
local X_STAT = { X_ATTACK = "attack", X_DEFEND = "defense", X_SPEED = "speed" }
-- Strings.source, not Strings: harvested at require time so the catalog
-- generator can see the literal, same pattern as MoveEffects.lua's
-- STAT_LABEL (#811) -- Strings(stat:upper()) alone is a dynamic argument
-- the harvester can't discover.
local STAT_LABEL = {
attack = Strings.source("ATTACK"), defense = Strings.source("DEFENSE"),
speed = Strings.source("SPEED"),
}
-- The trainer's ai_classes record from the merged registry; the direct
-- require covers battles built without a loader. A trainer record's
-- aiClass field picks a record other than its own id.
@@ -124,7 +133,7 @@ function TrainerAI.useItem(battle, item)
elseif X_STAT[item] then
local stat = X_STAT[item]
enemy.stages[stat] = math.min(6, (enemy.stages[stat] or 0) + 1)
table.insert(msgs, Strings("%s's\n%s rose!", displayName(enemy), Strings(stat:upper())))
table.insert(msgs, Strings("%s's\n%s rose!", displayName(enemy), Strings(STAT_LABEL[stat])))
elseif item == "GUARD_SPEC" then
enemy.mist = true
table.insert(msgs, Strings("%s's\nprotected against\nstat changes!", displayName(enemy)))
+12 -2
View File
@@ -59,6 +59,16 @@ local STONES = {
LEAF_STONE = true, MOON_STONE = true,
}
-- Strings.source, not Strings: harvested at require time so the catalog
-- generator can see the literal, same pattern as MoveEffects.lua's
-- STAT_LABEL (#811) -- Strings(stat:upper()) alone is a dynamic argument
-- the harvester can't discover.
local STAT_LABEL = {
hp = Strings.source("HP"), attack = Strings.source("ATTACK"),
defense = Strings.source("DEFENSE"), speed = Strings.source("SPEED"),
special = Strings.source("SPECIAL"), accuracy = Strings.source("ACCURACY"),
}
-- vitamins: stat-exp boosters (ItemUseVitamin)
local VITAMINS = { HP_UP = "hp", PROTEIN = "attack", IRON = "defense",
CARBOS = "speed", CALCIUM = "special" }
@@ -294,7 +304,7 @@ function ItemEffects.use(data, save, itemId, target, battle, moveIndex, ow)
"Nothing happened!") }
end
b.stages[stat] = cur + 1
return "consumed", { Strings("%s's\n%s rose!", b.name, Strings(stat:upper())) }
return "consumed", { Strings("%s's\n%s rose!", b.name, Strings(STAT_LABEL[stat])) }
end
-- ItemUseDireHit/ItemUseGuardSpec always set the bit and consume
-- the item, even when it is already active
@@ -493,7 +503,7 @@ function ItemEffects.use(data, save, itemId, target, battle, moveIndex, ow)
-- Spanish ROM puts the stat before the name), so the extracted line
-- cannot be filled positionally; the engine wording stands
return "consumed", { Strings("%s's %s\nrose!", monName(data, target),
Strings(vitaminStat == "hp" and "HP" or vitaminStat:upper())) }
Strings(STAT_LABEL[vitaminStat])) }
end
-- PP UP boosts the move the player picked (ItemUsePPUp's move menu)