feat(yellow): port SurfingPikachu behaviour

Yellow's IsSurfingPikachuInParty swaps the player's overworld sheet to
a Pikachu-on-a-surfboard when the party mon that knows SURF is a
Pikachu. The recomp was missing both halves of this: the sheet was
never extracted, and the engine had no seam for the swap.

Extraction: SurfingPikachuSprite (gfx/sprites/surfing_pikachu.2bpp)
loads outside SpriteSheetPointerTable via
LoadSurfingPlayerSpriteGraphics2, the same bypass RedBikeSprite uses.
Added the symbol to the Yellow manifest and a parallel extract in
RomExtractor / build_rom_data / extract/sprites, minting
SPRITE_SURFING_PIKACHU. PaletteFX colors it (player OBP palette, same
as the bike).

Engine: new field.playerSprites.surfPikachu (default
SPRITE_SURFING_PIKACHU, guarded so before extraction the ride keeps
the Seel). Player.new caches surfPikachuSprite; pose() picks it when
surfing and the SURF-mon is a Pikachu. New
OverworldState:syncSurfingPikachu derives the flag from partyKnows at
every surf-state toggle (mount, dismount, fly, teleport, blackout,
forced-surf tile, boot-restore). Runtime-only, re-derived at load so a
party change between save and load is honored.

Lane B: RFC at docs/rfcs/0001-surfing-pikachu-sprite.md.
Backward-compatible — existing mods see no change (surf still defaults
to SPRITE_SEEL; surfPikachu only resolves on a Yellow import after
regeneration). Parity tests in tests/parity_surfing_pikachu_sprite.lua
(12/12) and tests/mod_world_tests.lua (19229/19229 with the new
boot-seed checks). tests/parity_cinnabar_east_surf.lua (24/24)
unchanged.

Regeneration required: re-run make_yellow_manifest.py against a
pret/pokeyellow checkout, then re-import the Yellow ROM.
This commit is contained in:
Andi Miller
2026-08-01 15:39:46 +01:00
parent 22fd196045
commit 1fe35335a5
13 changed files with 437 additions and 15 deletions
+19
View File
@@ -495,6 +495,25 @@ function RomExtractor:extractSprites()
image = "assets/generated/sprites/red_bike.png",
frames = bikeFrames, walker = bikeFrames >= 6,
}
-- Yellow-only: the surfing-Pikachu overworld sheet, loaded outside
-- SpriteSheetPointerTable (LoadSurfingPlayerSpriteGraphics2) -- same
-- extra-extract shape as RedBikeSprite. (RFC 0001)
local surfPika = self.manifest.sprites.surfPikachu
if surfPika and self.symbols[surfPika.label] then
local spSymbol = self:symbol(surfPika.label)
self:write2bpp(
self.rom:bytes(spSymbol.bank, spSymbol.address,
surfPika.imageWidth * surfPika.imageHeight / 4),
surfPika.imageWidth, surfPika.imageHeight,
"sprites/" .. surfPika.imageBase .. ".png", true)
local spFrames = surfPika.imageHeight / 16
out.SPRITE_SURFING_PIKACHU = {
id = "SPRITE_SURFING_PIKACHU",
source = ("ROM:%s"):format(surfPika.label),
image = "assets/generated/sprites/" .. surfPika.imageBase .. ".png",
frames = spFrames, walker = spFrames >= 6,
}
end
self:write("sprites", out)
self:tick("Overworld sprites", #order + 1, #order + 1)
return out