Commit Graph

372 Commits

Author SHA1 Message Date
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
Shane McGovern a5f55d3498 fix(game3): stop the save dialog reporting a write that failed
do_save() discarded both pcall results and set _phase = "saved" unconditionally,
so a refused or failed save still showed "<player> saved the game." and closed the
menu. Only a truthy saveGame now reports success; a raised error, an explicit
false, or a missing saveGame enters a new save_failed phase, logs the reason via
Logger, and shows "The game could not be saved."

Test: tests/engine/game3_save_menu_failure_test.lua (8/10 fail on the previous
code, 10/10 pass here).
2026-09-22 01:41:53 +01:00
bryanthaboi c17a8f0d45 Merge pull request #2365 from thibautbus/fix/easy-chat-word-page-rows
Draw the Easy Chat word page's last two words
2026-09-21 14:46:58 -04: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 7da44937a3 Merge pull request #2376 from thibautbus/fix/frlg-summary-move-name-width
Give FireRed summary move names the room pret gives them
2026-09-21 14:45:25 -04: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
thibautbus 35727406dc Give FireRed summary move names the room pret gives them
The summary's MOVES page drew each move name with a fixed 64 px limit.
pret prints the names at x 3 of POKESUM_WIN_MOVES_3, a 10-tile window
that starts at tile 20 (pokemon_summary_screen.c:857, :2543), so a name
has up to the right edge of the screen: 77 px from the pen at 163.  The
cart's own longest names are 72 px -- SKY UPPERCUT, FRENZY PLANT, HYDRO
CANNON -- and lost their last letters, as did translated names that the
cart's window would hold (RUGISSEMENT for GROWL).

Clip to the window's right edge instead, measured from wherever the
chrome manifest puts the pen.
2026-09-21 17:09:45 +02: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
thibautbus 823dd07727 Draw the Easy Chat word page's last two words
The word picker pages eight words at a time and its navigation moves through four rows of two: `row < 3` when moving down, `3 * 2 + col + 1` when stepping up into the previous page. The draw loop rendered three rows, so the seventh and eighth word of every page were selectable -- the cursor sat on them, and pressing A picked them -- while nothing was drawn there. A group's last words could be chosen only blind.

Eight is the cart's own page: easy_chat_2.c scrolls selectWordRowsAbove by four, and easy_chat_3.c's PrintECRowsWin2 prints row * 16 + 96 for each row. The loop now draws four, starting 2 px higher so the fourth row's 14 px of glyphs stop exactly at the bottom of the 78 px frame.

tests/engine/game3_easy_chat_page_test.lua drives the screen with a group larger than one page and checks all eight words reach the font, inside the frame. It reports the two missing words before this change.
2026-09-20 21:22:28 +02:00
bryanthaboi 66f117b064 Merge pull request #2355 from thibautbus/fix/gen2-contest-coins-strings 2026-09-20 13:33:17 -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 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
bryanthaboi bb098734ec Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	src/core/game3/battle/effects/secondary.lua
#	src/core/game3/battle/init.lua
#	src/core/game3/battle/ui.lua
#	src/ui/game3/bag_menu.lua
#	src/ui/game3/berry_pouch.lua
#	src/ui/game3/party_menu.lua
#	src/ui/game3/pokedex.lua
#	src/ui/game3/shop_menu.lua
2026-09-20 11:25:03 -04:00
bryanthaboi 0d5cf4f9e1 CLOSES #2303, CLOSES #2306, CLOSES #2307, CLOSES #2309, CLOSES #2312, CLOSES #2317 2026-09-20 11:22:57 -04:00
thibautbus 58624c3013 Route the Bug Contest status box through Strings()
The box above the menu during a Bug-Catching Contest (pokecrystal ContestStatus) drew its three labels and the empty-slot placeholder as bare literals: CAUGHT, LEVEL, BALLS: and "None". They were the last raw player-facing sinks in this file, which tests/gen2_ui_translation_harvest_test.py already lists among the modules that should have none.

The values keep the cart's own columns (x=8, 7, 8) only while the label fits them: "ATTRAPÉ" fills the seven tiles CAUGHT leaves, and "GEFANGEN" or "ATRAPADO" one more, so each value starts after its label instead of being drawn over by it.

"None" carries a context: the bare key is also the import wizard's "no limit" label (src/import/online/Wizard.lua), and one catalog entry cannot serve both -- which is also why the harvest gate asks for the three labels and not for it.

The caught mon's own name is left alone: it is a nickname, or a species the `pokemon` registry renames.
2026-09-20 14:34:38 +02:00
thibautbus 06d99eb2aa Route the two-row confirmation prompts through Strings()
The Bug-Catching Contest confirmation was drawn as two bare literals, one per row, with no hook a mod could reach. It becomes one catalog key, "Would you like to\nend the Contest?", the way every other confirmation prompt in this port is written (src/ui/StartMenu.lua, src/ui/gen2/SaveMenu.lua): "Would you like to" alone has no stable translation out of context, and the cart writes it as one text (data/text/common_2.asm).

Both prompts in this file now share one helper. It draws with Chrome.printWrapped, which wraps on the translated line break AND on pixel width, and reads the line count back from it to warn -- once per prompt -- when a translation needs a third row the box cannot hold. The title-screen prompt went through a hand-rolled splitter on the first line break, which handed Font.draw an embedded break, rendered as a space: a translation needing three rows printed two of them joined and ran past the box.

The official wordings fit: "Voulez-vous arrê- / ter le concours?" and "Möchtest du den / Wettkampf beenden?".

tests/engine/gen2_contest_coins_translation_test.lua drives StartMenu's confirmContest arm and StartGameCornerGame's no-coins branch with a mod-loaded catalog and checks the translated wording reaches the screen, plus the English case. On dev the three prompt checks fail; the refusal's own check passes there too, because that lookup already ran at runtime -- what dev was missing is the key, not the call.
2026-09-20 14:34:19 +02:00
thibautbus 9bcc78e7f4 Translate the Easy Chat screen's own prompts and buttons
The Easy Chat editor (profile, questionnaire, battle-start message) drew all of its own text as literals: the screen's title and its two instruction lines, the "is as shown. Okay?" confirmation, the quit and delete-all confirmations, YES/NO, "Select a group." and the DEL. ALL / CANCEL / OK footer.

The per-screen strings are chosen when the screen opens, so they go through Strings() there. FOOTER_BTNS is a module-level table built before any catalog exists, so its labels are Strings.source and the draw loop looks each one up, the same shape as ItemsData.POCKET_LABEL; tests/game3_strings_module_tables_test.lua covers it.

The words the player picks are not touched: they come from the ROM's own Easy Chat word groups.

The title is still centred the way the cart centres it, but no longer starts left of the ribbon: the longest English title fills 118 of the header's 128 px, so a longer translation would have spilled out of the blue fill.
2026-09-20 11:55:05 +02:00
thibautbus 180eda1b4b Keep the FireRed save window's values clear of translated labels
The save window draws its stat values at a fixed x, 56 px into the window, which pret places for the English labels. The translated labels are wider in several languages ("DUREE JEU", "SPIELZEIT", "TIEMPO J."), and the time then prints over its label.

The value column now starts past the widest label, with the English gap between them. English labels leave it where pret has it.
2026-09-19 23:05:01 +02:00
thibautbus d7f5895e84 Translate FireRed place names in the map popup and forest preview
The popup shown on entering an area takes its text from MapSectionsExtract.SECTIONS, the cart's English section names, with the floor suffix map_name_popup.c adds (" 3F", " B1F", " ROOFTOP") glued on in English. Nothing in a translation mod could reach either.

getInfo now also returns the name before the suffix (baseName), and the popup passes it through Strings() and translates the floor as a whole label ("3F", "B1F", "ROOFTOP"): the European carts number floors differently (3F is "2E" in French, "2S" in German), so a template around the number could not say it. The forest preview's name window (MapPreviewScreen), which shows the ROM's English section name, and the summary's met-location fallback go through Strings() too.
2026-09-19 23:05:01 +02:00
thibautbus 357e84ead5 Translate FireRed ability names where they are shown
Species, move, item and trainer names reach a translation mod through registries, but nothing renames abilities: battle messages ("<POKéMON>'s <ability> prevents ...") and the summary page printed the ROM's English name whatever the language.

Abilities.name and the summary's ability line now pass the name through Strings(), keyed by the cart's English name ("SPEED BOOST", "COMPOUNDEYES"). The identifiers the battle code compares (SPEED_BOOST) and the description lookup are unchanged.
2026-09-19 23:04:31 +02:00
thibautbus d74a96f645 Route FireRed intro, Pokédex, map and option text through Strings()
The new-game intro (controls guide, Pikachu pages, Oak's speech, name choices), the naming screen titles, the title menu, Pokédex headers and labels, the Town Map names and dungeon descriptions, option values and hints, the Help System bar, the Hall of Fame and the Trainer Card were plain literals.

The intro text tables stay English sources and are passed to Strings() when shown. Option values go through Strings() with the option key as context ("option.battleStyle"), so a mod can tell the SHIFT battle style apart from other uses of the word.
2026-09-19 23:04:31 +02:00
thibautbus 85b6b96d61 Route FireRed menu text through Strings()
The bag, TM case, Berry Pouch, party, PC, box storage, shop, summary, save and yes/no menus drew their labels, prompts and result messages as literals, so a translation mod could not reach them.

Messages built at call time go through Strings() with whole-sentence templates. Action lists the menus compare against ("CANCEL", "EXIT", "SWITCH BOX"…) keep their English identifiers and are translated where they are drawn, as are the module-level PC labels, descriptions and TEXT_* strings, now marked with Strings.source(). Default box names are stored in English ("BOX 3") and shown translated, while names the player typed are drawn as they are.
2026-09-19 23:04:31 +02:00
bryanthaboi 2359287036 Merge pull request #2342 from thibautbus/fix/frlg-latin-glyphs 2026-09-19 16:44:44 -04:00
1jamie 12245c9679 Merge remote-tracking branch 'upstream/dev' into grandpas-garage
# Conflicts:
#	src/import/gba/extract_island1.lua
#	src/import/gba/map_sections_extract.lua
#	src/import/gba/region_map_extract.lua
#	src/ui/game3/save_menu.lua
2026-09-19 15:16:08 -05:00
1jamie c93f035359 fix(game3): hidden items, itemfinder, battle hud caught marker, moveset learnsets, and easy chat
- Hidden Items & Itemfinder (fixes #2314):
  * Extract BG_EVENT_HIDDEN_ITEM (kind 7) from GBA ROM with proper flag calculation (0x3E8 + id), underfoot flags, and quantities.
  * Implement overworld Itemfinder scan logic (7-tile radius, directional response, underfoot detection, SFX 65, and OEM text).
  * Wire up Bag Menu Itemfinder dispatch to overworld field runtime.

- Opponent Battle HUD Caught Marker (fixes #2313):
  * Add authentic Pokédex caught Poké Ball icon (B_INTERFACE_GFX_BALL_CAUGHT / tile 70) to opponent healthbox.
  * Match pokefirered parity in TryAddPokeballIconToHealthbox (wild encounters only, non-tutorial, non-ghost, checked against Dex).
  * Ensure status ailment icons (PSN, PAR, SLP, FRZ, BRN) take rendering priority over the caught ball marker.

- Level-Up Learnset & Movepool Assignment (fixes #2315):
  * Fix wild and generated Pokémon moveset assignment to assign the 4 most recent level-up moves up to the current level instead of getting stuck on basic moves (e.g. Tackle).

- Easy Chat / Profile Quick Chat:
  * Extract full word groups, categories, and dictionary entries from ROM.
  * Build Easy Chat interface with group selection, paginated word lists, and alphabetized filtering.
  * Fix dialog text overflowing when exiting profile / quick chat prompts.

- Nurse Joy & PC Storage / Map Polishing:
  * Implement Nurse Joy healing tray animation flow and audio cues in standard scripts.
  * Fix PC storage box header/wallpaper rendering and storage data extraction.
  * Correct Sevii Islands / Island 1 map section lookups and region map coordinate handling.
  * Fix circular dependency in battle residual handlers (partialTrap).

fixes #2325
fixes #2322
fixes #2315
fixes #2314
fixes #2313
2026-09-19 15:12:28 -05:00
bryanthaboi 65a1fcda25 Merge pull request #2331 from Nexhas28/nexhas28-fix-battle-item-stale-party-hp 2026-09-19 14:06:45 -04:00
bryanthaboi 6c21d25230 Merge pull request #2330 from Nexhas28/nexhas28-map-preview-rom-extraction 2026-09-19 14:06:27 -04:00
thibautbus a5faea0994 Draw the FireRed ROM font's European letters instead of blanks
FrlgFont.glyphId builds its character-to-glyph map from TextIR.CHARMAP, the decode table for US FireRed text, which only lists the accented letters US text prints (é, É). Every other letter fell back to glyph 0x00, so a French, German, Spanish or Italian string drawn by game3 lost à, ç, è, ê, ä, ö, ü, ß, ñ, ¿, ¡ and the rest to blank cells.

The US ROM font already draws the whole Latin block of pret's charmap, in both latin_normal and latin_small, at the charmap's own positions. FrlgFont now maps those characters too (FrlgFont.LATIN_GLYPHS). TextIR.CHARMAP is left as is, so decoding ROM text and extracted caches are unchanged, and every US character keeps its glyph.
2026-09-19 16:02:14 +02:00
Shane McGovern 2d12217d10 fix(game3): use live battle party for in-battle item use (#2316)
The bag's party-select screen read session.party, which battle_bridge only
writes back when the battle ends. Mid-battle it therefore showed pre-battle
HP and refused heals that would in fact work ("It won't have any effect.").

- Add PartyView.live(session): flushes the active player battlers into
  st.playerParty and returns it, falling back to session.party outside
  battle.
- bag_menu: read the live party, and stop re-mapping the party slot. The
  battleOrder wrapper in PartyMenu.show already yields a real slot, so the
  second map healed the wrong mon.
- Extract the battle hand-off into BagMenu.battleUse and route the Berry
  Pouch's USE through it. The pouch fell back to ItemUse.useField, which
  also only knows session.party, so a berry was eaten with no effect.
- Add tests/game3_battle_item_party_test.lua (7 blocks).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-19 14:58:46 +01:00
Shane McGovern 38a7e03f13 Show retail location previews for every FireRed map section
Most map sections never showed a Location Preview because the preview
artwork and the region-map GUIDE text were hand-authored placeholders:
only a handful of sections had art and none of it came from the ROM.

Read the real tables out of the dump instead.

Extraction (new):
- src/import/gba/region_map_tables.lua locates and validates
  sMapPreviewScreenData. The pinned offset from versions.lua is tried
  first and a bounded +/-0x8000 scan is only a guard. Validation is
  structural (mapsec in 88..196, type 0/1, three in-range ROM pointers,
  and the shipped invariant that the palette block is the first 0x40
  bytes of the tile block) so all 28 entries must hold.
- src/import/gba/map_preview_extract.lua bakes 21 deduplicated 240x160
  RGBA artworks (28 entries reuse art: Pattern Bush and the six Tanoby
  chambers), 109 mapsec names verified against sRegionMapSectionIdToName,
  and the 19 sDungeonInfo entries. Palette banks 13/14; bank-0 tilemap
  refs only occur in off-screen padding columns.
- src/import/gba/bg_bake.lua holds the shared 4bpp BG baking helpers;
  berry_pouch_extract.lua is refactored onto them (-94 lines).

Runtime:
- src/ui/game3/map_preview_screen.lua ports
  Task_RunMapPreviewScreenForest: 120-frame first-visit hold, 40-frame
  revisit, 48-frame fade-out, artwork and name window composited into one
  canvas so they fade together. Only MPS_TYPE_FOREST (8 sections) takes
  over the screen; the 20 MPS_TYPE_CAVE sections only change the warp
  fade colour, matching pret.
- src/core/game3/map.lua gives a changed section with a forest preview
  precedence over the map name popup (overworld.c state 12).
- src/core/game3/warp.lua routes every fade through warpFadeModes,
  applying WarpFadeOutScreen/WarpFadeInScreen: a section change into a
  cave-preview map forces FADE_TO_BLACK, otherwise MapTransitionIsEnter/
  IsExit decide white vs black on MAP_TYPE_UNDERGROUND.
- setworldmapflag feeds the transient visit flag; the HUD ticks and
  dismisses the screen; the dataset installs the cache on hydrate.
- region_map.lua's GUIDE panel uses the retail geometry, tint ramp and
  delayed text, and ROM names/dungeon text overlay the hand-authored
  fallbacks via ensureGenerated().

Two retail quirks are tolerated: MPS_ROCKET_WAREHOUSE and Berry Forest
share flagId 2231 (as do the six Tanoby chambers), and the Tanoby mapsec
is spelled MAPSEC_DILFORD_CHAMBER.

CACHE_VERSION 99 -> 100.

Verified: tests/game3_map_preview_extract_test.lua (new; ROM-gated, all
28 entries, 8 forest / 20 cave, 21 artworks, 109 names, 19 dungeons, the
shared flag, Pattern Bush art reuse), tests/game3_town_map_test.lua,
tests/run_engine.lua 587/587, scripts/test.sh --quick all tiers,
luacheck src 0 warnings / 0 errors.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-19 14:21:30 +01:00
Shane McGovern 171a2c21f7 fix(save): show the place name, not the internal map id, on the save screen
The location header drew `session.mapName or session.map`, and nothing in
src/ ever assigns session.mapName, so the fallback always won and the
screen printed the engine's internal id ("FR_ROUTE_22").

pret prints the sMapNames place name resolved from the map header:
  start_menu.c PrintSaveStats -> SAVE_STAT_LOCATION
  save_menu_util.c GetMapNameGeneric(dest, gMapHeader.regionMapSectionId)
  region_map.c GetMapName(dst, mapsec, 0)
and centres it in the 14-tile stats window. Do the same via the existing
MapSectionsExtract.getInfo() lookup, and centre the run instead of
drawing it at a fixed x.

getInfo() also gets two fixes on the path the save screen now depends on:
- the fuzzy id match picked whichever section pairs() happened to visit
  first, so "ROUTE_22" could resolve to "ROUTE 2". Prefer the exact match,
  else the longest.
- return `resolved` so callers can tell a real section from the Pallet
  Town placeholder the function falls back to for unknown maps.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-19 14:04:06 +01:00
bryanthaboi 4640814677 Merge pull request #2326 from Nexhas28/nexhas28-fix-levelup-stat-box-persists 2026-09-19 08:38:08 -04:00
Shane McGovern 7ec1d9be0c fix(game3): gate the level-up stat window on the EXP sequence (#2324)
The FRLG level-up stat window opened and the sequence kept pumping, so the
box outlived its own step: it stopped taking input (routing is phase-gated),
never closed, and drew over the win/money text.

- exp_seq: update() now waits while the window is open (mirroring the
  already-correct busy()), and finish()/reset() tear the window down silently
  so a stale onDone cannot advance a sequence that no longer owns it.
- stat_growth: close(opts) gains opts.silent for teardown callers.
- init: the input-routing phase list moves into STAT_WINDOW_PHASES behind
  Battle.statWindowPhase(), and Battle.update() tears down a window whose
  phase can no longer dismiss it.
- ui: the stat-window draw keys off the same predicate, so input and drawing
  can never disagree.

Tests: the "In-Battle Level Up Stat Growth Window" block in
tests/game3_battle_switch_and_faint_test.lua now pumps ExpSeq.update() while
the box is open, and a new CI-covered tests/engine suite pins the same
regression.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-19 13:26:02 +01:00
Shane McGovern 7e505fdf81 Keep the nickname screen's title inside its text box
Follow-up to #2319, found while testing it: the catch flow hand-wrote
"YOUR POKEMON'S NICKNAME?" into the text-entry window. That string is
141px wide, but sWindowTemplates[WIN_TEXT_ENTRY_BOX] only gives 127px
from x=73, so the title ran ~12px over the frame's right edge.

pret composes the title instead -- DrawMonTextEntryBox prints
gSpeciesNames[mon] followed by gText_PkmnsNickname ("'s nickname?"),
which fits for every species (worst case 123px).

- Naming.monTitle() is now the single source of that string, used by
  both the catch flow and special:158.
- Naming.draw clamps the title to L.titleMaxW, mirroring the GBA's
  per-window glyph clip, so an over-long name can never escape the frame
  (the default 240px maxWidth is wider than the box itself).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-19 13:02:22 +01:00
Shane McGovern def3f68f8a Draw the Gen 3 nickname mon icon at its real 32x32 size
The mon branch of drawPlayerIcon reused the player overworld sprite's
16x32 box to letterbox the icon, so a 32x32 Pokemon.icon came out at
scale 0.5 -- half size inside the frame baked into bg.png. The 64x64
frontPic fallback was worse still, at 0.25.

Give the mon its own box in the layout table (pret naming_screen.c:1422
CreateMonIcon(species, SpriteCallbackDummy, 56, 40) draws it unscaled on
that centre), so the icon fills the frame 1:1 and the fallback shrinks to
fit the same box. Also pin the frame to quads[0]: pret's dummy callback
leaves the icon on its first frame, not the 1px-shifted second pose.

The player/rival overworld slot keeps its 16x32 box untouched.

Fixes #2319

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-19 12:51:48 +01:00
1jamie aed3bbced7 fix(game3): battle animation timing, RNG parity, pokedex habitats, multichoice UI and overworld events
- Battle Animation Pipeline:
  * Eliminate 90-frame audio polling stall in anim_vm end/waitsound opcodes,
    allowing attack animations to transition instantly into target hit flicker
    and HP bar drain.
  * Correct visual task counting in AnimVm:visualCount to match pret gAnimVisualTaskCount.
  * Signal bg task completion on restorebg (vm.args[7] = -1).
  * Fix termination conditions on shake tasks (ShakeMon, ShakeMon2, ShakeBattleTerrain),
    WaterSport droplet arcs, and StartSinAnimTimer.
  * Handle RGB555 color args in start_blend_anim_sprite_color and route P.destroyTask
    to AnimTasks.destroy.

- RNG System & Seeding Parity:
  * Replace math.random in Party.giveMon with Rng.Random32() for personality values
    and Rng.Random() for IV bitfield extraction (HP/Atk/Def/Spe/SpA/SpD) matching GBA format.
  * Seed RNG on Title Screen A/Start press via Rng.seedNewGame() (matching SeedRngAndSetTrainerId)
    and perturb RNG state on title menu inputs.
  * Route NPC overworld idle/wander timers and wild encounter slot rolls through Rng.compat.

- Pokédex Habitat & Area Maps:
  * Fix encounter tables extraction and map indexing to resolve Pokemon habitat
    locations across Kanto/Sevii maps rather than showing Area Unknown.
  * Render steady semi-transparent red overlays for Pokédex area map locations.

- Multichoice Menus & UI:
  * Implement multichoice table extraction and data population for Viridian blackboard
    status ailments and special dialog interactions.
  * Improve choice box borders, pagination, and Town Map wall viewing.

- Overworld Events & Flags:
  * Fix Route 1 entrance girl NPC spawning and script triggers after starter selection.
  * Ensure consistent save reload state in Oak's Lab.
2026-09-19 01:30:46 -05:00
1jamie 03db72b70e Fixed evolution soft lock
fixed keyboard special keys highlighting
fixed audio flow for evolution
fixed town map
fixed pause menu not pausing tick
2026-09-18 20:44:18 -05:00
bryanthaboi 661d75ef1c junk 2026-09-18 16:49:48 -04:00
bryanthaboi bdfac727aa bing bang bong 2026-09-18 15:42:32 -04:00
bryanthaboi 96d23ab93b battle stuff 2026-09-18 13:11:33 -04:00
bryanthaboi 9e9a54cbb4 oaky baby 2026-09-18 09:12:33 -04:00
bryanthaboi c86cf40e73 perfected intro babbyyyyy 2026-09-18 08:28:29 -04:00
bryanthaboi c8bfac84f9 havok fixes and what not 2026-09-17 12:26:56 -04:00
bryanthaboi dfc0b05436 namign screen fix 2026-09-17 11:07:33 -04:00
bryanthaboi dbe0c5c9f0 all the fun features 2026-09-17 10:51:09 -04:00