mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 00:10:56 +02:00
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:
@@ -461,6 +461,27 @@ def extract_sprites(rom, symbols, manifest, out_dir, assets_dir):
|
||||
"walker": bike_frames >= 6,
|
||||
}
|
||||
|
||||
# Yellow-only: the surfing-Pikachu overworld ride sheet, loaded
|
||||
# outside SpriteSheetPointerTable (LoadSurfingPlayerSpriteGraphics2) --
|
||||
# same extra extract as RedBikeSprite. (RFC 0001)
|
||||
surf = manifest["sprites"].get("surfPikachu")
|
||||
if surf and _has_symbol(symbols, surf["label"]):
|
||||
surf_symbol = _symbol(symbols, surf["label"])
|
||||
surf_length = surf["imageWidth"] * surf["imageHeight"] // 4
|
||||
_write_2bpp_png(
|
||||
rom.bytes(surf_symbol.bank, surf_symbol.address, surf_length),
|
||||
surf["imageWidth"], surf["imageHeight"],
|
||||
os.path.join(assets_dir, "sprites", surf["imageBase"] + ".png"),
|
||||
transparent_color0=True)
|
||||
surf_frames = surf["imageHeight"] // 16
|
||||
out["SPRITE_SURFING_PIKACHU"] = {
|
||||
"id": "SPRITE_SURFING_PIKACHU",
|
||||
"source": f"ROM:{surf['label']}",
|
||||
"image": f"assets/generated/sprites/{surf['imageBase']}.png",
|
||||
"frames": surf_frames,
|
||||
"walker": surf_frames >= 6,
|
||||
}
|
||||
|
||||
util.write_lua(
|
||||
os.path.join(out_dir, "sprites.lua"), out,
|
||||
header="Source: canonical Pokemon Red ROM (overworld sprite sheets)")
|
||||
|
||||
@@ -83,6 +83,23 @@ def extract(pokered, out_dir, assets_dir, sprite_order):
|
||||
"walker": frames >= 6,
|
||||
}
|
||||
|
||||
# Yellow-only: the surfing-Pikachu overworld ride sheet, loaded
|
||||
# outside SpriteSheetPointerTable (LoadSurfingPlayerSpriteGraphics2) --
|
||||
# same extra extract as RedBikeSprite. (RFC 0001)
|
||||
surf_src = files.get("SurfingPikachuSprite")
|
||||
if surf_src:
|
||||
png_src = os.path.join(pokered, re.sub(r"\.2bpp$", ".png", surf_src))
|
||||
dst = os.path.join(assets_dir, "sprites", "surfing_pikachu.png")
|
||||
size = gfx.convert_png(png_src, dst, transparent_color0=True)
|
||||
frames = size[1] // 16
|
||||
out["SPRITE_SURFING_PIKACHU"] = {
|
||||
"id": "SPRITE_SURFING_PIKACHU",
|
||||
"source": "gfx/sprites.asm SurfingPikachuSprite (LoadSurfingPlayerSpriteGraphics2)",
|
||||
"image": "assets/generated/sprites/surfing_pikachu.png",
|
||||
"frames": frames,
|
||||
"walker": frames >= 6,
|
||||
}
|
||||
|
||||
util.write_lua(os.path.join(out_dir, "sprites.lua"), out,
|
||||
header="Sources: data/sprites/sprites.asm, gfx/sprites/*.png\n"
|
||||
"Frame order (walker): stand D/U/L, walk D/U/L; right = flipped left.")
|
||||
|
||||
@@ -280,7 +280,7 @@ def sprite_metadata(pokered, order):
|
||||
bike_png = re.sub(r"\.2bpp$", ".png", bike_source)
|
||||
with Image.open(os.path.join(pokered, bike_png)) as image:
|
||||
bike_width, bike_height = image.size
|
||||
return {
|
||||
out_manifest = {
|
||||
"order": out,
|
||||
"bike": {
|
||||
"label": "RedBikeSprite",
|
||||
@@ -289,6 +289,20 @@ def sprite_metadata(pokered, order):
|
||||
"imageHeight": bike_height,
|
||||
},
|
||||
}
|
||||
# Yellow-only: the surfing-Pikachu overworld ride sheet, parallel
|
||||
# to the bike entry; absent in Red/Blue. (RFC 0001)
|
||||
surf_source = files.get("SurfingPikachuSprite")
|
||||
if surf_source:
|
||||
surf_png = re.sub(r"\.2bpp$", ".png", surf_source)
|
||||
with Image.open(os.path.join(pokered, surf_png)) as image:
|
||||
surf_width, surf_height = image.size
|
||||
out_manifest["surfPikachu"] = {
|
||||
"label": "SurfingPikachuSprite",
|
||||
"imageBase": "surfing_pikachu",
|
||||
"imageWidth": surf_width,
|
||||
"imageHeight": surf_height,
|
||||
}
|
||||
return out_manifest
|
||||
|
||||
|
||||
def text_metadata(pokered):
|
||||
|
||||
@@ -103,6 +103,10 @@ YELLOW_EXTRA_SYMBOLS = (
|
||||
"Pic_e5b7d", "Pic_e5ddd", "GFX_e6020", "Pic_e6340", "Pic_e6587",
|
||||
"Pic_e67d6", "GFX_e6e6f", "GFX_e718f", "GFX_e74af", "Pic_e77cf",
|
||||
"Pic_f0abf", "Pic_f0cf4",
|
||||
# Yellow-only overworld player surf sprite, loaded outside
|
||||
# SpriteSheetPointerTable (LoadSurfingPlayerSpriteGraphics2) --
|
||||
# needs its own extract like RedBikeSprite. (RFC 0001)
|
||||
"SurfingPikachuSprite",
|
||||
)
|
||||
|
||||
# Yellow-only dialogue whose bank labels carry no leading underscore, so
|
||||
|
||||
@@ -19934,7 +19934,13 @@
|
||||
"imageWidth": 16,
|
||||
"label": "GamblerAsleepSprite"
|
||||
}
|
||||
]
|
||||
],
|
||||
"surfPikachu": {
|
||||
"imageBase": "surfing_pikachu",
|
||||
"imageHeight": 96,
|
||||
"imageWidth": 16,
|
||||
"label": "SurfingPikachuSprite"
|
||||
}
|
||||
},
|
||||
"symbols": {
|
||||
"AbraPicBack": [
|
||||
@@ -21369,6 +21375,22 @@
|
||||
28,
|
||||
16822
|
||||
],
|
||||
"FanClubChairPrintText1": [
|
||||
43,
|
||||
22070
|
||||
],
|
||||
"FanClubChairPrintText2": [
|
||||
43,
|
||||
22210
|
||||
],
|
||||
"FanClubChairPrintText3": [
|
||||
43,
|
||||
22243
|
||||
],
|
||||
"FanClubChairPrintText4": [
|
||||
43,
|
||||
22257
|
||||
],
|
||||
"FarfetchdPicBack": [
|
||||
10,
|
||||
28976
|
||||
@@ -25293,6 +25315,18 @@
|
||||
24,
|
||||
22540
|
||||
],
|
||||
"SSAnneKitchenCook7EelsAuBarbecueText": [
|
||||
38,
|
||||
21363
|
||||
],
|
||||
"SSAnneKitchenCook7PrimeBeefSteakText": [
|
||||
38,
|
||||
21414
|
||||
],
|
||||
"SSAnneKitchenCook7SalmonDuSaladText": [
|
||||
38,
|
||||
21298
|
||||
],
|
||||
"SSAnneKitchenCook7Text": [
|
||||
24,
|
||||
22545
|
||||
@@ -26069,10 +26103,18 @@
|
||||
21,
|
||||
25898
|
||||
],
|
||||
"SilphCo9FNurseDontGiveUpText": [
|
||||
38,
|
||||
30668
|
||||
],
|
||||
"SilphCo9FNurseText": [
|
||||
23,
|
||||
22467
|
||||
],
|
||||
"SilphCo9FNurseThankYouText": [
|
||||
38,
|
||||
30683
|
||||
],
|
||||
"SilphCo9FNurseYouLookTiredText": [
|
||||
38,
|
||||
30622
|
||||
@@ -26237,10 +26279,18 @@
|
||||
32,
|
||||
25380
|
||||
],
|
||||
"SurfingPikachuSprite": [
|
||||
63,
|
||||
28143
|
||||
],
|
||||
"SwimmerPic": [
|
||||
19,
|
||||
20787
|
||||
],
|
||||
"TMNotebookText": [
|
||||
39,
|
||||
27310
|
||||
],
|
||||
"TamerPic": [
|
||||
19,
|
||||
23374
|
||||
@@ -26753,6 +26803,10 @@
|
||||
6,
|
||||
21103
|
||||
],
|
||||
"ViridianCityFisherYouCanHaveThisText": [
|
||||
45,
|
||||
18396
|
||||
],
|
||||
"ViridianCityGambler1Text": [
|
||||
6,
|
||||
21055
|
||||
@@ -26801,6 +26855,14 @@
|
||||
6,
|
||||
21043
|
||||
],
|
||||
"ViridianCityYoungster2CaterpieAndWeedleDescriptionText": [
|
||||
45,
|
||||
18125
|
||||
],
|
||||
"ViridianCityYoungster2OkThenText": [
|
||||
45,
|
||||
18111
|
||||
],
|
||||
"ViridianCityYoungster2Text": [
|
||||
6,
|
||||
21067
|
||||
@@ -39174,7 +39236,30 @@
|
||||
]
|
||||
},
|
||||
"labels": [
|
||||
"FanClubChairPrintText1",
|
||||
"FanClubChairPrintText2",
|
||||
"FanClubChairPrintText3",
|
||||
"FanClubChairPrintText4",
|
||||
"MelanieBulbasaurText",
|
||||
"MelanieOddishText",
|
||||
"MelanieSandshrewText",
|
||||
"MelanieText1",
|
||||
"MelanieText2",
|
||||
"MelanieText3",
|
||||
"MelanieText4",
|
||||
"MelanieText5",
|
||||
"SSAnneKitchenCook7EelsAuBarbecueText",
|
||||
"SSAnneKitchenCook7PrimeBeefSteakText",
|
||||
"SSAnneKitchenCook7SalmonDuSaladText",
|
||||
"SilphCo2FSilphWorkerFPleaseTakeThisText",
|
||||
"SilphCo9FNurseDontGiveUpText",
|
||||
"SilphCo9FNurseThankYouText",
|
||||
"SilphCo9FNurseYouLookTiredText",
|
||||
"TMNotebookText",
|
||||
"TeachingHMsText",
|
||||
"ViridianCityFisherYouCanHaveThisText",
|
||||
"ViridianCityYoungster2CaterpieAndWeedleDescriptionText",
|
||||
"ViridianCityYoungster2OkThenText",
|
||||
"_AIBattleUseItemText",
|
||||
"_AIBattleWithdrawText",
|
||||
"_AbandonLearningText",
|
||||
@@ -41860,15 +41945,7 @@
|
||||
"_YeahText",
|
||||
"_YourNameIsText",
|
||||
"_ZapdosDexEntry",
|
||||
"_ZubatDexEntry",
|
||||
"MelanieText1",
|
||||
"MelanieText2",
|
||||
"MelanieText3",
|
||||
"MelanieText4",
|
||||
"MelanieText5",
|
||||
"MelanieBulbasaurText",
|
||||
"MelanieOddishText",
|
||||
"MelanieSandshrewText"
|
||||
"_ZubatDexEntry"
|
||||
],
|
||||
"pointers": {
|
||||
"AgathasRoom": {
|
||||
|
||||
Reference in New Issue
Block a user