mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 08:21:02 +02:00
Merge pull request #586 from andimiller/surfing-pikachu-fix
feat(yellow): port SurfingPikachu behaviour
This commit is contained in:
@@ -1011,6 +1011,10 @@ do
|
||||
"the boot path seeded field.palettes")
|
||||
check(Data.field.playerSprites.walk == "SPRITE_RED",
|
||||
"the boot path seeded field.playerSprites")
|
||||
check(Data.field.playerSprites.surf == "SPRITE_SEEL",
|
||||
"the surf sprite defaults to the Seel")
|
||||
check(Data.field.playerSprites.surfPikachu == "SPRITE_SURFING_PIKACHU",
|
||||
"the surfing-Pikachu sprite defaults to SPRITE_SURFING_PIKACHU (RFC 0001; Yellow ride)")
|
||||
check(Data.field.badgeGates.ROUTE_22_GATE.passedFlag == "PASSED_ROUTE22_GATE",
|
||||
"the boot path filled the gaps in a stamped key")
|
||||
check(Data.constants.world.stepFrames == 16,
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
-- Parity port: Yellow's IsSurfingPikachuInParty (home/map_objects.asm).
|
||||
-- When the party mon that knows SURF is a Pikachu, the player's surf sprite
|
||||
-- swaps from the default (the Seel) to a Pikachu overworld sheet. This
|
||||
-- mirrors vanilla Yellow, which repoints wSpritePlayerStatePtr at the
|
||||
-- surfing-Pikachu sheet during the ride.
|
||||
--
|
||||
-- Covers the engine change that adds field.playerSprites.surfPikachu,
|
||||
-- Player.surfPikachuSprite, OverworldState:syncSurfingPikachu, and the
|
||||
-- pose() sprite-pick switch. No sprite bytes are read -- the assertions
|
||||
-- compare the chosen SpriteRenderer's backing def, so the test is ROM-free
|
||||
-- against the fixture dataset and runs in CI without an import.
|
||||
--
|
||||
-- Self-contained; run via:
|
||||
-- luajit tests/parity_surfing_pikachu_sprite.lua
|
||||
package.path = "./?.lua;./?/init.lua;" .. package.path
|
||||
if not _G.love then _G.love = require("tests.love_stub") end
|
||||
|
||||
local Data = require("src.core.Data")
|
||||
if not (Data.maps and Data.maps.CINNABAR_ISLAND) then Data:load() end
|
||||
|
||||
local Game = require("src.core.Game")
|
||||
local Input = require("src.core.Input")
|
||||
local Renderer = require("src.render.Renderer")
|
||||
local SaveData = require("src.core.SaveData")
|
||||
local StateStack = require("src.core.StateStack")
|
||||
local OW = require("src.world.OverworldController")
|
||||
local S = require("tests.harness").suite("parity surfing-pikachu sprite")
|
||||
local check, eq = S.check, S.eq
|
||||
|
||||
-- field default: the new key is seeded alongside the existing surf sprite,
|
||||
-- so a mod-free boot gets the Pikachu overworld sheet by default
|
||||
check(Data.field.playerSprites.surfPikachu == "SPRITE_SURFING_PIKACHU",
|
||||
"field.playerSprites.surfPikachu defaults to SPRITE_SURFING_PIKACHU (RFC 0001; Yellow ride)")
|
||||
check(Data.field.playerSprites.surf == "SPRITE_SEEL",
|
||||
"the default surf sprite is still the Seel (no behavior change)")
|
||||
|
||||
Game.data = Data
|
||||
Game.input = Input; Input:init()
|
||||
Game.renderer = Renderer; Renderer:init()
|
||||
Game.stack = StateStack
|
||||
StateStack:init()
|
||||
Game.overworld = OW
|
||||
|
||||
local function mkMon(species, ...)
|
||||
local moves = {}
|
||||
for _, id in ipairs({ ... }) do
|
||||
table.insert(moves, { id = id, pp = 10, ppUp = 0 })
|
||||
end
|
||||
return {
|
||||
species = species, level = 30, hp = 50, maxHp = 50,
|
||||
status = 0, moves = moves,
|
||||
}
|
||||
end
|
||||
|
||||
local function freshOw(party)
|
||||
Game.save = SaveData.newGame()
|
||||
Game.save.party = party
|
||||
Game.save.inventory = { SOULBADGE = true }
|
||||
while Game.stack:top() do Game.stack:pop() end
|
||||
Game.stack:push(OW, "CINNABAR_ISLAND", 19, 8, "right")
|
||||
local ow = Game.stack:top()
|
||||
-- the mount itself goes through trySurf (TextBox + whiteFlash + step),
|
||||
-- so arm the surf state directly and let syncSurfingPikachu derive
|
||||
-- the sprite pick -- the unit under test.
|
||||
ow.player.surfing = true
|
||||
ow:syncSurfingPikachu()
|
||||
-- the real SPRITE_SURFING_PIKACHU art only exists after an Yellow
|
||||
-- import + the RFC 0001 extractor path; inject a sentinel surf sprite
|
||||
-- so pose()'s selection rule is testable in the fixture dataset.
|
||||
ow.player.surfPikachuSprite = { def = { id = "SPRITE_TEST_SURF_PIKA" } }
|
||||
return ow
|
||||
end
|
||||
|
||||
-- the sprite def backing a Player:surfSprite / surfPikachuSprite, so the
|
||||
-- assertion can compare identities without drawing
|
||||
local function surfSpriteId(player)
|
||||
local sprite, _px, _py = player:pose()
|
||||
return sprite and sprite.def and sprite.def.id or nil
|
||||
end
|
||||
|
||||
-- -------------------------------------------------- Pikachu knows SURF
|
||||
do
|
||||
local ow = freshOw({ mkMon("PIKACHU", "SURF"), mkMon("SQUIRTLE") })
|
||||
check(ow.player.surfing, "player is now surfing")
|
||||
check(ow.player.surfingPikachu == true,
|
||||
"surfingPikachu set when a Pikachu knows SURF")
|
||||
eq(surfSpriteId(ow.player), "SPRITE_TEST_SURF_PIKA",
|
||||
"pose() draws the surf-pikachu sprite, not the Seel (when set)")
|
||||
|
||||
-- dismount flips it off again: set surfing false and re-sync, the way
|
||||
-- the dismount paths in OverworldController do
|
||||
ow.player.surfing = false
|
||||
ow:syncSurfingPikachu()
|
||||
check(ow.player.surfingPikachu == false,
|
||||
"syncSurfingPikachu clears the flag when dismounted")
|
||||
end
|
||||
|
||||
-- -------------------------------------------------- no Pikachu, no swap
|
||||
do
|
||||
local ow = freshOw({ mkMon("SQUIRTLE", "SURF") })
|
||||
check(ow.player.surfing, "player is surfing")
|
||||
check(ow.player.surfingPikachu == false,
|
||||
"no Pikachu in the SURF-mon: surfingPikachu stays false")
|
||||
eq(surfSpriteId(ow.player), "SPRITE_SEEL",
|
||||
"pose() keeps the default Seel surf sprite")
|
||||
end
|
||||
|
||||
-- -------------------------------------------------- SURF-knower is the
|
||||
-- Pikachu's species, not a slot position: a Pikachu without SURF behind a
|
||||
-- Squirtle that has SURF must NOT trigger the swap
|
||||
do
|
||||
local ow = freshOw({ mkMon("SQUIRTLE", "SURF"), mkMon("PIKACHU", "THUNDER_SHOCK") })
|
||||
check(ow.player.surfingPikachu == false,
|
||||
"Pikachu present but not the SURF-mon: no swap")
|
||||
eq(surfSpriteId(ow.player), "SPRITE_SEEL",
|
||||
"pose() keeps the Seel when the SURF-mon is not a Pikachu")
|
||||
end
|
||||
|
||||
-- -------------------------------------------------- not surfing: no swap
|
||||
do
|
||||
local ow = freshOw({ mkMon("PIKACHU", "SURF") })
|
||||
ow.player.surfing = false
|
||||
ow:syncSurfingPikachu()
|
||||
check(ow.player.surfingPikachu == false,
|
||||
"syncSurfingPikachu is a no-op swap when not surfing")
|
||||
end
|
||||
|
||||
S.finish()
|
||||
Reference in New Issue
Block a user