Bug squashing and translation mods (#311)

* audio timing stuff

* bug fixes and translation additions

* translation stuff

* Update modkit.py

* better asset resolution
This commit is contained in:
bryanthaboi
2026-07-27 13:37:05 -04:00
committed by GitHub
parent 31365d8dfd
commit f0a88ea473
78 changed files with 3691 additions and 787 deletions
+14
View File
@@ -79,6 +79,19 @@ local PLAYER_SPRITES = {
bike = "SPRITE_RED_BIKE", fly = "SPRITE_BIRD",
}
-- The player's own trainer art: RedPicBack (the battle back pic, up until
-- "Go!"), OldManPicBack (the catch tutorial fights in his place), and
-- RedPicFront (gfx/player/red.png, shared by Oak's intro, the trainer card
-- and the Hall of Fame). Asset paths, not sprite ids -- these are pics,
-- unrelated to the walking sheet in PLAYER_SPRITES above. Resolution runs
-- through Sprites.playerPath, so the player.sprite hook can still vary them
-- per save after content freezes.
local PLAYER_PICS = {
back = "assets/generated/battle/redb.png",
demoBack = "assets/generated/battle/oldmanb.png",
front = "assets/generated/trainer_card/red.png",
}
-- Route22Gate_Script rewrites wLastMap from the player's Y every frame, so
-- the north exit leaves onto Route 23 and the south onto Route 22. Rules
-- are ordered, first match wins, the last row is the default.
@@ -106,6 +119,7 @@ FieldDefaults.FIELD = {
fishing = FISHING,
safari = SAFARI,
playerSprites = PLAYER_SPRITES,
playerPics = PLAYER_PICS,
lastMapRewrites = LAST_MAP_REWRITES,
-- CheckIfInOutsideMap: what counts as "outside" for the wLastMap memory
outsideTilesets = { "OVERWORLD", "PLATEAU" },
+103 -65
View File
@@ -22,6 +22,7 @@ local TextBox = require("src.render.TextBox")
local Transition = require("src.render.Transition")
local Warp = require("src.world.Warp")
local Zoom = require("src.render.Zoom")
local Strings = require("src.core.Strings")
-- isOverworld marks the live world state for WorldAPI's stack scan
local OverworldState = { isOpaque = true, isOverworld = true }
@@ -1312,10 +1313,10 @@ function OverworldState:goFishing(rod)
Game.stack:push(TextBox.new(Game, ". . .", function()
self.fishing = nil
if not enc then
Game.stack:push(TextBox.new(Game, "Not even a nibble!"))
Game.stack:push(TextBox.new(Game, Strings("Not even a nibble!")))
return
end
Game.stack:push(TextBox.new(Game, "Oh!\nIt's a bite!", function()
Game.stack:push(TextBox.new(Game, Strings("Oh!\nIt's a bite!"), function()
local BattleState = require("src.battle.BattleState")
local battle = BattleState.newWild(Game, enc.species, enc.level, { hooked = true })
if Game.save.safari and Map.inRegion(self.map.def, "SAFARI", "SAFARI_ZONE") then
@@ -1482,29 +1483,66 @@ function OverworldState:tryBookshelf(fx, fy)
if self.map.def.tileset == "MANSION"
and self.map:tileAt(fx * 2, fy * 2) == 0x38 then
Game.stack:push(TextBox.new(Game, t._DiglettSculptureText
or "It's a sculpture\nof DIGLETT."))
or Strings("It's a sculpture\nof DIGLETT.")))
return true
end
Game.stack:push(TextBox.new(Game, t._PokemonBooksText
or "Crammed full of\nPOKéMON books!"))
or Strings("Crammed full of\nPOKéMON books!")))
elseif kind == "stuff" then
Game.stack:push(TextBox.new(Game, t._PokemonStuffText
or "There's a slew of\nPOKéMON stuff!"))
or Strings("There's a slew of\nPOKéMON stuff!")))
elseif kind == "elevator" then
Game.stack:push(TextBox.new(Game, t._ElevatorText
or "An elevator!"))
or Strings("An elevator!")))
elseif kind == "statues" then
-- IndigoPlateauStatues: the plaque, then one of the two lines
-- keyed by the statue's column (XCoord bit 0)
local line = (self.player.cellX % 2 == 0) and t._IndigoPlateauStatuesText2
or t._IndigoPlateauStatuesText3
Game.stack:push(TextBox.new(Game,
(t._IndigoPlateauStatuesText1 or "INDIGO PLATEAU") .. "\f"
.. (line or "POKéMON LEAGUE HQ")))
(t._IndigoPlateauStatuesText1 or Strings("INDIGO PLATEAU")) .. "\f"
.. (line or Strings("POKéMON LEAGUE HQ"))))
end
return true
end
-- Bench guys are the one hidden-event family whose extracted label is a
-- wrapper rather than the string itself: bench_guys.asm defines e.g.
-- PewterCityPokecenterBenchGuyText:: as `text_far _PewterCityPokecenterGuyText`,
-- so data/generated/field.lua carries the wrapper name while the text sits
-- under the far label. The two only coincide for Mt Moon and the Celadon
-- hotel, which is why every other bench guy answered with silence (#248).
-- pokered's own names are irregular here (Cerulean/Lavender/Vermilion drop
-- "City", Cinnabar drops "Island"), so this is a table, not a transform.
local BENCH_GUY_TEXT = {
ViridianCityPokecenterBenchGuyText = "_ViridianCityPokecenterGuyText",
PewterCityPokecenterBenchGuyText = "_PewterCityPokecenterGuyText",
CeruleanCityPokecenterBenchGuyText = "_CeruleanPokecenterGuyText",
LavenderCityPokecenterBenchGuyText = "_LavenderPokecenterGuyText",
VermilionCityPokecenterBenchGuyText = "_VermilionPokecenterGuyText",
CeladonCityPokecenterBenchGuyText = "_CeladonCityPokecenterGuyText",
FuchsiaCityPokecenterBenchGuyText = "_FuchsiaCityPokecenterGuyText",
CinnabarIslandPokecenterBenchGuyText = "_CinnabarPokecenterGuyText",
RockTunnelPokecenterBenchGuyText = "_RockTunnelPokecenterGuyText",
}
-- SaffronCityPokecenterBenchGuyText is text_asm: he complains about ROCKET
-- until EVENT_BEAT_SILPH_CO_GIOVANNI, then thanks you for clearing them out.
-- Takes data/save rather than reading the Game upvalue so tests can resolve
-- every label in the table without standing a whole overworld up.
function OverworldState.benchGuyText(data, save, label)
if not label then return nil end
if label == "SaffronCityPokecenterBenchGuyText" then
local key = (save and save.flags and save.flags.EVENT_BEAT_SILPH_CO_GIOVANNI)
and "_SaffronCityPokecenterGuyText2"
or "_SaffronCityPokecenterGuyText1"
return data.text[key]
end
-- the direct name first, so a cache whose extractor already resolved the
-- far label keeps working without consulting the table
return data.text["_" .. label] or data.text[BENCH_GUY_TEXT[label] or ""]
end
-- Hidden events at the faced cell (data/events/hidden_events.asm):
-- HiddenItems give their item once, HiddenCoins fill the COIN CASE,
-- StartSlotMachine seats open the minigame. Taken spots persist in
@@ -1519,7 +1557,7 @@ function OverworldState:tryHiddenObject(fx, fy)
save.hiddenTaken = save.hiddenTaken or {}
if save.hiddenTaken[key] then return false end
if not require("src.inventory.Bag").add(save, h.item, 1) then
Game.stack:push(TextBox.new(Game, "You can't carry\nany more items!"))
Game.stack:push(TextBox.new(Game, Strings("You can't carry\nany more items!")))
return true
end
save.hiddenTaken[key] = true
@@ -1527,7 +1565,7 @@ function OverworldState:tryHiddenObject(fx, fy)
-- hidden items always play SFX_GET_ITEM_2 (hidden_items.asm)
require("src.core.Sound").play(Game.data, "Get_Item2")
Game.stack:push(TextBox.new(Game,
("%s found\n%s!"):format(save.player.name, name)))
Strings("%s found\n%s!", save.player.name, name)))
return true
end
end
@@ -1541,7 +1579,7 @@ function OverworldState:tryHiddenObject(fx, fy)
save.coins = math.min(9999, (save.coins or 0) + h.coins)
require("src.core.Sound").play(Game.data, "Get_Item2")
Game.stack:push(TextBox.new(Game,
("%s found\n%d coins!"):format(save.player.name, h.coins)))
Strings("%s found\n%d coins!", save.player.name, h.coins)))
return true
end
end
@@ -1553,20 +1591,20 @@ function OverworldState:tryHiddenObject(fx, fy)
if h.x == fx and h.y == fy then
if h.state == "out_of_order" then
Game.stack:push(TextBox.new(Game, txt._GameCornerOutOfOrderText
or "OUT OF ORDER\nThis is broken."))
or Strings("OUT OF ORDER\nThis is broken.")))
elseif h.state == "out_to_lunch" then
Game.stack:push(TextBox.new(Game, txt._GameCornerOutToLunchText
or "OUT TO LUNCH\nThis is reserved."))
or Strings("OUT TO LUNCH\nThis is reserved.")))
elseif h.state == "keys" then
Game.stack:push(TextBox.new(Game, txt._GameCornerSomeonesKeysText
or "Someone's keys!\nThey'll be back."))
or Strings("Someone's keys!\nThey'll be back.")))
elseif not save.inventory.COIN_CASE then
Game.stack:push(TextBox.new(Game, txt._GameCornerCoinCaseText
or "A COIN CASE is\nrequired!"))
or Strings("A COIN CASE is\nrequired!")))
elseif (save.coins or 0) == 0 then
-- AbleToPlaySlotsCheck: a COIN CASE with no coins can't play
Game.stack:push(TextBox.new(Game, txt._GameCornerNoCoinsText
or "You don't have\nany coins!"))
or Strings("You don't have\nany coins!")))
else
-- one machine per visit is secretly lucky
-- (wLuckySlotHiddenEventIndex, engine/slots/game_corner_slots.asm)
@@ -1609,7 +1647,7 @@ function OverworldState:tryHiddenObject(fx, fy)
-- bench guys (data/events/bench_guys.asm)
for _, h in ipairs(extras.benchGuys[self.map.id] or {}) do
if h.x == fx and h.y == fy and (not h.facing or h.facing == facing) then
local text = h.text and Game.data.text["_" .. h.text]
local text = OverworldState.benchGuyText(Game.data, save, h.text)
if text then
Game.stack:push(TextBox.new(Game, text))
return true
@@ -1626,7 +1664,7 @@ function OverworldState:tryHiddenObject(fx, fy)
if gym then
local key = save.inventory[gym.badge] and "_GymStatueText2" or "_GymStatueText1"
local text = Game.data.text[key]
or "{RAM}\nPOKéMON GYM\nLEADER: {RAM}"
or Strings("{RAM}\nPOKéMON GYM\nLEADER: {RAM}")
text = text:gsub("{RAM:wGymCityName}", gym.city)
:gsub("{RAM:wGymLeaderName}", gym.leader)
Game.stack:push(TextBox.new(Game, text))
@@ -1639,7 +1677,7 @@ function OverworldState:tryHiddenObject(fx, fy)
for _, h in ipairs(extras.printTrash and extras.printTrash[self.map.id] or {}) do
if h.x == fx and h.y == fy then
Game.stack:push(TextBox.new(Game, txt._VermilionGymTrashText
or "Nope, there's\nonly trash here."))
or Strings("Nope, there's\nonly trash here.")))
return true
end
end
@@ -1681,7 +1719,7 @@ function OverworldState:tryCardKeyDoor(fx, fy)
local t = Game.data.text
if not Game.save.inventory.CARD_KEY then
Game.stack:push(TextBox.new(Game,
t._CardKeyFailText or "Darn! It needs a\nCARD KEY!"))
t._CardKeyFailText or Strings("Darn! It needs a\nCARD KEY!")))
return true
end
require("src.core.Sound").play(Game.data, "Go_Inside")
@@ -1698,8 +1736,8 @@ function OverworldState:tryCardKeyDoor(fx, fy)
end
end
Game.stack:push(TextBox.new(Game,
(t._CardKeySuccessText1 or "Bingo!")
.. (t._CardKeySuccessText2 or "\nThe CARD KEY\nopened the door!")))
(t._CardKeySuccessText1 or Strings("Bingo!"))
.. (t._CardKeySuccessText2 or Strings("\nThe CARD KEY\nopened the door!"))))
return true
end
@@ -1716,7 +1754,7 @@ function OverworldState:trashCanSwitch(canIndex)
local t = Game.data.text
local save = Game.save
local tc = Game.data.field.hiddenExtras.trashCans
local trashText = t._VermilionGymTrashText or "Nope, there's\nonly trash here."
local trashText = t._VermilionGymTrashText or Strings("Nope, there's\nonly trash here.")
-- "Don't do the trash can puzzle if it's already been done."
if save.flags.EVENT_2ND_LOCK_OPENED then
Game.stack:push(TextBox.new(Game, trashText))
@@ -1764,7 +1802,7 @@ function OverworldState:trashCanSwitch(canIndex)
-- open.
Game.stack:push(TextBox.new(Game,
t._VermilionGymTrashSuccessText1
or "Hey! There's a\nswitch under the\ntrash!\fThe 1st electric\nlock opened!",
or Strings("Hey! There's a\nswitch under the\ntrash!\fThe 1st electric\nlock opened!"),
function() require("src.core.Sound").play(Game.data, "Switch") end))
return
end
@@ -1781,7 +1819,7 @@ function OverworldState:trashCanSwitch(canIndex)
-- prints, so the beep fires as the box closes, not as it opens.
Game.stack:push(TextBox.new(Game,
t._VermilionGymTrashSuccessText3
or "The 2nd electric\nlock opened!\fThe motorized door\nopened!",
or Strings("The 2nd electric\nlock opened!\fThe motorized door\nopened!"),
function() require("src.core.Sound").play(Game.data, "Go_Inside") end))
else
-- wrong can: ResetEvent EVENT_1ST_LOCK_OPENED and immediately
@@ -1793,7 +1831,7 @@ function OverworldState:trashCanSwitch(canIndex)
-- text prints, so the beep fires as the box closes, not as it opens.
Game.stack:push(TextBox.new(Game,
t._VermilionGymTrashFailText
or "Nope! There's\nonly trash here.\fHey! The electric\nlocks were reset!",
or Strings("Nope! There's\nonly trash here.\fHey! The electric\nlocks were reset!"),
function() require("src.core.Sound").play(Game.data, "Denied") end))
end
end
@@ -1815,12 +1853,12 @@ function OverworldState:billsHousePC()
if flags.EVENT_USED_CELL_SEPARATOR_ON_BILL
or not flags.EVENT_BILL_SAID_USE_CELL_SEPARATOR then
Game.stack:push(TextBox.new(Game, t._BillsHouseMonitorText
or "TELEPORTER is\ndisplayed on the\nPC monitor."))
or Strings("TELEPORTER is\ndisplayed on the\nPC monitor.")))
return
end
require("src.core.Music").stop()
Game.stack:push(TextBox.new(Game, t._BillsHouseInitiatedText
or "{PLAYER} initiated\nTELEPORTER's Cell\nSeparator!", function()
or Strings("{PLAYER} initiated\nTELEPORTER's Cell\nSeparator!"), function()
flags.EVENT_USED_CELL_SEPARATOR_ON_BILL = true
require("src.core.Sound").play(Game.data, "Switch")
self:queueScript({
@@ -1857,13 +1895,13 @@ function OverworldState:billsHousePokemonList()
end,
})
end
table.insert(items, { label = "CANCEL" })
table.insert(items, { label = Strings("CANCEL") })
-- TextBoxBorder b=10,c=9 at (0,0) -> total tw=11, th=12
Game.stack:push(Menu.new(Game, items,
{ tx = 0, ty = 0, tw = 11, th = 12 }))
end
Game.stack:push(TextBox.new(Game, t._BillsHousePokemonListText1
or "BILL's favorite\nPOKéMON list!", openList))
or Strings("BILL's favorite\nPOKéMON list!"), openList))
end
-- BillsHouseBillExitsMachineScript: human Bill appears inside the machine
@@ -1957,7 +1995,7 @@ function OverworldState:trySurf(fx, fy)
local p = self.player
p.surfing = true
require("src.core.Music").setSurfing(Game.data, true)
local text = (Game.data.text._SurfingGotOnText or "{PLAYER} got on\n{RAM:wNameBuffer}!")
local text = (Game.data.text._SurfingGotOnText or Strings("{PLAYER} got on\n{RAM:wNameBuffer}!"))
:gsub("{RAM:wNameBuffer}", name)
Game.stack:push(TextBox.new(Game, text, function()
-- start_sub_menus.asm .surf: UseItem returns (mount + text done),
@@ -2005,7 +2043,7 @@ function OverworldState:tryCut(fx, fy)
-- gen 1 confirms nothing (engine/overworld/cut.asm UsedCut): the
-- _UsedCutText message, then the tree vanishes with dust + SFX_CUT
local name = mon.nickname or Game.data.pokemon[mon.species].name
local text = (Game.data.text._UsedCutText or "{RAM:wNameBuffer} hacked\naway with CUT!")
local text = (Game.data.text._UsedCutText or Strings("{RAM:wNameBuffer} hacked\naway with CUT!"))
:gsub("{RAM:wNameBuffer}", name)
Game.stack:push(TextBox.new(Game, text, function()
self.cutBlocks = self.cutBlocks or {}
@@ -2141,7 +2179,7 @@ function OverworldState:talkTo(npc)
-- the string "0" as truthy, so screen it out and fall through to text.
if d.item and d.item ~= "0" and d.item ~= 0 then
if not require("src.inventory.Bag").add(Game.save, d.item, 1) then
Game.stack:push(TextBox.new(Game, "You can't carry\nany more items!"))
Game.stack:push(TextBox.new(Game, Strings("You can't carry\nany more items!")))
return
end
Game.save.itemsTaken = Game.save.itemsTaken or {}
@@ -2157,7 +2195,7 @@ function OverworldState:talkTo(npc)
require("src.core.Sound").play(Game.data,
(ddef and ddef.keyItem) and "Get_Key_Item" or "Get_Item1")
Game.stack:push(TextBox.new(Game,
("%s found\n%s!"):format(Game.save.player.name, name)))
Strings("%s found\n%s!", Game.save.player.name, name)))
return
end
@@ -2166,7 +2204,7 @@ function OverworldState:talkTo(npc)
if d.pokemon then
npc:facePlayer(self.player)
local text = select(1, Game.data:resolveText(self.map.def.label, d.text))
or "Gyaoo!"
or Strings("Gyaoo!")
local BattleState = require("src.battle.BattleState")
Game.stack:push(TextBox.new(Game, text, function()
local battle = BattleState.newWild(Game, d.pokemon, d.level)
@@ -2209,7 +2247,7 @@ function OverworldState:talkTo(npc)
if entry then
if entry.mart then
npc:facePlayer(self.player)
Game.stack:push(TextBox.new(Game, "Hi there!\nMay I help you?", function()
Game.stack:push(TextBox.new(Game, Strings("Hi there!\nMay I help you?"), function()
Screens.push(Game, "ShopMenu", entry.mart)
unfreeze()
end))
@@ -2253,7 +2291,7 @@ function OverworldState:openPC(onDone)
-- when Bill hands over the SS Ticket)
local metBill = flags.EVENT_MET_BILL or flags.EVENT_GOT_SS_TICKET
table.insert(items, {
label = metBill and "BILL'S PC" or "SOMEONE'S PC",
label = metBill and "BILL'S PC" or Strings("SOMEONE'S PC"),
onSelect = function()
require("src.core.Sound").play(Game.data, "Enter_PC")
Screens.push(Game, "BoxMenu")
@@ -2273,7 +2311,7 @@ function OverworldState:openPC(onDone)
-- Prof. Oak's dex rating only appears once you have the Pokédex
if flags.EVENT_GOT_POKEDEX then
table.insert(items, {
label = "PROF.OAK's PC",
label = Strings("PROF.OAK's PC"),
onSelect = function()
self:dexRating()
done()
@@ -2293,7 +2331,7 @@ function OverworldState:openPC(onDone)
require("src.core.Sound").play(Game.data, "Turn_Off_PC")
done()
end
table.insert(items, { label = "LOG OFF", onSelect = logOff })
table.insert(items, { label = Strings("LOG OFF"), onSelect = logOff })
-- pokered sets BIT_NO_MENU_BUTTON_SOUND for the whole PC session
-- (engine/overworld/pokecenter_pc.asm / player_pc.asm); DisplayPCMainMenu
-- calls TextBoxBorder with c=14 (interior width, +2 for the border), so
@@ -2318,9 +2356,9 @@ function OverworldState:dexRating()
local lo = math.floor(owned / 10) * 10
key = ("_DexRatingText_Own%dTo%d"):format(lo, lo + 9)
end
local rating = Game.data.text[key] or "Keep it up!"
local rating = Game.data.text[key] or Strings("Keep it up!")
local completion = Game.data.text._DexCompletionText
or "POKéDEX comp-\nletion is:\f{NUM:hDexRatingNumMonsSeen} POKéMON seen\n{NUM:hDexRatingNumMonsOwned} POKéMON owned\fPROF.OAK's\nRating:"
or Strings("POKéDEX comp-\nletion is:\f{NUM:hDexRatingNumMonsSeen} POKéMON seen\n{NUM:hDexRatingNumMonsOwned} POKéMON owned\fPROF.OAK's\nRating:")
completion = completion
:gsub("{NUM:hDexRatingNumMonsSeen[^}]*}", tostring(seen))
:gsub("{NUM:hDexRatingNumMonsOwned[^}]*}", tostring(owned))
@@ -2367,20 +2405,20 @@ end
-- POKéMON" and "fighting fit".
function OverworldState:nurseHeal(onDone, npc)
local t = Game.data.text
local bye = t._PokemonCenterFarewellText or "We hope to see\nyou again!"
local bye = t._PokemonCenterFarewellText or Strings("We hope to see\nyou again!")
local hello = t._PokemonCenterWelcomeText
or "Welcome to our\nPOKéMON CENTER!"
or Strings("Welcome to our\nPOKéMON CENTER!")
if not Game.save.usedPokecenter then
Game.save.usedPokecenter = true -- BIT_USED_POKECENTER
hello = hello .. "\f"
.. (t._ShallWeHealYourPokemonText or "Shall we heal your\nPOKéMON?")
.. (t._ShallWeHealYourPokemonText or Strings("Shall we heal your\nPOKéMON?"))
end
Game.stack:push(TextBox.new(Game, hello, nil, { choice = function(yes)
if not yes then
Game.stack:push(TextBox.new(Game, bye, onDone))
return
end
local need = t._NeedYourPokemonText or "OK. We'll need\nyour POKéMON."
local need = t._NeedYourPokemonText or Strings("OK. We'll need\nyour POKéMON.")
Game.stack:push(TextBox.new(Game, need, function()
-- the nurse turns to the machine, the map music stops, and the
-- party heals before the machine runs (predef HealParty)
@@ -2415,7 +2453,7 @@ end
function OverworldState:finishNurseHeal(bye, onDone)
local t = Game.data.text
local fit = t._PokemonFightingFitText or "Your POKéMON are\nfighting fit!"
local fit = t._PokemonFightingFitText or Strings("Your POKéMON are\nfighting fit!")
Game.stack:push(TextBox.new(Game, fit .. "\f" .. bye, onDone))
end
@@ -2427,21 +2465,21 @@ end
-- for the original serial handshake; declining prints "Please come again!"
function OverworldState:cableClubReceptionist(onDone)
local t = Game.data.text
local welcome = t._CableClubNPCWelcomeText or "Welcome to the\nCable Club!"
local welcome = t._CableClubNPCWelcomeText or Strings("Welcome to the\nCable Club!")
if not Game.save.flags.EVENT_GOT_POKEDEX then
-- CableClubNPC .didNotConnect path before the pokedex
Game.stack:push(TextBox.new(Game, welcome .. "\f"
.. (t._CableClubNPCMakingPreparationsText
or "We're making\npreparations.\vPlease wait."), onDone))
or Strings("We're making\npreparations.\vPlease wait.")), onDone))
return
end
local apply = t._CableClubNPCPleaseApplyHereHaveToSaveText
or "Please apply here.\fBefore opening\nthe link, we have\vto save the game."
or Strings("Please apply here.\fBefore opening\nthe link, we have\vto save the game.")
Game.stack:push(TextBox.new(Game, welcome .. "\f" .. apply, nil,
{ choice = function(yes)
if not yes then
Game.stack:push(TextBox.new(Game,
t._CableClubNPCPleaseComeAgainText or "Please come\nagain!", onDone))
t._CableClubNPCPleaseComeAgainText or Strings("Please come\nagain!"), onDone))
return
end
Game:writeSave()
@@ -2476,7 +2514,7 @@ function OverworldState:engageTrainer(npc, onDone)
local battleText = header and header.battle and Game.data.text[header.battle]
if not battleText then
battleText = select(1, Game.data:resolveText(self.map.def.label, d.text))
or "I like shorts!\nThey're comfy and\neasy to wear!"
or Strings("I like shorts!\nThey're comfy and\neasy to wear!")
end
local wonText = header and header.won and Game.data.text[header.won]
@@ -2555,11 +2593,11 @@ function OverworldState:checkVictoryRewards(trainerClass, partyIndex)
if reward.badge then
local name = Game.data.items[reward.badge] and Game.data.items[reward.badge].name
or reward.badge
table.insert(lines, ("%s received\nthe %s!"):format(Game.save.player.name, name))
table.insert(lines, Strings("%s received\nthe %s!", Game.save.player.name, name))
end
if reward.item then
local name = Game.stringBuffer or reward.item
table.insert(lines, ("%s received\n%s!"):format(Game.save.player.name, name))
table.insert(lines, Strings("%s received\n%s!", Game.save.player.name, name))
end
end
if #lines > 0 then
@@ -2753,7 +2791,7 @@ function OverworldState:applyFieldPoison()
local queue = {}
for _, mon in ipairs(fainted) do
local name = mon.nickname or Game.data.pokemon[mon.species].name
table.insert(queue, ("%s\nfainted!"):format(name))
table.insert(queue, Strings("%s\nfainted!", name))
end
local alive = false
for _, mon in ipairs(save.party) do
@@ -2767,7 +2805,7 @@ function OverworldState:applyFieldPoison()
end
if not alive then
Game.stack:push(TextBox.new(Game,
("%s blacked\nout!"):format(save.player.name), function()
Strings("%s blacked\nout!", save.player.name), function()
local Pokemon = require("src.pokemon.Pokemon")
for _, mon in ipairs(save.party) do Pokemon.heal(mon) end
save.money = math.floor(save.money
@@ -2903,7 +2941,7 @@ function OverworldState:onStepComplete()
if Game.save.repelSteps == 0 then
-- no encounter on the exact wear-off step (wild_encounters.asm
-- .lastRepelStep returns CantEncounter)
Game.stack:push(TextBox.new(Game, "REPEL's effect\nwore off."))
Game.stack:push(TextBox.new(Game, Strings("REPEL's effect\nwore off.")))
return
end
end
@@ -3037,14 +3075,14 @@ function OverworldState:checkBadgeGate()
-- Route22GateGuardGoRightAheadText plays sound_get_item_1
require("src.core.Sound").play(Game.data, "Get_Item1")
Game.stack:push(TextBox.new(Game,
t["_" .. g.passText] or "Go right ahead!"))
t["_" .. g.passText] or Strings("Go right ahead!")))
end
return false
end
-- Route22GateGuardNoBoulderbadgeText plays SFX_DENIED
require("src.core.Sound").play(Game.data, "Denied")
Game.stack:push(TextBox.new(Game,
(t["_" .. g.failText] or "You don't have the\nBOULDERBADGE yet!")
(t["_" .. g.failText] or Strings("You don't have the\nBOULDERBADGE yet!"))
.. (t._Route22GateGuardICantLetYouPassText or ""), function()
self:scriptMove(p, "down", 1)
end))
@@ -3065,14 +3103,14 @@ function OverworldState:checkBadgeGate()
-- Route23OhThatIsTheBadgeText plays sound_get_item_1
require("src.core.Sound").play(Game.data, "Get_Item1")
local text = (t["_" .. g.passText] or
"Oh! That is the\n{RAM}!"):gsub("{RAM:wNameBuffer}", badgeName)
Strings("Oh! That is the\n{RAM}!")):gsub("{RAM:wNameBuffer}", badgeName)
Game.stack:push(TextBox.new(Game, text))
return false
end
-- Route23YouDontHaveTheBadgeYetText plays SFX_DENIED
require("src.core.Sound").play(Game.data, "Denied")
local text = (t["_" .. g.failText] or
"You don't have the\n{RAM} yet!"):gsub("{RAM:wNameBuffer}", badgeName)
Strings("You don't have the\n{RAM} yet!")):gsub("{RAM:wNameBuffer}", badgeName)
Game.stack:push(TextBox.new(Game, text, function()
self:scriptMove(p, "down", 1)
end))
@@ -3110,7 +3148,7 @@ function OverworldState:checkForcedMovement()
Game.save.forcedBike = true
require("src.core.Music").playMap(Game.data, self.map.id, true)
else
Game.stack:push(TextBox.new(Game, "You need a\nBICYCLE for the\nCycling Road!",
Game.stack:push(TextBox.new(Game, Strings("You need a\nBICYCLE for the\nCycling Road!"),
function()
local back = ({ up = "down", down = "up",
left = "right", right = "left" })[p.facing]
@@ -3238,7 +3276,7 @@ function OverworldState:boulderIntoHole(npc)
for i = #self.entities, 1, -1 do
if self.entities[i] == npc then table.remove(self.entities, i) end
end
Game.stack:push(TextBox.new(Game, "The boulder fell\nthrough the hole!"))
Game.stack:push(TextBox.new(Game, Strings("The boulder fell\nthrough the hole!")))
return true
end
end
@@ -3268,7 +3306,7 @@ function OverworldState:safariStep()
if not st or not self:inSafariStepZone() then return false end
st.steps = st.steps - 1
if st.steps > 0 then return false end
self:safariGameOver("PA: Ding-dong!\nTime's up!")
self:safariGameOver(Strings("PA: Ding-dong!\nTime's up!"))
return true
end
@@ -3277,7 +3315,7 @@ function OverworldState:safariGameOver(text)
Game.save.safari = nil
local t = Game.data.text
Game.stack:push(TextBox.new(Game,
(text or "") .. "\f" .. (t._GameOverText or "PA: Your SAFARI\nGAME is over!"),
(text or "") .. "\f" .. (t._GameOverText or Strings("PA: Your SAFARI\nGAME is over!")),
function()
local exit_ = FieldDefaults.fieldValue(Game.data, "safari", "exitWarp")
self:startWarpTo(exit_.map, exit_.x, exit_.y, exit_.facing or "down")
@@ -3320,7 +3358,7 @@ function OverworldState:afterBattle(result, battle)
else
-- throwing the last SAFARI BALL ends the game
if Game.save.safari and Game.save.safari.balls <= 0 then
self:safariGameOver("PA: You're out of\nSAFARI BALLs!")
self:safariGameOver(Strings("PA: You're out of\nSAFARI BALLs!"))
end
evolutions()
end