Merge pull request #586 from andimiller/surfing-pikachu-fix

feat(yellow): port SurfingPikachu behaviour
This commit is contained in:
bryanthaboi
2026-08-02 00:58:32 -04:00
committed by GitHub
13 changed files with 437 additions and 15 deletions
+6
View File
@@ -77,6 +77,12 @@ local SAFARI = {
local PLAYER_SPRITES = {
walk = "SPRITE_RED", surf = "SPRITE_SEEL",
bike = "SPRITE_RED_BIKE", fly = "SPRITE_BIRD",
-- Yellow's IsSurfingPikachuInParty: when the SURF-mon is a Pikachu,
-- the player rides this sheet. GFX loads via
-- LoadSurfingPlayerSpriteGraphics2, not SpriteSheetPointerTable, so
-- it needs a manifest extract (RFC 0001). Guarded in Player.new so
-- before extraction lands the ride keeps the Seel.
surfPikachu = "SPRITE_SURFING_PIKACHU",
}
-- The player's own trainer art: RedPicBack (the battle back pic, up until
+25
View File
@@ -389,6 +389,10 @@ function OverworldState:setMap(mapId, x, y, facing, opts)
and not self.map:isWalkableCell(x, y)
and self.map:isWaterCell(x, y)
end
-- re-derive from the live party: a reloaded save with the SURF-Pikachu
-- since deposited should not render the Pikachu sheet.
-- ponytail: re-derived rather than persisted.
self:syncSurfingPikachu()
end
-- crossConnection re-arms this after setMap; clear so a warp/reload
-- cannot leave a stale deferred PlayMapMusic pending
@@ -1489,6 +1493,21 @@ function OverworldState:partyKnows(moveId)
return partyKnowsVanilla(moveId)
end
-- IsSurfingPikachuInParty (home/map_objects.asm): when the SURF-mon
-- is a Pikachu, pose() renders the Pikachu surf sprite. Called at
-- every surf-state change so a reloaded save picks the right sheet
-- after a party change. No-op when not surfing.
function OverworldState:syncSurfingPikachu()
local p = self.player
if not p then return end
if not p.surfing then
p.surfingPikachu = false
return
end
local mon = self:partyKnows("SURF")
p.surfingPikachu = mon ~= nil and mon.species == "PIKACHU" or false
end
-- The rejection loop shared by the Good and Super Rods
-- (item_effects.asm ItemUseGoodRod .RandomLoop / ReadSuperRodData): an
-- odd random byte is no bite; otherwise a 2-bit pick rerolls until it
@@ -1585,6 +1604,7 @@ function OverworldState:flyTo(mapId)
Game.save.onBike = false
Game.save.forcedBike = nil -- HandleFlyWarpOrDungeonWarp res BIT_ALWAYS_ON_BIKE
self.player.surfing = false
self:syncSurfingPikachu()
-- the bird carries the player off westward before the warp
-- (engine/overworld/player_animations.asm LoadBirdSpriteGraphics)
self.flyAnim = { frames = 48 }
@@ -1612,6 +1632,7 @@ function OverworldState:beginTeleportOut(onDone)
end
require("src.core.Sound").play(Game.data, "Teleport_Exit1")
self.player.surfing = false
self:syncSurfingPikachu()
self.player.inputLocked = true
-- rising spin: the mirror of the arrival spin-drop set in startWarpTo, so
-- spinRise lifts the sprite (Player:pose) while spinFrames counts down
@@ -2275,6 +2296,7 @@ function OverworldState:trySurf(fx, fy, onClose)
Game.stack:push(TextBox.new(Game, text, function()
if onClose then onClose() end
p.surfing = true
self:syncSurfingPikachu()
require("src.core.Music").setSurfing(Game.data, true)
Game.stack:push(require("src.render.Transition").whiteFlash(Game, nil,
function() self:stepForwardOrCrossEdge(p.facing) end))
@@ -3196,6 +3218,7 @@ function OverworldState:onStepComplete()
-- dismounting a surf: landing on a walkable cell ends it
if p.surfing and self.map:isWalkableCell(p.cellX, p.cellY) then
p.surfing = false
self:syncSurfingPikachu()
require("src.core.Music").setSurfing(Game.data, false)
end
@@ -3497,6 +3520,7 @@ function OverworldState:checkForcedMovement()
end
elseif tile.mode == "surf" then
p.surfing = true
self:syncSurfingPikachu()
require("src.core.Music").setSurfing(Game.data, true)
end
return false
@@ -3775,6 +3799,7 @@ end
function OverworldState:warpToHealPoint(onDone, opts)
local heal = self:healPoint()
self.player.surfing = false
self:syncSurfingPikachu()
-- HandleFlyWarpOrDungeonWarp + DisplayPlayerBlackedOutText both clear
-- BIT_ALWAYS_ON_BIKE (home/overworld.asm / home/text_script.asm)
Game.save.forcedBike = nil
+8
View File
@@ -41,11 +41,18 @@ function Player.new(data, cx, cy, facing)
-- LoadSurfingPlayerSpriteGraphics, home/overworld.asm)
local walkId = FieldDefaults.fieldValue(data, "playerSprites", "walk")
local surfId = FieldDefaults.fieldValue(data, "playerSprites", "surf")
local surfPikaId = FieldDefaults.fieldValue(data, "playerSprites", "surfPikachu")
local bikeId = FieldDefaults.fieldValue(data, "playerSprites", "bike")
self.sprite = SpriteRenderer.new(data.sprites[walkId], "player")
if surfId and data.sprites[surfId] then
self.surfSprite = SpriteRenderer.new(data.sprites[surfId], "player")
end
-- Yellow's surfing-Pikachu ride (Yellow LoadSurfingPlayerSpriteGraphics2,
-- paired with field.playerSprites.surfPikachu). rotated in at pose()
-- when the SURF-mon is a Pikachu.
if surfPikaId and data.sprites[surfPikaId] then
self.surfPikachuSprite = SpriteRenderer.new(data.sprites[surfPikaId], "player")
end
if bikeId and data.sprites[bikeId] then
self.bikeSprite = SpriteRenderer.new(data.sprites[bikeId], "player")
end
@@ -288,6 +295,7 @@ function Player:pose()
-- RodResponse (engine/items/item_effects.asm) zeroes wWalkBikeSurfState
-- across FishingAnim, so casting from the water shows the on-foot sheet
local sprite = (self.fishing and self.sprite)
or (self.surfing and self.surfingPikachu and self.surfPikachuSprite)
or (self.surfing and self.surfSprite)
or (self.onBike and self.bikeSprite) or self.sprite
return sprite, self.px, py, facing, phase, flip, hopping