Commit Graph

475 Commits

Author SHA1 Message Date
bryanthaboi 1c13c4c0d6 Merge pull request #2398 from thibautbus/fix/game3-trainer-class-ids
Recognise FireRed trainer classes by id, not by their name
2026-09-22 20:12:50 -04:00
1jamie 63155055a4 fix(game3/battle): preserve foe.name and foe.nickname when constructing wild battle mon 2026-09-22 19:07:19 -05:00
1jamie 6a0e10ec66 fix(test): resolve test runner stubs, cache loaders, and thread yield handling 2026-09-22 18:53:24 -05:00
1jamie f2a75271b0 Merge upstream/dev into grandpas-garage and resolve switch_seq conflict 2026-09-22 18:41:25 -05:00
1jamie ce8a2d3777 Fix battle EXP distribution, party status badges, pacing trainer sight, and LeafGreen launcher colors 2026-09-22 18:32:26 -05:00
1jamie 249d321082 Implement Old Man battle tutorial and S.S. Anne departure cutscene with accurate pret timing and OAM layering 2026-09-22 17:20:31 -05:00
bryanthaboi d920f7acdb clean up 2026-09-22 17:05:12 -04:00
bryanthaboi 9208d83a28 Merge branch 'dev' into pr-2409
# Conflicts:
#	src/ui/game3/save_menu.lua
2026-09-22 16:41:00 -04:00
1jamie 109a87c81c fix(game3): fix BGM silence after fanfares during battle and overworld dialog
- Preserve active track voices in Player.stopAt after fast-forward catchup
- Add start-from-0 fallback in Player.stopAt when at timestamp predates the retained snaps ring
- Mute m4a worker during stopAt pause so it does not synthesize empty buffers while fanfare plays
- Ensure Audio.resumeBgm calls Source:play on bgmSource if stopped
2026-09-22 15:37:18 -05:00
bryanthaboi 23617f43b4 CLOSES #2290, CLOSES #2338, CLOSES #2186, CLOSES #2305, CLOSES #2332, CLOSES #2320, CLOSES #2344, CLOSES #2291, CLOSES #2335, CLOSES #2340, CLOSES #2385, CLOSES #2352, CLOSES #2378, CLOSES #2403, CLOSES #2404, CLOSES #2405, CLOSES #2367, CLOSES #2037, CLOSES #2177, CLOSES #2368, CLOSES #2382, CLOSES #1951
CLOSES #2290, CLOSES #2338, CLOSES #2186, CLOSES #2305, CLOSES #2332, CLOSES #2320, CLOSES #2344, CLOSES #2291, CLOSES #2335, CLOSES #2340, CLOSES #2385, CLOSES #2352, CLOSES #2378, CLOSES #2403, CLOSES #2404, CLOSES #2405, CLOSES #2367, CLOSES #2037, CLOSES #2177, CLOSES #2368, CLOSES #2382, CLOSES #1951
2026-09-22 16:27:28 -04:00
Shane McGovern a5014826cd fix(game3): multichoice cart-arity fallback and tint boundary coverage
The wave-3 tint batch as its own commit, so the harness fold before it stays
reviewable on its own.

multichoice.lua: Multichoice.COUNTS is the live option-count source for the
cart - 65 of 65 entries match pret's src/script_menu.c, where
ScriptMenu_MultichoiceGrid reads sMultichoiceLists[id].list and .count
(:915-930).  The table is load-bearing in a ROM-less run: with no import
cache present there is nothing else to ask how many options a list has.

adapters.lua: the multichoice operand reader no longer treats row[4] as an
option count.  pret asm/macros/event.inc:893-899 lays the command out as
x, y, multichoiceId, ignoreBPress - four bytes and no count field - so the
old read shifted the operand stream and every later decode in the command
went wrong with it.

tests/game3_photo_tint_boundary_test.lua: pins the cache-miss arity fallback
and the yield behaviour it rests on, which is the BUG2 root cause.  Run
twice green; the existing tint suite still passes 33/33 alongside it.
2026-09-22 19:24:52 +01:00
Shane McGovern 72f083f0c4 Merge remote-tracking branch 'upstream/dev' into nexhas28/gen3-review-v3-fixes
# Conflicts:
#	src/core/game3/save_schema_firered.lua
#	src/core/game3/scripting/natives.lua
#	src/import/RomExtractorGen3.lua
#	src/import/gba/versions.lua
2026-09-22 18:28:33 +01:00
Shane McGovern 41935aa2a2 fix(game3): wave 2 - 118 review fixes, seam work, and the gate to 282 suites
Second batch of the Gen 3 review work: the review lanes closed their queues
and the test gate grew with them.

Review census: 139 routed findings -- 118 fixed, 14 invalid (the report had
Gen 4+ semantics in more than one place), 6 struck as stale after
re-verification, 1 re-routed. Every fix carries its own pret citation in
docs/game3/review-v3-triage.md; the E10 opcode closure is itemised in
docs/game3/e10-opcode-spec.md (18 ops wired, 2 reclassified to the seam class).

Representative ROM-grounded changes:
- The POKéMON start-menu entry is now gated on FLAG_SYS_POKEMON_GET the way
  retail does it (pokefirered/src/start_menu.c:217-218, flag 0x828).
- Money ops read their amount raw and gate the change on the disable byte
  (pokefirered/src/scrcmd.c:1798-1830, pokefirered/asm/macros/event.inc:1166-1186);
  random and the warp family VarGet theirs
  (pokefirered/src/scrcmd.c:455-461, :719-731).
- The HM table matches FRLG: there is no Whirlpool HM
  (pokefirered/include/constants/items.h:411-418).
- The day-care party-full guard follows src/daycare.c:525, :1081.
- Knock Off keeps its battle-scoped send-out mask
  (pokefirered/src/battle_script_commands.c:2730-2752, :4489), carried from
  the first batch.

Structural work: the I6/I9/J1 architecture items landed as seams (profile
selector, capability flags, font provider, virtual-object layer), the adopted
footprint register keeps a KEEP verdict, the quantizer target is met, and the
30 drain items plus carves 1-4 are folded in.

Tests: T6 now runs 282 top-level suites (273 + 9 new), the re-sweep ends at
267 pass / 0 fail, the engine tier is 634 suites, modkit 37, and the full
gate ran green twice with T3 active on an imported Red cache. Documented
skips are the lua5.4 oversize-save oracle where lua5.4 is absent and the
config-partials listed in docs/game3/game3-artifact-conversions-v3.md.

Docs shipped: review-v3-triage.md, e10-opcode-spec.md, rse-seams.md,
game3-suite-sweep-v113.md, game3-artifact-conversions-v3.md,
test-baseline-v3.md (plus the first-batch docs already on the branch).
2026-09-22 18:23:06 +01:00
bryanthaboi 21c868b90a leafgreen 2026-09-22 09:56:36 -04:00
thibautbus 5ad701022b Pick the FireRed Elite Four and champion transitions by class id
BattleTransition.pickTrainer told the Elite Four and the champion by class
names ("ELITE_FOUR", "CHAMPION", "RIVAL") or by class ids 57 and 58, and
the Elite Four members by trainer ids 412-419. None of these is
FireRed's: TRAINER_CLASS_ELITE_FOUR is 87, TRAINER_CLASS_CHAMPION 90, and
Lorelei, Bruno, Agatha and Lance are trainers 410-413 (735-738 for the
rematch). The bridge also never passed the class, so these battles always
got the terrain transition. The bridge now hands over the foe's class id
and pickTrainer follows GetTrainerBattleTransition
(pokefirered/src/battle_setup.c:624): the Elite Four member's own
transition, BLUE for the champion, and none for the rival's class (a
caller's isRival flag still asks for BLUE).

A Trainer Tower or e-Reader foe carries a facility class instead, whose
numbers overlap the trainer classes (FACILITY_CLASS_LASS is 90, the
champion's class), and pret never picks their transition by class
(battle_setup.c:660), so pickTrainer ignores the class for them.
2026-09-22 15:28:54 +02:00
thibautbus fa0ab3621f Record FireRed gym, Elite Four and champion wins by class id
The quest log told a gym leader, Elite Four or champion win from the
trainer's class name ("LEADER", "ELITE FOUR", "CHAMPION"). With a
translated class every such win fell back to the plain trainer event, and
in French, where the gym LEADER class reads CHAMPION, every gym win would
be logged as a champion battle. pret switches on the class id
(pokefirered/src/quest_log_battle.c:25); the battle state now keeps the
trainer's class id next to its name, and the recorder reads it.
2026-09-22 15:20:31 +02:00
thibautbus 1c2a7d64de Give the FireRed rival and champion the rival's name by class id
Trainers.info gave a trainer the player's chosen rival name only when its
class was called "RIVAL". A mod that translates the class (RIVALE in
German and Italian) made the rival battle as the ROM's placeholder TERRY,
and the champion, whose class is CHAMPION, was always TERRY. pret's
B_TXT_TRAINER1_NAME names TRAINER_CLASS_RIVAL_EARLY, _RIVAL_LATE and
_CHAMPION by the rival's name (pokefirered/src/battle_message.c:2078);
Trainers.info now does the same, by class id.
2026-09-22 15:20:31 +02:00
bryanthaboi 351b7b3cbb Merge pull request #2396 from thibautbus/fix/game3-egg-nickname 2026-09-22 07:38:18 -04:00
bryanthaboi 35f02cc5d2 CLOSES #2394 2026-09-22 07:36:16 -04:00
Shane McGovern b5f2f4fcfd Merge remote-tracking branch 'upstream/dev' into nexhas28/gen3-review-v3-fixes
# Conflicts:
#	src/core/game3/battle/anim_seq.lua
#	src/core/game3/battle/effects/secondary.lua
#	src/core/game3/battle/engine.lua
#	src/core/game3/battle/state.lua
#	src/core/game3/battle/switch_seq.lua
#	tests/engine/game3_knock_off_item_test.lua
2026-09-22 12:25:30 +01:00
Shane McGovern e2454bf568 fix(game3): review v3 fixes (battle, field, script, save) + gated T6 tier
Second wave of fixes from the Gen 3 codebase review (210 findings), the
regressions the full-suite sweep surfaced, and a T6 tier so the top-level
tests/game3_*.lua suites run inside ./scripts/test.sh instead of outside it.

Battle: Knock Off and Thief/Trick item persistence follow the ROM, not the
review - the party slot is never written through during battle, and the
knocked-off mask suppresses the item on later send-outs
(pokefirered/src/battle_script_commands.c:2731 MOVE_EFFECT_KNOCK_OFF with the
STICKY_HOLD guard at :2732, opponent-steal guard at :2610-2622, mask use at
:4489). Rapid Spin keeps its one-per-use chain order (:8435-8474); Growl
keeps pret's target (src/data/battle_moves.h, include/battle.h:63).

Scripting: operand layouts match pret/asm/macros/event.inc - givemon 15 bytes
(:989-997), comparestat {B,W} (:1573-1576), setptr/loadbytefromptr/setptrbyte
take a word pointer (:118-137). 18 of the 20 E10 ops are wired per
src/scrcmd.c (per-op citations in docs/game3/e10-opcode-spec.md), money and
random and the warp family read their operands as VarGets
(src/scrcmd.c:1798-1830, :455-461, :719-731), and the day-care party-full
guard is in place (src/daycare.c:525,:1081).

Field and UI: fishing counts rounds as the ROM does
(src/field_player_avatar.c:1740-1765), an unresolved map section no longer
reports Pallet Town (src/region_map.c:3782), and a font provider sits behind
a FireRed capability/profile seam.

Save data: profile-driven Options.block, a save version round-trip, and slot
id validation that blocks path traversal (slotDiskPath("firered", "../evil")
returns nil).

Importer: the parallel import path now writes both completion markers,
object kind/clone bytes decode as pret defines them
(include/global.fieldmap.h:110-130), and the HM table drops Whirlpool
(include/constants/items.h:411-418).

Tests: six new scenario suites (battle_ai, capture, event, menu, move,
overworld) and eight engine suites (profile, capabilities, cache paths, font
provider, options block, save version round-trip, version dispatch,
versions_game). T6 runs every top-level game3 suite at GAME3_JOBS default 8
with per-suite logs, failure cause classification and a KNOWN_GAME3_FAILURES
ceiling of 23 as a shrinking guard.

Docs: the working notes under docs/game3 (triage ledger, pret citation audit,
e10 opcode spec, RSE seams, sweep v3/v113, test baseline, artifact
conversions, merge trial).

Full gate at this snapshot: exit 0, all tiers passed - engine 624/624,
T6 279/279 (0 known, 0 fresh failures), gen2 146/146, modkit 37/37, luacheck
clean, privacy gate 3/3 over a 9,711-file publication set.
2026-09-22 12:22:50 +01:00
thibautbus f8fbe53346 Show a FireRed egg as an egg on the PC panel, summary and trade screens
Three screens that show a single mon still drew an egg as the species it
will hatch into, and printed its stored nickname untranslated:

- The PC's hovered-mon panel drew the hatch species' front pic, the stored
  "EGG" nickname, "/PICHU", gender, level and held item. pret draws the
  SPECIES_OR_EGG pic and, for an egg, prints only gText_EggNickname with the
  other lines blank (pokemon_storage_system_data.c:1034, :1091).
- The summary's egg page drew the hatch species' front pic, where pret uses
  SPECIES_OR_EGG (pokemon_summary_screen.c:4016). It also recognised an egg by
  mon.isEgg alone; it now uses Pokemon.isEgg like the rest of the engine.
- The trade scene drew a traded egg as its species and named it by the stored
  nickname (trade_scene.c:757, :1239).

The script string buffers (BufferMonNickname and the other nicknameOf
helpers) now also give the language's EGG for an egg, as GetMonData
(MON_DATA_NICKNAME) does (pokemon.c:3020).
2026-09-22 12:10:44 +02:00
thibautbus aaacf02dc0 Draw a FireRed egg's menu icon as an egg
The party, the PC boxes and the release animation drew an egg with the icon
of the species it will hatch into, so a Pichu egg showed as a Pichu. pret's
party menu picks the icon from GetMonData(MON_DATA_SPECIES_OR_EGG), which is
SPECIES_EGG for any egg (pokefirered/src/pokemon.c:3245, party_menu.c:2655).

Pokemon.speciesOrEgg does the same, and the icon call sites use it. The
species pass only extracted icons up to NUM_SPECIES - 1, so the egg extractor
now also bakes gMonIconTable[SPECIES_EGG] as pokemon/icons/412.rgba. Cache
version 114 re-imports existing caches so the icon appears.
2026-09-22 12:10:44 +02:00
thibautbus ee63ffff2d Name a FireRed egg by the language's own EGG
Party.giveMon stores "EGG" as a new egg's nickname (party.lua for a
gift egg, breeding.lua for the Day-Care's), and the party slots and the
other screens that name a mon through Pokemon.displayName and
displayMonName print that nickname as it is. A translated game therefore
listed its eggs as EGG, while the summary, which prints Strings("EGG")
for an egg, already said OEUF, EI, HUEVO or UOVO.

The cart does not rely on the stored nickname either: its Day-Care
writes the Japanese タマゴ there (daycare.c:1100), and GetMonData
returns gText_EggNickname for any egg's nickname (pokemon.c:3020). Do the
same in both name helpers: an egg reads as Strings("EGG"), whatever its
nickname holds, which also covers the eggs saves already carry. A
hatched egg, whose nickname is cleared, goes back to its species name.
2026-09-22 12:10:24 +02:00
bryanthaboi e5569bd0a1 Dublin time 2026-09-22 05:34:03 -04:00
Shane McGovern 2c356f28ea fix(game3): save, item, map, script and importer bugs found in review
The rest of the Gen 3 review fixes, each with a gated suite in tests/engine/.
ROM semantics were checked against pret/pokefirered.

Scripts
- givemon carried the wrong operand layout, found earlier; four more layout
  desyncs came out of pret asm/macros/event.inc: comparestat is {byte,word},
  and setptr / loadbytefromptr / setptrbyte each carry a leading byte plus a
  word. A wrong size mis-decodes every instruction after the bad one, so
  Versions.CACHE_VERSION moves to 113 and existing caches re-import.
- handlers for previously handler-less verbs: comparestat,
  bufferitemnameplural, setmonmove, setmonmetlocation, the modern
  fateful-encounter pair, the script-locals family (copylocal, setptr,
  loadbytefromptr, setptrbyte, copybyte, compare_local_to_* and
  compare_ptr_to_*), the RAM-script family (setvaddress, vgoto, vcall,
  vgoto_if, vcall_if, vmessage, vbuffermessage, vbufferstring, endram,
  returnram) and the same-map forms of the *at verbs.
- setdooropen / setdoorclosed read their coordinates through VarGet.

Battles
- Knock Off and Thief / Trick persist the item change instead of only
  touching the in-battle copy.
- knocked-off party slots are tracked in a bitmask, so a slot reused later
  does not inherit the flag.

Field and UI
- Player.reset restores facing and clears the surf flags.
- a definition-less Map.load no longer leaves collision unbound.
- an unresolved region-map section no longer reports PALLET TOWN.
- the naming screen splits input from the timer, so update(dt) stops
  indexing a number.
- the hall of fame commits through the engine save path and serializes its
  fields.

Persistence
- gameStats, the link-battle records and the trainer card are serialized.
- the PC deposit refuses at the 999 cap instead of destroying the overflow.

Robustness
- Data.load runs cached modules sandboxed.
- the file browser quotes shell arguments.
- .meta dimensions are bounds-checked and mids.idx validates its header.
2026-09-22 03:28:39 +01:00
1jamie 31826b4d39 Fixed GBA OAM priority mapping and overhead layer partitioning for actors 2026-09-21 19:28:21 -05:00
1jamie a26cb67277 use Collision instance for elevation lookups 2026-09-21 18:19:17 -05:00
1jamie dffcaeb4c0 fix: use rawget for global bit library lookups and fix variable assignment in trainer fan club 2026-09-21 17:21:20 -05:00
1jamie eee6637842 Merge branch 'upstream/dev' into grandpas-garage 2026-09-21 17:12:25 -05:00
1jamie 6be2de2844 feat(game3): finished Gen 3 save editor, scripting specials, field elevation, and parallel ROM extraction
- Save Editor: Implement full Gen 3 FireRed save editing including PID/personality, IVs/EVs, natures, ability slots, gender, shiny toggles, held items, Pokédex flags, and event/trainer toggles.
- Scripting & Specials: Add Pokédex rating evaluations, Magikarp/Heracross size records, Trainer Fan Club tracking, and FireRed event flag mappings.
- Overworld & Rendering: Interleave metatile and actor draw calls by row bucket with OAM elevation priority sorting for bridges and overhead layers.
- ROM Extractor: Parallelize Gen 3 extraction across 4 concurrent love.thread workers with love.filesystem searchers for cross-platform support (Switch/Xbox/Mobile/Desktop).
- LZ77 Decompressor: Accelerate GBA LZ77 with preallocated FFI buffers and 0-based displacement indexing ensuring 100% byte-for-byte asset parity.
- Testing: Add unit test suites for LZ77, size records, Pokédex rating, Fan Club, elevation priority, Seafoam puzzles, and Gen 3 save editor operations.
2026-09-21 17:07:50 -05:00
Shane McGovern b5366c5671 Merge branch 'dev' into nexhas28-implement-deoxys-triangle-event
Only src/import/gba/versions.lua conflicted, and not on the value: both
branches had independently taken CACHE_VERSION 111 for unrelated cache
layouts.  dev's 111 is the hidden-item bitfield fix in
extract_map_events.lua (id % 256, qty % 128); this branch's 111 is the new
deoxys_rock_fragments field effect.  Sharing one number would let a cache
stamped by dev be treated as fresh, silently dropping the meteorite
shatter artwork, so the Deoxys artwork moves to 112.

src/core/game3/field_view.lua auto-merged: dev's Doors.draw viewport
arguments and this branch's bg_flash veil do not overlap.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-21 19:51:17 +01:00
bryanthaboi fe28fb8653 Merge pull request #2375 from thibautbus/fix/easy-chat-species-move-names
Let a translation reach the Easy Chat words
2026-09-21 14:45:32 -04:00
bryanthaboi bf7f2bee90 CLOSES #2364, CLOSES #2380, CLOSES #2366, CLOSES #2362 2026-09-21 14:43:32 -04:00
Shane McGovern 6d3af84fde fix(game3): make the Deoxys rock redden and its shards visible
Two Birth Island visuals were broken; both are now verified on screen.

1. The meteorite never reddened as the triangle puzzle progressed.
   OwSprites.recolour_sprite called Image:newImageData, which LOVE 11 does
   not have, so the pcall always failed and setObjectPalette silently
   returned false for all ten steps.  Keep the ImageData the sheet was
   decoded from in load_one and clone() it before mapPixel (which mutates
   in place); the override copy keeps its imageData in step too.

2. The shatter happened, but the shards were invisible.  The flash was a
   whole-screen Renderer.screenVeil painted after the composite, and the
   shards only live ~8 frames, so they were drawn underneath it.  pret
   (field_effect.c:3946) blends PALETTES_BG only -- BlendPalettes plus
   BeginNormalPaletteFade -- so the OBJ shards, which share the
   meteorite's palette tag 4371, keep their colours against a white map.
   Replace startWhiteFlash with startBgFlash, drawn by FieldView.draw
   between the last map layer and the actors.  Also match pret's shake
   cadence (flip every second frame) and its `++tTimer > 120` test.

Adds tests/drivers/game3_deoxys_visual.lua, which walks all eleven puzzle
positions, shatters the rock, and counts shard-coloured pixels in the
saved frames: 492 px at the shatter frame, 192 px a few frames later
(0 before this fix).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-21 19:39:12 +01:00
Shane McGovern 29d8492d4e fix(game3): redden the Deoxys rock and shatter it before Deoxys appears
Two Birth Island visuals were missing against pokefirered.

The rock never reddened as the puzzle progressed. pret loads
sDeoxysObjectPals[num] into the rock's OBJ palette (field_specials.c:2398);
the engine can only substitute colours into the already-baked sprite, and the
ramp it matched against held the .pal ASCII values (32/82/139) while the ROM
bakes 33/82/140, so almost every pixel missed the lookup. ROCK_PALS now holds
the 8-bit expansion of the ROM ramp at 0x3F6206 (verified byte-exact against
all 11 palettes), sourcePalette() supplies the pristine colours the meteorite
is baked with, and recolour_sprite falls back to the nearest source colour
within a small tolerance so a one-unit rounding difference can never silently
kill the swap again. A swap that matches nothing now returns nil and says so.

The rock vanished instead of shattering. FLDEFF_DESTROY_DEOXYS_ROCK already
ran the camera shake and thunder, but the fragment stage had no artwork and
moved the shards wrongly. Added the four 8x8 tiles from ROM 0x3CBDB0 with
palette 0x3F6346 (ramp step 10, the awakened red pret inherits from the rock);
decoding them through the field-effect extractor reproduces the original
graphics exactly, so no nibble handling was needed. The shards now all spawn
at the rock's own corner and travel +/-16 x / +/-12 y per frame with no
gravity (field_effect.c:3993), dropping out once they leave the viewport, while
the shake decays from amplitude 4 and the rock is removed.

Cache version 111 forces existing caches to re-import so the new sheet appears.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-21 19:23:16 +01:00
Shane McGovern 1cf9c5109c Implement DoDeoxysTriangleInteraction (Birth Island Deoxys event)
Ports pokefirered's Birth Island Deoxys puzzle and its battle in full.

Puzzle (src/core/game3/deoxys.lua), from src/field_specials.c:2319-2456:
- Task_DoDeoxysTriangleInteraction with the 11 rock positions, the 10
  per-step caps and the pre-reset step-counter read that pret uses.
- IncrementBirthIslandRockStepCount wired into StepEvents.onStepTaken.
- MoveDeoxysObject / SetDeoxysTrianglePalette palette swaps, realised via
  a new OwSprites.setObjectPalette colour substitution because palettes
  are baked to RGBA at extract time.
- FLDEFF_MOVE_DEOXYS_ROCK (67) and FLDEFF_DESTROY_DEOXYS_ROCK (68), plus
  setfieldeffectargument plumbing and the rock-move lerp / rock-shatter
  field effects.
- DoDeoxysTriangleInteraction = 0x1AB added to Std.SPECIAL, with real
  handlers for it and SetDeoxysTrianglePalette.

Battle: seteventmon SPECIES_DEOXYS, 30 expands to CreateEnemyEventMon,
which the engine already implements, so the encounter flows through
Encounters._pendingWild into StartLegendaryBattle. No new code needed
there; tests now pin the whole chain.

Fixes the Deoxys battle theme. Both battle-BGM sites gated on species
386, but FRLG's SPECIES_DEOXYS is 410 (species.h:419) and 386 is
SPECIES_VOLBEAT (species.h:395), so Deoxys silently fell through to the
generic wild theme. Extracted Audio.legendaryBattleSong as the single
source of truth, keyed by the FRLG internal species id, matching the
switch in src/battle_setup.c:349.

Verification: 24/24 Deoxys groups, game3_special_events_test,
engine tier 593/593, luacheck 0/0 in 970 files, plus the hand-run
game3 battle/field-effect/object/step suites.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-21 18:43:47 +01:00
thibautbus 709c7db286 Read the Easy Chat species and move words from the dataset
Four of the 22 Easy Chat groups carry no text of their own: POKéMON,
POKéMON (NAT), MOVE 1 and MOVE 2 are lists of species and move ids, and
pret prints them through gSpeciesNames and gMoveNames.  The extractor
resolves them the same way, so easy_chat_data.lua ended up holding 807
copies of names the dataset already has -- copies that go stale the
moment anything renames a species or a move.  A mod that renames them,
a translation filling the species_names and move_names catalogs first
of all, changed every screen but this one: the picker still offered
the cart's English names, and the profile read back in them.

Resolve those two kinds of word at draw time instead, against the same
Pokemon.name / Pokemon.moveName the party screen and the Pokédex use.
When the dataset has no entry for an id it answers with a placeholder
("POKéMON 063", "-------"), which is worse than the word the cart
shipped, so that case keeps the extracted text.  An entry that names
the group ("easyChat.POKéMON|PIKACHU") still wins over both, since a
species name may need a wording other screens do not use.  A bare-key
entry does not: bare keys are shared with menu labels this engine
already translates (CUT, FLASH, STRENGTH), and one of those should not
decide what a move is called here.

tools/modkit.py keeps emitting those 807 rows, since the species and
move catalogs it writes come from the Gen 1 dataset and do not list a
Hoenn species or a Gen 3 move; its note now says an empty entry shows
the game's own name, so a translator only fills the ones nothing else
renames.
2026-09-21 17:24:41 +02:00
thibautbus bfc2534a81 Let a translation reach the Easy Chat vocabulary
The Easy Chat screen picks its words from src/core/game3/easy_chat_data.lua, the ROM's own word tables as the extractor writes them: 1,813 English words across 22 groups, plus the group names the picker lists. It drew them straight from that table, so every word the player composes with stayed English, and the screen's own prompts -- routed through Strings() already -- sat in a frame of untranslated vocabulary.

src/core/game3/easy_chat_text.lua is the seam. The data module stays exactly what the extractor produced, because it is regenerated from the ROM and anything written into it would be lost on the next import; the words are translated where they are drawn instead, the same split the rest of game3 uses for extracted text. Five sites go through it: the group list, its header, the word list, the word sitting in a slot, and the saved profile a message box prints (ShowEasyChatMessage). The ids the save file stores are untouched.

Each word carries its group's context ("easyChat.FEELINGS"), because the same English word means different things in different groups and 155 of them collide with labels this engine already translates elsewhere (ATTACK, BAG, CANCEL, BACK...). Strings() falls back to the plain key, so a catalog that does not need the distinction lands with one entry -- with the consequence that a mod which already translates BAG for the menus now sees that wording in the Easy Chat list too, until it adds a context-specific entry.

A seam nothing can discover is not much use: tools/modkit.py's catalog generator emits these keys too. The literal harvester cannot see them (the picker looks each word up at draw time, not at a Strings("...") call site), so the dataset dump walks EasyChatData.GROUPS and writes 1,835 rows into lang/strings.lua, where a translator finds them beside the rest of the engine text.

Each word is clipped to the box it sits in: in the picker, the red selection rectangle; in the phrase frame, the slot's own frame. The cart's words fit those boxes (72 px at most); a longer translation is cut at the box edge instead of running past the highlight, into the next slot's cursor, or off screen. Group names get the room up to the scroll arrows instead, because an official translation already needs more than the rectangle: the French cart's VIE QUOTIDIEN. is 83 px.

tests/engine/game3_easy_chat_text_test.lua covers the context lookup, the fallback to a plain key, the untouched English with no catalog, and that the extracted data itself is never rewritten. It lives in tests/engine so the T1/T2 tier actually runs it.
2026-09-21 17:24:41 +02:00
bryanthaboi fdc9506256 another big dump 2026-09-21 11:14:17 -04:00
bryanthaboi 2945ed8987 firered: elevators, braille, alt layouts, on-frame scripts, fly and heal data, field item use 2026-09-21 05:39:03 -04:00
bryanthaboi 92b4fb9eb5 firered: forced movement, darkness, safari, rocket hideout, victory road, script ops 2026-09-20 21:17:57 -04:00
bryanthaboi 4d7350c776 Merge remote-tracking branch 'origin/dev' into HEAD 2026-09-20 17:49:32 -04:00
bryanthaboi 1af407498c firered softlocks, specials, cave encounters, sevii ferry 2026-09-20 17:31:31 -04:00
bryanthaboi 21947bd652 Merge pull request #2359 from 1Jamie/grandpas-garage 2026-09-20 13:32:30 -04:00
bryanthaboi 605654a086 CLOSES #2310 2026-09-20 13:21:16 -04:00
1jamie fc08349576 Fixes #2361 where:
1. The Cerulean City Policeman remained in front of the burgled house at `(30, 12)` even after receiving the S.S. Ticket from Bill.
2. Bill remained standing inside the cell separator teleporter door at `(3, 3)` upon returning to Sea Cottage after helping him.
2026-09-20 12:06:39 -05:00
1jamie 2d599700e3 fix(game3/battle): send out first usable pokemon when lead is fainted (closes #2358)
- State.new and Battle.start now default the initial active battler slot to the first usable (non-egg, HP > 0) Pokémon in the party instead of unconditionally selecting slot 1.
- In double battles, slots 0 and 2 correctly select the first and second conscious Pokémon.
- Add regression test in tests/game3_battle_fainted_lead_test.lua verifying single, double, and party-order behavior when the lead slot is fainted.
2026-09-20 11:50:33 -05:00
1jamie 4024b023f5 move _lastSentPal and _lastOpaque0 variables to file scope 2026-09-20 11:45:07 -05:00
1jamie c125abfcea fix(game3): fix viridian gym sliding door, npc jump movement, cachefs extraction, and battle performance
- Doors:
  - Fix Viridian City Gym and Dojo warp doors registering as wooden house doors; map metatile 0x15B to SlidingDouble animation and SE_SLIDING_DOOR (18).
  - Embed full FRLG metatile fallback lookup table and support CacheFs/Dataset.cache() for door textures and manifests.
- Scripting & Movement (Fixes #2356):
  - Add support for jump movement opcodes (jump_2_down, jump_in_place, etc.) for both player and NPC objects.
  - Implement scriptJump routines updating cell coords and triggering jump trajectory animations.
- CacheFS & GBA ROM Extraction:
  - Wire CacheFS persistence across storage, region map, battle AI/moves, items, pokedex chrome, multichoice, and easy chat extractors for Android portability.
  - Fix box storage chrome extraction palette/shadow colors and duplicate label bug.
  - Add region map chrome extraction and interactive Town Map rendering.
- Performance & Android Optimization:
  - Eliminate dynamic per-frame require/pcall lookups in battle render/update loops.
  - Pre-allocate particle sort comparator in AnimVm to avoid per-frame closures.
  - Cache palette shader uniform uploads and blend state in AnimPal/AnimVm.
  - Pool scratch color tables in FrlgFont to remove garbage generation during text token scanning.
  - Harden audio FFI caching and m4a worker initialization for sandboxed environments.
- Tests:
  - Add test suites for Viridian Gym doors, jump opcodes, storage chrome, and town map caching.
fixes #2356
fixes #2357
2026-09-20 11:41:16 -05:00