Commit Graph

36 Commits

Author SHA1 Message Date
bryanthaboi 9308439697 Merge pull request #2370 from bryanthaboi/nexhas28-fix-gen2-encounters-unknown-id
Close Gold's encounters id space so an unknown id fails the mod (#2369)
2026-09-22 10:25:51 -04:00
Shane McGovern e12cda633a Close Gold's encounters id space so an unknown id fails the mod
mod.content.encounters:patch on a Gold boot keys by encounter KIND
(encounters.grass.ROUTE_29), but the id space was open: a key the
catalog did not describe was treated as a mod's own data and merged
as-is.  A Gen 1 encounters mod ported unchanged writes the MAP where
Gold wants the kind, so the call was accepted, merged into
data.gen2Encounters.ROUTE_29 and read by nothing -- vanilla game, no
error, nothing in the Mod Manager (#2369).

That key cannot be a mod's own data the way an extra palette id can:
src/battle/gen2/Encounter.lua, src/core/gen2/Roamers.lua,
src/core/gen2/BugContest.lua and src/world/gen2/World.lua read this
table by name and the set of names is fixed, so an unknown id is a
write nothing reads.  Add an opt-in `keysClosed` shape slot (folded by
Schemas.shapeFor like the other gen2* slots) that Schemas.check honours
in its keys/keyValue branch, and set it on encounters' Gen 2 spec.  An
unknown id now names the ids that do exist instead of silently
no-opping; api 2 fails the mod, api 1 keeps the warning.

Also catalogue roamMons, which the extractor has always emitted
(RomExtractorGen2:readRoamMons) and Roamers.roster has always read, but
which was missing from gen2Keys.

Only encounters opts in: palettes, battle_anims and constants keep
their open id spaces, and Red's encounters id space is untouched.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-21 09:14:53 +01:00
thibautbus 29bbdfce43 Harden statusBlockedId, document the failed flag, cover the volatile clear
Three follow-ups on the commit below.

statusBlockedId read the status record's optional `id` field. A mod that overrides a status rather than patching it replaces the record outright, and `id` is optional in the registry (src/mods/Schemas.lua), so the field can be nil there and full paralysis would quietly stop clearing bide, thrash, charge and trapping. It now falls back on the key the record was looked up under.

`msgs.failed` is the only thing that suppresses a primary effect's success animation now, so it is no longer an internal detail: the move_effects registry documents it, and docs/modding/reference/registries.md is regenerated. That regeneration also picks up rows that had drifted from the schema before this branch (missText, trueColor, the sprite cell fields).

The parity suite gains the case the commit below changes the rules in -- PAR's own roll clearing the volatiles, and a flinch on a paralysed battler leaving them alone -- and its translated-label block now restores the shared Data.text entries through pcall, so a failing check there cannot leak "pioupiou zzz" into every later parity suite.
2026-09-20 12:03:54 +02: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 bdfac727aa bing bang bong 2026-09-18 15:42:32 -04:00
bryanthaboi e2114f7c85 importers / lttp example 2026-09-16 16:42:59 -04:00
bryanthaboi 8c8581ed0c CLOSES #2089, CLOSES #2198, CLOSES #2227, CLOSES #2246, CLOSES #2277, CLOSES #2278, CLOSES #2280, CLOSES #2281, CLOSES #2282, CLOSES #2283, CLOSES #2286 2026-09-16 09:45:20 -04:00
Code-Grub 4bd44050ec Give mon menu icons the trueColor opt-out
A full-colour icon pack renders white with purple blotches on any screen
that declares an SGB palette zone, and only in ADVANCED. Colour there is
applied after the frame is drawn: a shader reads each pixel's red channel
and swaps in one of four palette colours. That suits the game's own art,
which is already four greys. Full-colour art is not. A yellow body is
nearly all high red, so it clears 0.83 and comes back as colour 0, white in
every palette, while the darker pixels land in colour 2, which MEWMON
paints {115,33,165}.

Art that should be left alone says so with a trueColor flag and is re-blit
unshaded. Battle pics, trainer portraits and overworld sprites all carry
one. Menu icons had no way to. The icons record has no such field, and
Sprites.iconPath returned a path alone where Sprites.path and
Sprites.playerPic both return path, trueColor. Nowhere to put the flag and
nothing to read it, so the art always met the shader.

R.sprites is the precedent for allowing it. Overworld walkers are OBJ art
drawn through an OBJ palette, the same hardware class as a menu icon, and
they carry the flag already.

iconPath now takes the record's flag through opts and returns it, and a
pokemon.icon hook may set ctx.trueColor for art it substitutes, which is
the contract pokemon.sprite already has.

PartyMenu.drawIcon skips the OBP bake for flagged art and reports its
covering rect. The bake matters as much as the shader does: obpIcon is
itself a four-shade remap on the red channel, so it flattens full-colour
art before the zone pass ever runs. The flag overrides `name` rather than
being read beside it, because a hook can substitute full-colour art for a
species that resolved to a built-in class through icons.byDex, leaving name
set. The entry-shape split from #274 does not catch that case.

The reported rect is 16x16 for the two OAM-block branches and the file's
own size for single-frame art, matching what each branch actually draws.

No vanilla icon record sets the flag, so both rect buckets stay empty on a
vanilla boot and the zone lists stay exactly the ones the states returned.
2026-09-11 11:55:52 -04:00
bryanthaboi 50da140c02 Merge pull request #2056 from thibautbus/fix/complete-translation-coverage-v0241 2026-09-02 06:29:57 -04:00
bryanthaboi 700331cb8f CLOSES #2057, CLOSES #2058, CLOSES #2059, CLOSES #2060, CLOSES #2061, CLOSES #2062, CLOSES #2063, CLOSES #2064, CLOSES #2066, CLOSES #2067, CLOSES #2069, CLOSES #2070, CLOSES #2072 2026-09-01 19:03:13 -04:00
thibautbus 23aeda4871 Expose localized Gen2 content registries
GEN2_STATUS_IDS (psn/brn/frz/par/paralysis/slp -> Gen 2's own registry ids)
was declared verbatim in both PartyMenu.lua and SummaryMenu.lua; move it to
Status.GEN2_ID_ALIASES so a future status alias fix only has one copy to
update.

SummaryMenu.TYPE_NAMES was left behind after this branch switched its two
former internal uses to the shared TypeChart.displayName/DISPLAY_NAMES; it
has no remaining callers anywhere in src/ or tests/.
2026-09-01 21:52:42 +02:00
bryanthaboi 45742481bf CLOSES #2025, CLOSES #2029, CLOSES #2030, CLOSES #2031, CLOSES #2033, CLOSES #2034, CLOSES #2035, CLOSES #2038, CLOSES #2040, CLOSES #2041, CLOSES #2042, CLOSES #2043, CLOSES #2044, CLOSES #2045, CLOSES #2046, CLOSES #2048, CLOSES #2049, CLOSES #2050, CLOSES #2051, CLOSES #2052, CLOSES #2053, CLOSES #2054 2026-09-01 13:45:00 -04:00
bryanthaboi f4424ea4a0 rquesated hooks 2026-08-31 07:56:27 -04:00
bryanthaboi a9ce423bdc CLOSES #1275, CLOSES #1295, CLOSES #1384, CLOSES #1628, CLOSES #1677, CLOSES #1682, CLOSES #1691, CLOSES #1813, CLOSES #1822, CLOSES #1861, CLOSES #1867, CLOSES #1868, CLOSES #1869, CLOSES #1870, CLOSES #1871, CLOSES #1872, CLOSES #1874, CLOSES #1876, CLOSES #1878, CLOSES #1880, CLOSES #1881, CLOSES #1882, CLOSES #1884, CLOSES #1885, CLOSES #1886, CLOSES #1887, CLOSES #1888, CLOSES #1889, CLOSES #1890, CLOSES #1891, CLOSES #1892, CLOSES #1893, CLOSES #1894, CLOSES #1895, CLOSES #1896, CLOSES #1899, CLOSES #1900, CLOSES #1901
CLOSES #1275, CLOSES #1295, CLOSES #1384, CLOSES #1628, CLOSES #1677, CLOSES #1682, CLOSES #1691, CLOSES #1813, CLOSES #1822, CLOSES #1861, CLOSES #1867, CLOSES #1868, CLOSES #1869, CLOSES #1870, CLOSES #1871, CLOSES #1872, CLOSES #1874, CLOSES #1876, CLOSES #1878, CLOSES #1880, CLOSES #1881, CLOSES #1882, CLOSES #1884, CLOSES #1885, CLOSES #1886, CLOSES #1887, CLOSES #1888, CLOSES #1889, CLOSES #1890, CLOSES #1891, CLOSES #1892, CLOSES #1893, CLOSES #1894, CLOSES #1895, CLOSES #1896, CLOSES #1899, CLOSES #1900, CLOSES #1901
2026-08-28 12:15:36 -04:00
thibautbus 273350d11e Route the Gen2 #DEX screen's kind/text through the pokemon registry
src/ui/gen2/PokedexMenu.lua reads its KIND label and both description
pages from data.gen2Pokedex.entries, loaded straight from disk before
mods:load runs -- a separate table from data.pokemon, the `pokemon`
registry's own merge target. mod.content.pokemon:patch(id, { dexEntry =
... }) therefore validated but never reached the screen. Adds
src/core/gen2/PokedexText.lua to project a patched dexEntry onto the
#DEX table after the merge (Game2:load, alongside the other Gen 2
post-merge registries), and a text2 field to the dexEntry schema for
the entry's second description page, which the screen already reads
but the registry had no field for. Also routes the OPTION/SEARCH panel
titles (PokedexMenu.lua) through Strings(), the same literal-wrapping
pattern already used elsewhere in this screen and its siblings.
2026-08-26 19:43:53 +02:00
MaxTomahawk 38d515f547 fix(mod-api): reserve generated metadata ids 2026-08-24 10:10:23 +02:00
MaxTomahawk 7e069df740 fix(mod-api): validate dataset roots lazily 2026-08-24 09:57:57 +02:00
1jamie 286988a1e3 fixes #1537 #1528
also fixes feet layering of gold with grass. Unifies gen1/gen2 grass layering into a single system
2026-08-18 15:37:27 -05:00
sanjinpepic 455ff21aff Validate a map object's pokemon field against the pokemon registry
R.maps.objects was f.opt(f.list(f.any)): a static wild encounter's
species (OverworldController.lua's d.pokemon, handed straight to
BattleState.newWild) went completely unchecked at load time, unlike an
encounter slot's species. A typo'd or removed id sat in a loaded mod
and only surfaced as a crash the moment a player reached that object.

Objects share one array across every kind -- NPCs, signs, warps and
static encounters all coexist with no field the loader could use to
tell them apart ahead of time -- so a strict f.rec covering the whole
shape would reject every kind this schema does not enumerate. Added
f.partial, an open counterpart to f.rec: it type-checks (and, through
collectRefs, cross-reference-checks) only the fields it is given and
leaves everything else on the value alone, the same extensibility
f.rec already grants at a record's top level but nowhere further in.
R.maps.objects now types just `pokemon` through it, so a bad species
id is a load-time "unresolved reference" error instead of a runtime
crash, while an NPC object's sprite/movement/range/... fields -- never
named in this schema -- still pass through untouched.
2026-08-16 22:48:33 +02:00
sanjinpepic 82ae667611 Update the crossValidate comment for growth_rates / evolution_methods
The comment above the gatedFor skip in Schemas.crossValidate still
described growth_rates and evolution_methods as unconfirmable Gen 2
namespaces, the way they were before each got a real Gen 2 id space:
growth_rates keeps its Gen 1 target and is seeded from the extractor's
data.pokemon.growthRates (src/battle/gen2/Mon.lua), and
evolution_methods routes to gen2EvolutionMethods, a fixed literal set
(src/core/gen2/Evolution.lua) that exists with or without a ROM
import. Schemas.GEN2 does not gate either name -- gate_gen2_mod_api.lua
pins that directly, including a case that a bad evolution method on a
Gold species is still caught -- so the two are validated like any other
reference today, not skipped.  Nothing here changes that behavior;
only the comment, which was describing an earlier state of the code,
is corrected.
2026-08-16 22:48:33 +02:00
bryanthaboi c8f6c7241b i did it for greg 2026-08-14 17:07:06 -04:00
bryanthaboi ae6cac89e1 G2 support 2026-08-11 11:53:30 -04:00
Thomas Armstrong 81f18e244d Support variable-size anchored overworld sprites 2026-08-09 04:10:39 -04:00
Shane McGovern ed8a89c5ce Honor trainer battleTheme override (fixes #945)
trainers.battleTheme validated and merged onto the trainer record but was
never read: battle music came solely from data.audio.battle[kind] where
kind is computeMusicKind()'s final/gym/trainer/wild.  Route both battle-
theme start sites through a single choke point:

- BattleState:playBattleTheme() cues Music.playBattle with the override
  (self.trainer.battleTheme via battleTheme()), defaulting to the kind
  when unset, so vanilla fights and #782's non-gym Giovanni are unchanged.
- BattleState:enter() and OverworldController:pushBattle() both call it.
- Music.playBattle gains an optional 4th song arg that overrides the kind
  default, and real call sites now populate the music.select trainerId.
- Victory jingles stay kind-based: a custom battle theme has no derivable
  win-variant.

New ROM-free T2 suite tests/engine/trainer_battle_theme_bug945.lua covers
mod load, override resolution, the choke point, and the nil-override
parity gate.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-07 10:53:42 +01:00
bryanthaboi 8fbe819493 bingus dingus 2026-08-04 10:32:51 -04:00
bryanthaboi 33998aab5d add ttf tiles option
lets a translation keep chosen characters on the rom tiles instead of the ttf.
needed for japanese: sizing the font for kana makes latin narrower, which
knocks the party menu numbers out of line.
2026-08-04 09:11:05 -04:00
bryanthaboi 3a6557ffe2 CLOSES #779 , CLOSES #743 + new font 2026-08-04 06:41:34 -04:00
bryanthaboi 9326b07583 CLOSES #415, CLOSES #484, CLOSES #488, CLOSES #492, CLOSES #497, CLOSES #541, CLOSES #559, CLOSES #562, CLOSES #563, CLOSES #564, CLOSES #565, CLOSES #566, CLOSES #567, CLOSES #568, CLOSES #569, CLOSES #570, CLOSES #571, CLOSES #572 2026-08-01 08:10:20 -04:00
bryanthaboi c62f1334bc merge main into dev 2026-07-30 11:35:57 -04:00
Cyberboy 350402b179 i18n: route hardcoded UI strings through Strings(), auto-size menu boxes, optional metric dex fields
Makes several UI spots reachable for translation mods without forking the
engine. All behavior-neutral for the base game: Strings(x) is the identity
function when no catalog is loaded, and the metric dex fields are opt-in.

- ListMenu: draw the title via Strings() (covers bag/PC/box/fly/move lists)
- Menu / (see follow-up ChoiceBox): grow the box to the widest label so longer
  localized labels don't overflow the frame
- OptionsMenu: wrap toggle values (ON/OFF, SET/SHIFT, WIDE/OG, text speed) and
  the ruleset display name in Strings()
- Schemas: add optional dexEntry.heightM / weightKg
- DexEntryMenu: render metric height/weight when those fields are present,
  otherwise unchanged (ft/in + lb)
2026-07-30 15:03:35 +02:00
johnjohto 7aa06942be Support custom trainer palettes and base portraits 2026-07-29 23:59:59 -04:00
bryanthaboi 52e3a0d23f update for tiled stuff 2026-07-28 20:32:29 -04:00
bryanthaboi f0a88ea473 Bug squashing and translation mods (#311)
* audio timing stuff

* bug fixes and translation additions

* translation stuff

* Update modkit.py

* better asset resolution
2026-07-27 13:37:05 -04:00
bryanthaboi 3069b2e2a9 The new experience (#201)
* new launcher and save converts and pipeline

* fixing bugs
2026-07-25 12:36:53 -04:00
bryanthaboi 8278b209b4 Squadhing som bug (#130)
* squashy squash

* squassshheee

CLOSES #128
CLOSES #114
CLOSES #113
CLOSES #111
CLOSES #110
CLOSES #109
CLOSES #107
CLOSES #106
CLOSES #105
CLOSES #90
CLOSES #103

* brock guy fix

CLOSES #39
2026-07-23 16:55:03 -04:00
bryanthaboi 47923d95b3 big ass modding update 2026-07-19 16:18:18 -04:00