Commit Graph

1662 Commits

Author SHA1 Message Date
bryanthaboi b478c640c0 one one baby 2026-09-20 08:11:52 -04:00
bryanthaboi 7236afc11d Merge pull request #2351 from bryanthaboi/nexhas28-fix-yellow-badge-jingle-2339 2026-09-19 19:41:27 -04:00
bryanthaboi 73edcd24cf Merge pull request #2350 from Nexhas28/nexhas28-modkit-gen3check 2026-09-19 19:41:14 -04:00
bryanthaboi 96cb788cdf Merge pull request #2349 from Nexhas28/nexhas28-gen3-start-menu-coverage 2026-09-19 19:40:50 -04:00
bryanthaboi b8458c2a4f Merge pull request #2345 from Nexhas28/nexhas28-fix-portmaster-launcher-update 2026-09-19 19:40:09 -04:00
Shane McGovern f11b9c762f fix(audio): play the badge jingle the cartridge plays (#2339)
Badge lines are end-battle texts (SaveEndBattleTextPointers), so they are
printed while the battle sound engine is still loaded.  A text sound command
names an id rather than a sound, and that engine's SFX_Headers_2 gives those
ids different sounds: the id behind sound_get_item_1 / sound_level_up is
SFX_Level_Up there, and the id behind sound_get_key_item is SFX_Ball_Poof.
The port resolved the jingle by name, so it always played the overworld
reading (Get_Item1 / Get_Key_Item) -- the wrong jingle for receiving the
BOULDERBADGE, and the same collision for Cerulean, Saffron, Cinnabar and
Viridian in Red, Blue and Yellow alike.

Add victories.badgeSoundFor(), which answers with the jingle that is actually
audible for the badge hand-over:

  Red/Blue  Brock Level_Up, Misty Ball_Poof, Sabrina Ball_Poof,
            Blaine Ball_Poof, Giovanni Level_Up
  Yellow    the same, except Misty: CeruleanGym.asm drops the command from
            that line, so Yellow's Cascade Badge has no jingle

Vermilion, Celadon and Fuchsia carry no sound on their badge lines in any
version.  The raw `badgeSound` field keeps the overworld-engine name, which is
what the TM texts (printed in the overworld) genuinely need.

Tests: pin the literal per-version jingle and its page for all five badge
leaders in tests/engine/gym_badge_jingle_page_bug1982.lua (53 checks), and
have the #1606 and driver suites assert through badgeSoundFor.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-19 23:28:31 +01:00
Shane McGovern bd92b6cb34 feat(modkit): add gen3check for FireRed mods
There was no way to ask "will this mod load on FireRed?". `validate`, `lint`
and `pack` only reason about Gen 1, and `gen2check` refuses a Gen 3 mod at the
manifest gate. `gen3check` is the Gen 3 counterpart of `gen2check`:

    python3 tools/modkit.py gen3check mods/<id> [--notes] [--json] [--strict]

- Factor the shared compat analysis behind a `Generation` descriptor, with
  `GEN2`/`GEN3` module-level singletons holding everything that differs: the
  compat facade name, the generation's own directory (`gen2` vs `game3`), the
  legacy manifest flag, the screen-twin prefix, and the sibling spelling. Every
  compat helper now takes a trailing `gen`.
- MK400-MK410 stay shared by design -- they are the same questions asked of a
  different facade -- and the verdict line (`on gen 3: ...`) disambiguates.
- Gen 3 sibling spelling: `src/ui/game3/` is snake_case while
  `src/world/game3/WorldAPI.lua` is not, so MK403 tries both spellings and
  stops at the first sibling that exists. Only 8 Gen 1 modules are reachable
  this way (`BattleAPI`, `BagMenu`, `HallOfFame`, `IntroMovie`, `OptionRows`,
  `ShopMenu`, `SummaryMenu`, `TrainerCard`), so MK403 on Gen 3 is live but
  narrow.
- MK409's screen-twin half is switched off on Gen 3 -- there is no
  `Screens.GEN2_IDS` equivalent -- while its version-string half still runs,
  since a Gen 1 version id in a mod that declares only FireRed is a real bug.
- Rename `GEN2_IDS_DUMP` to `VERSION_IDS_DUMP`, now parameterized by
  generation.

Gen 2 behaviour is unchanged: `diff -r` of `gen2check --notes` and
`gen2check --notes --json` output over all 10 shipped mods, captured before and
after this change, is empty.

Tests: `tests/modkit/cases/gen3check.lua` grows from 504 to 542 checks. Every
fixture is derived from the engine at run time -- the coverage table,
`GEN1_ONLY_MODULES`, and the `game3/` directory listings -- so the suite cannot
drift away from the tables it is asserting against. It covers MK400's Gen 3
wording, MK402 naming `Gen3Compat` and never `Gen2Compat`, MK403 naming the
snake_case path, MK404 quoting the member, MK409's version-string half, MK410
counted once at file scope, the `--json` verdict shape, and that no adapted
module is ever MK402.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-19 23:14:59 +01:00
Shane McGovern 8fce7bcdc6 fix(gen3): ui.start_menu.items is raised on FireRed
Gen3Compat's src.ui.StartMenu coverage row claimed the
ui.start_menu.items hook is "not raised on FireRed yet". It is raised:
src/ui/game3/start_menu.lua calls it from StartMenu.show with
(game, entries), the same name and arity as src/ui/StartMenu.lua and
src/ui/gen2/StartMenu.lua.

The note is what a mod author reads when asking whether the hook exists
on this generation, so a stale one costs a mod the hook for no reason.

- Rewrite the note to describe the FireRed arm: a row is { id, label }
  and carries no onSelect to rewire, and a non-table return is dropped
  without the Logger.error the other two generations emit.
- List ENTRIES as backed and note it: the facade passes the module's
  list straight through, and show() rebuilds it on every open, so a mod
  has to act per open rather than once.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-19 23:08:57 +01:00
Shane McGovern 93e071bcaa Fix self-update on the PortMaster SBC build
The SBC and RG34XXSP ports hand LÖVE a source *directory*
(`love <dir>`), so `love.filesystem.isFused()` is false there and
`Boot.run` bailed on its first line.  The launcher still offered
"Update vX.Y.Z" and "Restart to update", so an update downloaded,
verified, and then was silently ignored forever.

- Boot.canUpdateInPlace() replaces the fused-only gate: a packaged
  build updates whether it is fused (AppImage, Flatpak game.love) or
  unpacked, and only a dev / source checkout (engine "0.0.0-dev") is
  excluded.  Fails closed when the host cannot be established.
  Prelaunch.updateAllowed now delegates to it, so the boot gate and the
  `--update` gate cannot disagree.
- Check.hostPort() reads the release-target marker, with
  POKEPORT_HANDHELD as a legacy fallback (the SBC launcher has always
  exported it, so packs predating this change still identify
  themselves).  The marker was read from the environment but never
  exported, so a full-package fallback on a handheld resolved to a
  desktop AppImage that cannot run there.
- A handheld now fetches its own package ("Download port update",
  reusing the worker's cross-platform download_full) instead of
  offering a URL it has no browser to open, and reports "Update package
  ready" once it is in the save directory for a manual re-extract.
- Launchers export POKEPORT_PORTMASTER / POKEPORT_RG34XXSP.

An in-place update only ever mounts a payload over the running source,
so relaxing the gate is less invasive than the fused path it joins.
A runtime bump still needs a full package, which the minShell gate
already reports as needs_full.

Tests: tests/engine/update_boot_host_gate.lua (new, 10 checks) pins the
gate; update_check_tests.lua pins both port asset names and hostPort().
scripts/test.sh --quick: all tiers passed; scripts/lint.sh: 0 errors.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-19 22:15:02 +01:00
bryanthaboi 2359287036 Merge pull request #2342 from thibautbus/fix/frlg-latin-glyphs 2026-09-19 16:44:44 -04:00
bryanthaboi 8949eaa3d6 Merge pull request #2341 from 1Jamie/grandpas-garage 2026-09-19 16:44:24 -04:00
1jamie 0ec205db1e fix: resolve PC animation state bugs and update map event bit extraction logic 2026-09-19 15:23:43 -05: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
bryanthaboi 1b068be9c9 Merge pull request #2333 from TheRealSolidusSnake/dev 2026-09-19 16:13:00 -04: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
Solidus Snake f6c96f3bc8 Fix tests
Just reupload to test fixes
2026-09-19 15:39:26 -04: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
bryanthaboi 641b0f3c37 Merge pull request #2329 from Nexhas28/nexhas28-fix-save-screen-map-names 2026-09-19 14:05:37 -04:00
bryanthaboi 60c1a631a7 Merge pull request #2288 from campavao/rfc-0021-item-survives-the-wire 2026-09-19 14:05:21 -04:00
Solidus Snake c2e71dc034 Fixed Android Import
Just updates RomImporter so asset importers like A Link to the Past work properly on Android.

The importer now uses Android's native file picker, stages imported files separately from the normal Pokémon ROM flow, and routes them back to the correct importer when the picker closes. 

The fallback file browser also no longer filters out .smc / .sfc files.
2026-09-19 12:26:52 -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 e64f6ff6f7 Merge pull request #2327 from Nexhas28/nexhas28-add-firered-bug-template 2026-09-19 08:44:44 -04:00
Shane McGovern 3e62090b4c Add FireRed to game options in bug report template
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-19 13:42:14 +01:00
bryanthaboi 4640814677 Merge pull request #2326 from Nexhas28/nexhas28-fix-levelup-stat-box-persists 2026-09-19 08:38:08 -04:00
bryanthaboi 3b156aadde Merge pull request #2321 from Nexhas28/nexhas28-fix-firered-nickname-mon-icon-scale 2026-09-19 08:37:39 -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 6326aaabbe ci: retrigger after transient Maven Central 403
The Android APK job failed resolving every artifact from
repo.maven.apache.org with 403 Forbidden, before any build work ran.
The same job passed in the four preceding runs on this repo, and the
Nexhas28 token has no re-run rights on the upstream repository, so this
empty commit is the only available way to retrigger the workflow.

No source changes.
2026-09-19 13:05:09 +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
bryanthaboi da6fe8877f Merge pull request #2304 from Nexhas28/nexhas28-gba-egg-moves 2026-09-19 07:17:18 -04:00
Shane McGovern 58dfc00538 Extract FRLG gEggMoves so Gen 3 mods see egg moves
The Gen 3 extractor emitted eggCycles/eggGroups but never egg moves, so the
gen3_dexnav hidden-mon roll was inert on FireRed: a hidden mon kept its
level-up moves and could never gain an egg move.

Decode the ROM's own gEggMoves table (pokefirered/src/data/pokemon/
egg_moves.h) into pokemon/egg_moves.lua and expose it the same way as
learnsets/tmhm:

- Versions: EGG_MOVES = 0x25EF0C plus the 20000 species offset, the 0xFFFF
  run terminator and a defensive per-species cap.  The table is one flat u16
  stream of `{ species + 20000, move..., 0xFFFF }` runs with no final
  terminator, so the first word that is neither a header nor a plausible move
  id ends the scan.
- PokemonExtract: extract_egg_moves + write_egg_moves_lua, written to the
  cache by run(), required by ready() so a stale cache re-extracts, and
  returned as pack.eggMoves.  FORMAT_VERSION 3 -> 4.
- Pokemon.eggMoves(species): runtime accessor with the same species coercion
  as Pokemon.learnset; nil for a species with no egg move.
- Schemas: eggMoves on monTables/monRecord/writeMon and the Gen 3 species
  field list, so a mod reads record.eggMoves as move ids and writes names back.
- Versions.CACHE_VERSION 98 -> 99 to force re-extraction.

The table is sparse: species without egg moves are absent rather than an
empty list, all the way from the ROM to the mod record.

Validated against a supported FireRed USA 1.0 dump: 165 species, 973 moves,
<=8 per species, Bulbasaur {113,130,219,204,80,345,320,174}, Mankey
{157,193,96,68,179,251,279,265} (no Toxic), Mew nil.

Tests: new tests/engine/game3_egg_moves.lua (31 checks ROM-free, 39 with a
ROM), gate_gen3_mod_api 734/734, engine tier 587/587, modkit tier 37/37,
lint --gate clean.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-19 10:45:52 +01:00
bryanthaboi c385cebd0c Merge pull request #2301 from Nexhas28/nexhas28-wild-encounter-grace-period 2026-09-19 05:34:32 -04:00
Shane McGovern ba3c8ad754 Merge remote-tracking branch 'origin/dev' into nexhas28-wild-encounter-grace-period
dev added the public Encounters.tableFor export (MapCatalog resolution,
prefix stripping, route normalization) at the same point in
src/core/game3/encounters.lua where this branch inserts the cooldown
block. The two changes are purely additive, so both are kept: dev's
export sits next to table_for where it belongs, followed by the
cooldown/rate-test block.

scripts/lint.sh auto-merged cleanly, keeping both dev's PATH export for
node_modules/.bin and ~/.luarocks/bin and this branch's luacheck --version
probe.

Re-verified after the merge that dev's RNG-parity commit does not shift
the encounter goldens: 88/88 cooldown checks still pass, including the
Route 1 4000-step counts (401 with the grace period vs 858 without).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-19 10:29:50 +01:00
bryanthaboi c26d1a54f6 Merge pull request #2300 from Nexhas28/fix-firered-house-wall-collision 2026-09-19 05:22:11 -04:00
bryanthaboi a24d59ad9e Merge pull request #2298 from 1Jamie/grandpas-garage 2026-09-19 05:22:02 -04:00
Shane McGovern 4f07872b86 Diagnose luacheck builds that cannot run under Lua 5.5
luacheck 1.2.0 is the newest release and predates Lua 5.5, which made
generic-for control variables read-only. Built against Lua 5.5 it dies
loading luacheck.standards -- before linting a single file -- so the T0
gate can never pass, and the failure surfaced only as a raw loader
traceback that looks like a repo defect.

Probe `luacheck --version` before the real run:

  - if it cannot run, print the captured error plus the Lua 5.4 install
    command and exit 2, matching the existing not-found path
  - if it runs but reports Lua 5.5, warn that 1.2.0 predates it and that
    results are suspect, then lint anyway

CI is unaffected: it installs luacheck against Ubuntu's Lua, not 5.5.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-19 09:58:37 +01:00
Shane McGovern deca93582c Port FireRed encounter rate test and banked failure rate
Completes the pret/pokefirered parity for wild encounter rate gating,
on top of the cooldown port in b84c2c15.

Ports DoWildEncounterRateTest as Encounters.encounterRate(): *16, bike
*80/100, banked failure rate +buff*16/200, flute, Cleanse Tag *2/3,
ability (Stench /2, Illuminate *2), then the 1600 clamp last -- pret's
exact order, which the modifier-order tests pin down.

Ports AddToWildEncounterRateBuff: a failed rate test (or a rate test
that passes but picks no slot) banks the area rate; an active Repel
zeroes the bank. Without this, encounters were still flat-rate rather
than ramping after near-misses.

Also ports the Mach/Acro bike *80/100 modifier.

End-to-end goldens on Route 1 (rate 21, 4000 steps) shift 397->401
with the cooldown and 843->858 without. That matches pret's steady
state analytically: a 6-step cooldown followed by p~0.112 as the bank
ramps gives a cycle of (1-0.94^6)/0.112+6 ~ 10.3 steps, ~10%/step.
The observed 401/4000 = 10.03% confirms it.

tests/game3_rng_test.lua now resets rate modifiers per replay, since
the banked rate makes sequences history-dependent; a map load resets
in-game.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-19 09:50:08 +01:00
Shane McGovern b84c2c1511 Port FireRed wild encounter grace period
Gen3 (FireRed) had no cooldown between wild battles, so every step on an
encounter tile rolled at the area's full rate -- Route 1 (~21%) landed a
battle on roughly one step in five. Gen2 already had this via
World:wildCooldownStep(); game3 never did.

Port pret/pokefirered wild_encounter.c:

- GetMapBaseEncounterCooldown -> Encounters.mapBaseCooldown: steps of
  immunity derived from the area's own rate (rate >= 80 -> none, < 10 ->
  8, else 8 - rate/10).
- HandleWildEncounterCooldown -> Encounters.handleCooldown: a soft floor,
  not a hard gate -- once the minimum elapses a 5%/step leak lets a battle
  through anyway. Includes the White/Black Flute, Cleanse Tag, Stench and
  Illuminate modifiers in pret's application order (Cleanse Tag before the
  ability mod, which changes the result).
- ResetEncounterRateModifiers -> Encounters.resetRateModifiers, wired to
  the two places pret resets: map load (Map.load, including seamless
  connection crossings) and battle start (BattleBridge.startWild). The
  latter is the bug-#1229 class -- scripted battles and fishing re-arm the
  grace period even though no step rolled.

Measured on Route 1 over 4000 steps: 843 encounters before, 397 after;
first encounter never lands before step 7.

Not ported (makes encounters more likely, so it does not affect the
reported symptom): the encounterRateBuff anti-frustration ramp and the
Mach/Acro bike rate modifier.

tests/engine/wild_encounter_cooldown.lua covers the cooldown golden table,
the counter/leak mechanics, all five modifiers, both re-arm paths and the
end-to-end step count.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-19 09:46:02 +01:00
Shane McGovern 89c7959c70 fix(game3): gate the warp-cell walkable repair on the metatile behavior
Collision.installWarps force-marked every map-header warp cell walkable. That
is right for the outdoor MB_WARP_DOOR tiles extract can leave solid, but it also
opened cells that only carry a dead warp event. PalletTown_PlayersHouse_1F has
one at (3,9): the wall tile directly left of the door mat, mid 26, coll 1,
behavior 0x00 MB_NORMAL. pret never lets the player stand there, so the warp
never fires; the forced COLL_DOOR let the player walk out of the house through
the wall and land in PalletTown.

The repair now fires only when the behavior is unreadable (tileset attrs stopped
before that mid, so Collision.behavior returns nil) or is a behavior pret would
actually warp on. Collision.isWarpMetatileBehavior covers 0x60-0x6F plus 0x71,
matching field_control_avatar.c: the arrow warps (TryArrowWarp), the directional
stair warps (IsDirectionalStairWarpMetatileBehavior), and everything
IsWarpMetatileBehavior accepts (cave door, ladder, fall warp, regular warp,
Lavaridge 1F warp, warp door, escalators, union-room warp).

Warp indexing is unchanged: a dead warp on a wall is still registered, only the
grid write is gated.

tests/engine/firered_house_wall_warp_bug2297.lua is ROM-free: it embeds the pret
13x10 map.bin grid and the `building` metatile attributes, translates them with
the real ScriptingCollision.fromCell, binds the real Versions.WARPS table, and
asserts the wall stays solid, the four warp cells still behave, the repair still
applies for unreadable attrs and live warp behaviors, and an exhaustive
0x00-0xFF table pins the predicate. It fails 6 checks against the pre-fix file.

luajit tests/run_engine.lua: 585/585 suites passed.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-19 09:19:26 +01:00
1jamie 18a30b83f0 fix(game3): add partial_trap_name helper and include search path in lint.sh 2026-09-19 01:37:32 -05: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
bryanthaboi d6d8ee2329 Merge pull request #2295 from 1Jamie/grandpas-garage 2026-09-18 21:54:47 -04:00
1jamie d95960b2f1 fixes undefined vars 2026-09-18 20:47:35 -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 78522b5c20 Merge pull request #2293 from 1Jamie/grandpas-garage
Grandpas garage
2026-09-18 17:27:41 -04:00
bryanthaboi 7279ce57c1 Update WorldAPI.lua 2026-09-18 17:23:21 -04:00