- PC storage: moving a party Pokemon out of the middle of the party left a
nil hole, so later slots showed as empty in battle and could not be filled
(#2494). Add Storage.compactParty (pret CompactPartySlots) and run it after
every PC party move, when the PC opens, and on save load so already broken
saves are repaired.
- Party menu: using a TM/HM from the bag now labels each slot ABLE!,
NOT ABLE! or LEARNED, matching DisplayPartyPokemonDataToTeachMove (#2520).
- Add regression tests for both.
- Fix held item (berries, herbs, pinch stat items) activation and permanent consumption on battler and party data during battles.
- Enforce mandatory bicycle riding on Cycling Road (Route 16-18) when dismounting Surf, entering map, loading saves, and prevent bike dismount attempts.
- Fix warp and door exit arrivals (e.g. Safari Zone gate) to execute warpExitArrival post-fade step so the player is placed on the correct forward tile and does not bypass triggers.
- Optimize field rendering pipeline by caching animated tileset pieces, pooling native metatile cell allocations, and hoisting dynamic module lookups to fast accessors.
- Save Editor (Gen 3):
- Added full support for EV, IV, Nature, Ability, and Ribbons editing for Gen 3 Pokémon.
- Implemented stat calculations, personality-based nature derivation, and Gen 3 save data structures.
- Added comprehensive unit test suite in tests/save_editor_gen3_ev_iv_tests.lua.
- GBA Extraction & Graphics:
- Added zero-copy ARM-safe ROM accessor and fast LZ77 string decompression.
- Fixed bag chrome decompression string-buffer indexing preventing solid-color renders.
- Fixed TM Case HM badge tile offset and palette mapping.
- Added builtin moves fallback in src/core/game3/battle/builtin_moves.lua.
- Improved parallel extraction resilience, desktop fallback search paths, and prefix handling.
- Android & Launchers:
- Added dynamic high-res app launcher shortcuts and color palettes for all supported games.
- Updated Android icon generator and intent dispatch handlers.
endFrame composites a world override mirrored on LOVE 12, because a
pipeline's own projection pre-flips Y. ctx.drawFx puts the standing
effects into that same canvas as ordinary 2D, so the mirror inverts them
instead of righting them: a trainer's "!" draws upside down, below its
feet, on the mirrored half of the screen.
The condition is now Renderer.mirrorsWorldOverride, named because it is
not only the blit's business, and ctx.drawFx runs its block under the
matching mirror about the bound canvas. project() is left alone: it
already answers in screen rows.