Commit Graph

1542 Commits

Author SHA1 Message Date
Claude 5920402de4 Gen 2: a cart saves into its own scope, not the base game's
A cart's saves are keyed by cart id, not by version.  SaveData resolves
every path through activeScopeKey, which answers cart_<id> while one is
active, and the launcher lists, creates and selects a cart's slots from
the cartSlots registry (RomImporter._refreshSlots / _selectSlot /
_newSlot).

src/core/gen2/Save.lua asked in the version's name alone.  saveNames
built saves/<version>/<slot>.lua or save_<suffix>.lua from the version in
both branches and never consulted the active cart, so a cart on Gold,
Silver or Crystal read and wrote the BASE GAME's playthrough.  Gen 1 was
unaffected because it saves through SaveData itself, which is already
cart-scoped -- so this only showed on a Gen 2 cart.

It was worse than sharing one file.  Save.save opens by asking
activeSlot(version) and, on nil, calling createSlot + setActiveSlot in
the version's name, so the first save inside a cart registered a slot in
the base game's registry and made it active: the cart's playthrough
appeared in the launcher's list for the base version, and the player's
own save there was what the cart then overwrote.

Both sites now resolve the cart scope the way SaveData does, and the
names they build are SaveData's own -- slotDir's saves/cart_<id>/ and
legacyNames' save_cart_<id>.lua -- so the in-game save layer and the
launcher land on one file again.

tests/gen2_save_test.lua covers the cart's flat name, its slot name, the
slot going into the cart's registry rather than the base game's, the
cart's scope winning over a base slot, and the base game keeping its own
once the cart is cleared.  Four of them fail on the unpatched module.

Reported as "when I select Wild Crystal to launch it loads my save from
regular Crystal".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 18:36:17 +00:00
bryanthaboi fdd1d61ea6 yellow residuals 2026-09-05 09:03:47 -04:00
bryanthaboi 4420f14cbd CLOSES #2185, CLOSES #2188, CLOSES #2189, CLOSES #2190, CLOSES #2191, CLOSES #2192, CLOSES #2193, CLOSES #2194, CLOSES #2195, CLOSES #2196, CLOSES #2197 2026-09-05 08:55:59 -04:00
bryanthaboi 469ba593a6 textbox fix for intro fade out 2026-09-04 10:48:56 -04:00
bryanthaboi 1863af2bac gsc: the battle HUD frame fades with the rest of the screen 2026-09-04 10:45:08 -04:00
bryanthaboi 9ae53ac81f gsc: intro palette rotations, music fade and player icon, CLOSES #2171 2026-09-04 10:45:07 -04:00
bryanthaboi e6e3459dbe yellow: Bill's house Pikachu timing, collision and spawn cell, CLOSES #2165 2026-09-04 10:45:02 -04:00
bryanthaboi e9cf07ef47 gsc: map-load white hold measured on hardware, CLOSES #2141 2026-09-04 10:45:02 -04:00
bryanthaboi aa997eeb51 online fixerino 2026-09-04 09:56:35 -04:00
bryanthaboi 8d62d80f1c yellow: a parked Pikachu blocks the player in every follow-disabled scene 2026-09-04 09:41:20 -04:00
bryanthaboi f30ecb484e Merge branch 'dev' of https://github.com/bryanthaboi/gen1recomp into dev 2026-09-04 09:34:16 -04:00
bryanthaboi 16d684297e battle: status moves print the cart's refusal lines 2026-09-04 09:25:34 -04:00
bryanthaboi 77f8c0ca7e battle: a refused status effect keeps its animation cancelled 2026-09-04 09:20:36 -04:00
bryanthaboi 7f1336a830 CLOSES #2114, CLOSES #2115, CLOSES #2116, CLOSES #2117, CLOSES #2118, CLOSES #2124, CLOSES #2125, CLOSES #2126, CLOSES #2148, CLOSES #2149, CLOSES #2150, CLOSES #2151, CLOSES #2152, CLOSES #2153, CLOSES #2155, CLOSES #2156, CLOSES #2158, CLOSES #2159, CLOSES #2161, CLOSES #2162, CLOSES #2163, CLOSES #2164, CLOSES #2170, CLOSES #2173, CLOSES #2174, CLOSES #2175, CLOSES #2176, CLOSES #2178, CLOSES #2179, CLOSES #2180, CLOSES #2181, CLOSES #2182 2026-09-04 09:15:16 -04:00
bryanthaboi ff2ea95eb9 Merge pull request #2160 from thibautbus/fix/gen2-move-learn-and-item-effect-strings 2026-09-03 16:51:27 -04:00
bryanthaboi bf39f7a3da Merge pull request #2154 from 1Jamie/android-fixes 2026-09-03 16:51:10 -04:00
1jamie 3cce66d497 Implement Android image export functionality and enhance save data management. Added exportImageToGallery method for saving images to the public Pictures directory on Android. Updated Printer and SaveData modules to support this feature, ensuring proper slot resolution and user feedback. Improved touch handling in the launcher to prevent unintended actions after exiting games. 2026-09-03 13:17:59 -05:00
thibautbus fa4e96e879 Route Battle Tower Menu messages through Strings()
BattleTowerMenu:drawPanel() shows self.message via Chrome.printWrapped
directly, with no Strings() lookup of its own -- same shape as
Game2:say()/TextBox.new(), which also expect their caller to have
already resolved the text. Every assignment to self.message in this
file used the raw Strings.source() return value (an identity/harvest
marker, not a lookup) instead: the level-picker prompt, the "quit your
challenge?" confirmation, the level-cap refusal, and the Uber-clause
refusal (which additionally called Lua's own string.format() on the
untranslated source instead of Strings(UBER_TEXT, name)). The YES/NO
confirmation labels and the CANCEL row label had the identical bug one
level down: declared with Strings.source() but printed via
Chrome.print() with no Strings() call around them either. The whole
Battle Tower level-picker menu, including its yes/no prompt, stayed in
English regardless of any translation catalog a mod supplied.

Found via a systematic trace of every TextBox.new() (143) and
Chrome.print()/Chrome.printWrapped() call site in the engine back to
its text source, cross-checked against the real tools/modkit.py
harvester and an independent review of the branch. That same review
also flagged src/world/OverworldController.lua:3314
(Strings.source("%s's PC"):format(playerName)) as the same bug shape,
and an earlier revision of this branch "fixed" it to
Strings("%s's PC", playerName) -- but that call site is not a bug:
openPC() deliberately builds every PC row's label from a stable
English Strings.source() value first, so the ui.pc.items mod hook can
match/reorder rows by their vanilla English text, and only translates
via a second translatedLabels[playerPcLabel] pass after the hook has
run (openPC()'s own comment: "Hooks identify the vanilla rows by their
English source labels. Delay localization until after ui.pc.items has
inspected/reordered/replaced them"). Translating playerPcLabel early
broke that contract -- confirmed by tests/engine/rby_translation_runtime_test.lua,
which already covered this exact case and failed with
"ui.pc.items sees the stable player-PC source label (got PC DE RED,
want RED's PC)" once that change landed. Reverted; the label was
already being translated correctly by the existing second pass, which
this branch never needed to touch.

A second independent review round, after the fixes above, found one
more real gap in the same family: ItemEffects.lua's VITAMIN_LABEL
table (folded into the previous commit once found), and confirmed
everything else on the branch clean.

Verified with tools/modkit.py's harvest_engine_strings that all seven
BattleTowerMenu.lua literals are still discovered from their
Strings.source() declarations, luajit tests/run_gen2.lua: 144/145 (the
one unrelated pre-existing failure noted in the previous commit), and
luajit tests/run_engine.lua: 533/533 (including the PC-label
regression test above, now passing).
2026-09-03 19:11:36 +02:00
thibautbus 9401445c0f Route Gen 2 move-learning and item-effect text through Strings()
learnMoveOn (Game2.lua) built every move-learning message -- level-up
learn, TM/HM teach, the "trying to learn X, forget a move?" flow, "stop
learning?", the HM-can't-be-forgotten refusal, "which move should be
forgotten?", and the "1, 2 and... forgot X, learned Y!" replace result
-- with a bare string literal or a direct :format() call, bypassing
Strings() entirely (src/core/Strings.lua). Game2:say()/TextBox.new()
show whatever text they're handed with no lookup of their own, so this
meant the entire move-learning UI stayed in English no matter what
translation catalog a mod supplied -- confirmed against a real
gen1recomp-translation-mods build, which has translated overrides for
several of these keys that could never apply because the literal never
reached the catalog. Same bug for the TM/HM teach refusals ("X can't
learn Y!"/"X already knows Y!").

src/core/gen2/ItemEffects.lua had the same bug across its whole family:
every item-usage message (Potion/heal, status cure, Revive, Rare Candy,
Vitamin, PP restore/PP Up, "no effect"/"can't use on an EGG"/"can't use
on this #MON") was either a bare table constant or built with :format()
directly, none of it routed through Strings(). Its constants are now
wrapped in Strings.source() at declaration (so a mod's catalog harvest
still finds them, per the pattern already documented in Strings.lua)
and looked up through Strings() at each use site. The Vitamin success
message's own VITAMIN_LABEL table (HEALTH/ATTACK/DEFENSE/SPEED/SPECIAL)
gets the same two-part fix -- each entry wrapped in Strings.source() at
declaration, and Strings(VITAMIN_LABEL[stat]) at the one use site --
matching the identical stat-name tables in MoveEffects.lua/TrainerAI.lua/
gen2/Effects.lua/ContestMenu.lua/SummaryMenu.lua, which all already do
this; without it a Vitamin's stat name would stay in English mid-sentence
even inside an otherwise fully translated message. Two more call sites
read ItemEffects.TEXT_NO_EFFECT raw outside that file and needed the
same fix: PartyMenu.lua's Softboiled-no-target refusal, and
BattleState.lua's X-item-reused-with-no-effect and
BitterBerry-when-not-confused refusals -- both battle-side messages
where every neighboring self.message assignment already correctly
wraps in Strings(), which is what made these two stand out as missed.

None of this changes vanilla (no mod loaded) output: Strings.get() is
an identity function with no catalog active, so every message renders
byte-identical to before. Verified with the headless Gen 2 suite
(luajit tests/run_gen2.lua): 144/145, the one failure
(gen2_fishing_time_test.lua) reproduces identically on dev before this
change and is unrelated (fishing time-group logic); the full engine
suite (luajit tests/run_engine.lua) is 533/533.
2026-09-03 19:11:23 +02:00
bryanthaboi d6cf840e02 test fix 2026-09-03 08:51:37 -04:00
bryanthaboi e2a04c8fee shader logic fix. CLOSES #2144, CLOSES #2145 2026-09-03 08:49:08 -04:00
bryanthaboi 2c18d904a6 logic clock 2026-09-03 07:11:31 -04:00
bryanthaboi d31e412a50 CLOSES #2139, CLOSES #2140, CLOSES #2142, CLOSES #2143 2026-09-03 06:33:23 -04:00
bryanthaboi f00e867be8 CLOSES #2130, CLOSES #2131, CLOSES #2132, CLOSES #2134, CLOSES #2135, CLOSES #2136, CLOSES #2137, CLOSES #2138 2026-09-03 01:36:18 -04:00
bryanthaboi 67ff7463ab CLOSES #2119, CLOSES #2121, CLOSES #2122, CLOSES #2123, CLOSES #2127 2026-09-02 23:46:27 -04:00
bryanthaboi 59d1808602 Restore the iOS artifact upload step 2026-09-02 19:30:52 -04:00
bryanthaboi 9c914514a9 Publish the CI iOS ipa to a rolling prerelease instead of an Actions artifact 2026-09-02 19:13:13 -04:00
bryanthaboi 5a92256625 Rebuild iOS when ci.yml changes 2026-09-02 18:54:16 -04:00
bryanthaboi bdc9869a0b Retry the iOS artifact upload once when it stalls 2026-09-02 18:46:17 -04:00
bryanthaboi ae99ff412b Retry iOS dependency clones over HTTP/1.1 2026-09-02 18:33:36 -04:00
bryanthaboi 4e32ca1de3 Strip Gen 2 text terminator from intro and gender select, use × glyph in Gen 1 item lists 2026-09-02 18:05:09 -04:00
bryanthaboi 61c416b23e Merge remote-tracking branch 'origin/main' into dev 2026-09-02 17:34:50 -04:00
bryanthaboi feaa86947c Merge pull request #2128 from 1Jamie/crystal-toes 2026-09-02 17:23:31 -04:00
1jamie 395c0542a3 crystal bank 1 / attrmap overworld + proper IN_GRASS feet compositing (pret 1:1)
wire up Crystal PalMap tile attrs (bank 1, flips, BG_PRIO) through map bake,
attr grid, and BG-over-OAM blits instead of the gold/silver grassAtlasFor shortcut.
crystal-only: MapAttrGrid + TileAttrs, OAM bottom/top split for IN_GRASS,
keyed grass over feet strip via attrmap, drawBgPriorityOver for wAttrmap bit 7.
gold/silver left alone on the old path — all of this gated behind isCrystal().
fixes standing still in grass with tufts on torso / feet on top of grass (#2080).
RomExtractorGen2 pulls crystal PalMap attrs; SpriteRenderer splits standing
sheets on frameHeight; tests for tile attrs + feet strip regression.
2026-09-02 14:46:24 -05:00
bryanthaboi f5ccd6f004 Merge pull request #2039 from laaledesiempre/fix/options-decode-cache
perf(core): memoize options decode between saves
2026-09-02 13:02:12 -04:00
github-actions afc4f6de77 chore(ios): update app-repo.json [skip ci] 2026-09-02 12:27:14 -04:00
bryanthaboi b87ee9e5d6 Merge pull request #2112 from bryanthaboi/dev
CLOSES #2087, CLOSES #2107, CLOSES #2108, CLOSES #2109
v0.2.52
2026-09-02 12:13:11 -04:00
bryanthaboi 2504a36a84 CLOSES #2087, CLOSES #2107, CLOSES #2108, CLOSES #2109 2026-09-02 12:06:56 -04:00
github-actions db12d28310 chore(ios): update app-repo.json [skip ci] 2026-09-02 10:30:33 -04:00
bryanthaboi 6afeca1aeb Merge pull request #2111 from bryanthaboi/dev
perhaps fixing fps issue
v0.2.51
2026-09-02 10:16:37 -04:00
bryanthaboi 4b2aa483bb perhaps fixing fps issue 2026-09-02 10:02:32 -04:00
github-actions 37b3a2e591 chore(ios): update app-repo.json [skip ci] 2026-09-02 09:54:52 -04:00
bryanthaboi a86f349e64 Merge pull request #2110 from bryanthaboi/dev
CLOSES #2080, CLOSES #2082, CLOSES #2084, CLOSES #2085, CLOSES #2086,…
v0.2.50
2026-09-02 09:33:22 -04:00
bryanthaboi d6c74bb7bb CLOSES #2080, CLOSES #2082, CLOSES #2084, CLOSES #2085, CLOSES #2086, CLOSES #2090, CLOSES #2091, CLOSES #2093, CLOSES #2094, CLOSES #2095, CLOSES #2099, CLOSES #2100, CLOSES #2101, CLOSES #2102 2026-09-02 09:18:54 -04:00
github-actions fde3ce3771 chore(ios): update app-repo.json [skip ci] 2026-09-02 07:01:57 -04:00
bryanthaboi 70db453f6a Merge pull request #2105 from bryanthaboi/dev v0.2.49 2026-09-02 06:48:16 -04:00
bryanthaboi 8ca1e08a86 Merge pull request #2097 from thibautbus/fix/gen2-move-effects-validation 2026-09-02 06:30:08 -04:00
bryanthaboi 50da140c02 Merge pull request #2056 from thibautbus/fix/complete-translation-coverage-v0241 2026-09-02 06:29:57 -04:00
bryanthaboi 51801a27bc CLOSES #2092, CLOSES #2098 2026-09-02 05:58:45 -04:00
thibautbus 56a9a3f0b4 Widen Gen 2 move_effects registration to the movedex's own effect ids
Battle.MOVE_EFFECT_RECORDS only lists effects with a standalone handler --
by design, per its own comment: a move whose effect is just "deal damage"
(EFFECT_NORMAL_HIT and the multi-hit/recoil/drain families) has none and
falls through to the generic damage path. But src/mods/Schemas.lua's
`moves.effect = f.id("move_effects")` cross-check treats move_effects as
the complete id space for the field regardless of generation, so every
"full" effect the real Gold/Silver movedex uses read as a dangling
reference the instant any mod's `moves` patch touched the registry --
caught building the Gold/Silver translation mod, whose move_names patch
(name only, never effect) was enough to trigger the scan on all ~130
moves.

registerMoveEffectsInto now also registers a bare `{kind="primary"}`
marker for every effect id data.moves actually uses that MOVE_EFFECT_RECORDS
doesn't already cover. Both of moveEffectRecordFor's call sites already
treat a handler-less record exactly like a missing one (nil-checking
.run/.status before use), so this is a validation-only change with no
battle behavior difference -- a real typo in a mod's own effect patch is
still caught, since the widened set is seeded from the pre-merge data.moves,
not from whatever a mod patches in afterward.
2026-09-02 11:43:30 +02:00