mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-25 23:11:15 +02:00
Enhance Surfing Pikachu minigame assets and functionality
- Updated .gitignore to include new generated assets for the Surfing Pikachu minigame. - Added new image assets for the minigame, including title background and intro frames. - Implemented extraction of Surfing Pikachu title art in RomExtractor. - Updated CacheContract to include new asset paths. - Enhanced SurfingMinigame with new constants and functions for improved gameplay mechanics. - Added unit tests for new features and ensured existing tests pass.
This commit is contained in:
+9
-2
@@ -1,5 +1,9 @@
|
||||
# Generated from a user-provided Pokemon Red ROM.
|
||||
# Regenerate with: python3 tools/build_data.py --rom /path/to/pokemon-red.gb --clean
|
||||
# Generated from a user-provided Pokemon ROM (Red/Blue/Yellow/Gold/Silver/Crystal).
|
||||
# Regenerate with: python3 tools/build_data.py --rom /path/to/rom.gb --clean
|
||||
# Yellow Surfing Pikachu minigame art also lands here on import:
|
||||
# assets/generated/minigame/surf_1{a,b,c}.png
|
||||
# assets/generated/minigame/title_bg.png
|
||||
# assets/generated/minigame/intro_pika_{0,1,2,3}.png
|
||||
data/generated/
|
||||
assets/generated/
|
||||
|
||||
@@ -62,6 +66,9 @@ mobile/dist/
|
||||
# ROM cache exactly like data/generated/, so it is never committable.
|
||||
/build/tiled/
|
||||
|
||||
# Local visual scratch from minigame development (not wired to CI).
|
||||
/tests/fixtures/
|
||||
|
||||
# per-machine iOS bundle-id pin (see scripts/build_ios.sh)
|
||||
mobile/ios/bundle_id.local
|
||||
|
||||
|
||||
@@ -34,6 +34,11 @@ CacheContract.VERSION_REQUIRED_FILES = {
|
||||
"assets/generated/battle/trainers/jessie_james.png",
|
||||
"assets/generated/battle/profoakb.png",
|
||||
"assets/generated/pikachu/pikapic_1.png",
|
||||
"assets/generated/minigame/surf_1a.png",
|
||||
"assets/generated/minigame/surf_1b.png",
|
||||
"assets/generated/minigame/surf_1c.png",
|
||||
"assets/generated/minigame/title_bg.png",
|
||||
"assets/generated/minigame/intro_pika_0.png",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -1637,6 +1637,113 @@ function RomExtractor:extractYellowTitleArt()
|
||||
end
|
||||
end
|
||||
|
||||
function RomExtractor:extractSurfingPikachuTitleArt()
|
||||
-- engine/minigame/surfing_pikachu.asm
|
||||
-- DrawSurfingPikachuMinigameIntroBackground: compose the 160x144
|
||||
-- "Pikachu's Beach" title from SurfingMinigame_* tilemaps over
|
||||
-- SurfingPikachu1Graphics3 tiles (mirrors tools/build_rom_data.py).
|
||||
if not self.symbols["SurfingPikachu1Graphics3"] then return end
|
||||
if not self.symbols["SurfingMinigame_BeachIntroTilemap"] then return end
|
||||
|
||||
local beachIntro = self:symbol("SurfingMinigame_BeachIntroTilemap")
|
||||
local useCtrlPad = self:symbol("SurfingMinigame_UseControlPadTilemap")
|
||||
local toSurfRad = self:symbol("SurfingMinigame_ToSurfRadTilemap")
|
||||
local titleMap = self:symbol("SurfingMinigame_TitleTilemap")
|
||||
|
||||
local beachBytes = self.rom:bytes(
|
||||
beachIntro.bank, beachIntro.address, 12 * 20)
|
||||
local useCtrlBytes = self.rom:bytes(
|
||||
useCtrlPad.bank, useCtrlPad.address, 15)
|
||||
local toSurfRadBytes = self.rom:bytes(
|
||||
toSurfRad.bank, toSurfRad.address, 13)
|
||||
local titleMapBytes = self.rom:bytes(
|
||||
titleMap.bank, titleMap.address, 6 * 12)
|
||||
|
||||
local screen = {}
|
||||
for _ = 1, 20 * 18 do screen[#screen + 1] = 0xff end
|
||||
|
||||
for i = 1, #beachBytes do
|
||||
screen[6 * 20 + i] = beachBytes[i]
|
||||
end
|
||||
for r = 0, 5 do
|
||||
for c = 0, 11 do
|
||||
screen[r * 20 + (4 + c) + 1] = titleMapBytes[r * 12 + c + 1]
|
||||
end
|
||||
end
|
||||
for r = 0, 2 do
|
||||
for c = 0, 14 do
|
||||
screen[(7 + r) * 20 + (3 + c) + 1] = 0xff
|
||||
end
|
||||
end
|
||||
for i = 1, #useCtrlBytes do
|
||||
screen[7 * 20 + 3 + i] = useCtrlBytes[i]
|
||||
end
|
||||
for i = 1, #toSurfRadBytes do
|
||||
screen[9 * 20 + 4 + i] = toSurfRadBytes[i]
|
||||
end
|
||||
|
||||
local gfx3 = self:symbol("SurfingPikachu1Graphics3")
|
||||
local rawGfx3 = self.rom:bytes(gfx3.bank, gfx3.address, 144 * 16)
|
||||
local tiles = {}
|
||||
for tile = 0, 143 do
|
||||
local one = {}
|
||||
for j = 1, 16 do one[j] = rawGfx3[tile * 16 + j] end
|
||||
tiles[tile + 1] = ImageWriter.decode2bpp(one, 8, 8, false)
|
||||
end
|
||||
local blank = ImageWriter.blank(8, 8, 1, 1, 1, 1)
|
||||
|
||||
local titleBg = ImageWriter.blank(160, 144, 1, 1, 1, 1)
|
||||
for r = 0, 17 do
|
||||
for c = 0, 19 do
|
||||
local tileId = screen[r * 20 + c + 1]
|
||||
local tileImg = blank
|
||||
if tileId ~= 0xff then
|
||||
local idx = tileId >= 0x80 and (tileId - 0x80 + 1) or (128 + tileId + 1)
|
||||
if idx >= 1 and idx <= 144 then tileImg = tiles[idx] end
|
||||
end
|
||||
ImageWriter.blit(titleBg, tileImg, c * 8, r * 8)
|
||||
end
|
||||
end
|
||||
self:save(titleBg, "minigame/title_bg.png")
|
||||
|
||||
-- Intro paddling Pikachu frames (surfing_pikachu_oam.asm .IntroPikachu).
|
||||
local INTRO_PIKA_FRAME_BASE = { 0x80, 0x84, 0x88, 0x8c }
|
||||
local INTRO_PIKA_OAM = {
|
||||
{ -12, -16, 0x03, true }, { -12, -8, 0x02, true },
|
||||
{ -12, 0, 0x01, true }, { -12, 8, 0x00, true },
|
||||
{ -4, -16, 0x13, true }, { -4, -8, 0x12, true },
|
||||
{ -4, 0, 0x11, true }, { -4, 8, 0x10, true },
|
||||
{ 4, -16, 0x23, true }, { 4, -8, 0x22, true },
|
||||
{ 4, 0, 0x21, true }, { 4, 8, 0x20, true },
|
||||
}
|
||||
local function blitIntroTile(target, tile, tx, ty, flipX)
|
||||
for y = 0, 7 do
|
||||
for x = 0, 7 do
|
||||
local sx = flipX and (7 - x) or x
|
||||
local r, g, b, a = tile:getPixel(sx, y)
|
||||
local px, py = tx + x, ty + y
|
||||
if a ~= 0 and px >= 0 and py >= 0
|
||||
and px < target:getWidth() and py < target:getHeight() then
|
||||
target:setPixel(px, py, r, g, b, a)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
for frame, vramBase in ipairs(INTRO_PIKA_FRAME_BASE) do
|
||||
local pose = ImageWriter.blank(32, 24, 1, 1, 1, 0)
|
||||
local sheetBase = vramBase - 0x80
|
||||
for _, sp in ipairs(INTRO_PIKA_OAM) do
|
||||
local dy, dx, rel, flipX = sp[1], sp[2], sp[3], sp[4]
|
||||
local tileImg = tiles[sheetBase + rel + 1]
|
||||
if tileImg then
|
||||
blitIntroTile(pose, tileImg,
|
||||
16 + dx + (flipX and 8 or 0), 12 + dy, flipX)
|
||||
end
|
||||
end
|
||||
self:save(pose, ("minigame/intro_pika_%d.png"):format(frame - 1))
|
||||
end
|
||||
end
|
||||
|
||||
function RomExtractor:raw2bpp(label, width, height, relative, options)
|
||||
options = options or {}
|
||||
local expected = width * height / 4
|
||||
@@ -1978,6 +2085,7 @@ function RomExtractor:extractField()
|
||||
self:save(image, spec[5])
|
||||
end
|
||||
end
|
||||
self:extractSurfingPikachuTitleArt()
|
||||
|
||||
-- Yellow-only: TalkToPikachu's framed portrait, one 5x5 base frame per
|
||||
-- PikaPicAnimScript -- each script's FIRST pikapic_loadgfx in
|
||||
|
||||
+702
-343
File diff suppressed because it is too large
Load Diff
@@ -44,18 +44,22 @@ print("Running SurfingMinigame unit tests...")
|
||||
local mg = SurfingMinigame.new(mockGame)
|
||||
assert_eq(mg.routine, -1, "Initial routine must be ROUTINE_TITLE (-1)")
|
||||
mg:startFromTitle()
|
||||
assert_eq(mg.routine, 0, "Routine must advance to ROUTINE_START_GAME (0) after startFromTitle()")
|
||||
assert_eq(mg.routine, 0, "Routine must be ROUTINE_START_GAME (0) after startFromTitle()")
|
||||
assert_eq(mg.hp, 6000, "Initial HP must be 6000 (60.00s)")
|
||||
assert_eq(mg.speed, 0.25, "Initial speed must be 0.25")
|
||||
assert_eq(mg.distance, 0, "Initial distance must be 0")
|
||||
assert_eq(mg.pikaState, 0, "Initial Pikachu state must be PIKA_STATE_RIDING (0)")
|
||||
print("✓ Initial state & Title transition test passed")
|
||||
|
||||
-- Test 2: Start banner transition to RunGame
|
||||
for _ = 1, 40 do
|
||||
-- Test 2: StartGame one-shot then RunGame; banner slides during play (pret-accurate)
|
||||
mg:update()
|
||||
assert_eq(mg.routine, 1, "First tick should advance to ROUTINE_RUN_GAME (1)")
|
||||
assert_true(mg.startBannerX > 80, "START banner should begin off-screen")
|
||||
for _ = 1, 36 do
|
||||
mg:update()
|
||||
end
|
||||
assert_eq(mg.routine, 1, "Routine should advance to ROUTINE_RUN_GAME (1)")
|
||||
assert_eq(mg.routine, 1, "Routine should remain ROUTINE_RUN_GAME (1) while banner slides")
|
||||
assert_eq(mg.startBannerX, 80, "START banner should finish centered after 36 frames")
|
||||
print("✓ Start banner transition test passed")
|
||||
|
||||
-- Test 3: Automatic acceleration and HP countdown
|
||||
@@ -68,7 +72,7 @@ print("✓ Auto acceleration and HP countdown test passed")
|
||||
|
||||
-- Test 4: Landing Evaluation Matrix
|
||||
local old_getWaveTile = mg.getWaveTileUnderPika
|
||||
mg.getWaveTileUnderPika = function() return 0x01 end -- force open water
|
||||
mg.getWaveTileUnderPika = function() return 0x01 end -- force open water (flat branch)
|
||||
mg.frameSet = 5
|
||||
assert_eq(mg:evaluateLanding(), "rough", "Angle 5 on open water should be rough landing")
|
||||
mg.frameSet = 6
|
||||
@@ -82,6 +86,17 @@ for f = 8, 14 do
|
||||
assert_eq(mg:evaluateLanding(), "wipeout", "Upside-down frame " .. f .. " must be wipeout")
|
||||
end
|
||||
mg.getWaveTileUnderPika = old_getWaveTile
|
||||
-- TileInteraction keys off chr tile ids, not pattern metatile ids
|
||||
mg.getWaveTileUnderPika = function() return 0x06 end
|
||||
mg.frameSet = 6
|
||||
assert_eq(mg:evaluateLanding(), "clean", "Frame 6 on chr tile $06 rising slope must be clean")
|
||||
mg.getWaveTileUnderPika = function() return 0x0b end
|
||||
mg.frameSet = 6
|
||||
assert_eq(mg:evaluateLanding(), "hard", "Frame 6 on chr tile $0b open water must be hard")
|
||||
mg.getWaveTileUnderPika = function() return 0x08 end
|
||||
mg.frameSet = 6
|
||||
assert_eq(mg:evaluateLanding(), "hard", "Frame 6 on chr tile $08 foam must use flat rules (hard)")
|
||||
mg.getWaveTileUnderPika = old_getWaveTile
|
||||
print("✓ Landing evaluation matrix test passed (including upside-down frames 8..14)")
|
||||
|
||||
-- Test 5: Stunt Scoring
|
||||
@@ -129,13 +144,15 @@ assert_eq(mg.pikaState, 0, "Pikachu should recover and return to PIKA_STATE_RIDI
|
||||
print("✓ Wipeout crash recovery test passed")
|
||||
|
||||
-- Test 7: Results tally countdown sequence
|
||||
mg.showResultsCard = true
|
||||
mg.routine = 7 -- ROUTINE_WRITE_TOTAL
|
||||
mg.hp = 100
|
||||
mg.radness = 200
|
||||
mg.totalScore = 0
|
||||
mg.routineTimer = 1
|
||||
mg.routineTimer = 0
|
||||
mg:update()
|
||||
assert_eq(mg.routine, 8, "Routine should advance to ROUTINE_ADD_HP_TOTAL (8)")
|
||||
mg.routineTimer = 0 -- pret waits 64 frames before tally; skip for unit test
|
||||
|
||||
while mg.routine == 8 do
|
||||
mg:update()
|
||||
@@ -143,6 +160,7 @@ end
|
||||
assert_eq(mg.hp, 0, "HP should be tallied down to 0")
|
||||
assert_eq(mg.totalScore, 100, "Total score should include 100 from HP")
|
||||
assert_eq(mg.routine, 9, "Routine should advance to ROUTINE_ADD_RAD_TOTAL (9)")
|
||||
mg.routineTimer = 0
|
||||
|
||||
while mg.routine == 9 do
|
||||
mg:update()
|
||||
@@ -153,7 +171,8 @@ assert_eq(mg.routine, 10, "Routine should advance to ROUTINE_WAIT_LAST (10)")
|
||||
-- Test 8: Crossing finish line while jumping upside-down crashes into water and rights Pikachu before results
|
||||
local mg8 = SurfingMinigame.new(mockGame, nil, true)
|
||||
mg8.routine = 1 -- ROUTINE_RUN_GAME
|
||||
mg8.distanceFixed = (24 * 128 - 2) * 256
|
||||
mg8.distanceSection = 24
|
||||
mg8.distanceAcc = 0
|
||||
mg8.speedFixed = 512
|
||||
mg8.pikaState = 1 -- PIKA_STATE_JUMPING
|
||||
mg8.frameSet = 11 -- Upside down
|
||||
@@ -193,7 +212,8 @@ print("✓ Mid-air upside-down finish line crossing crash & recovery test passed
|
||||
-- Test 9: Crossing finish line while upright jumping lands cleanly and proceeds
|
||||
local mg9 = SurfingMinigame.new(mockGame, nil, true)
|
||||
mg9.routine = 1
|
||||
mg9.distanceFixed = (24 * 128 - 2) * 256
|
||||
mg9.distanceSection = 24
|
||||
mg9.distanceAcc = 0
|
||||
mg9.speedFixed = 512
|
||||
mg9.pikaState = 1
|
||||
mg9.frameSet = 4 -- Clean flat
|
||||
@@ -221,7 +241,8 @@ print("✓ Mid-air upright finish line crossing test passed")
|
||||
-- Test 10: Crossing finish line while already crashed recovers before results
|
||||
local mg10 = SurfingMinigame.new(mockGame, nil, true)
|
||||
mg10.routine = 1
|
||||
mg10.distanceFixed = (24 * 128 - 2) * 256
|
||||
mg10.distanceSection = 24
|
||||
mg10.distanceAcc = 0
|
||||
mg10.speedFixed = 512
|
||||
mg10.pikaState = 3 -- PIKA_STATE_CRASHED
|
||||
mg10.crashTimer = 50
|
||||
@@ -260,12 +281,39 @@ mg12.landingTimer = 20
|
||||
mg12.speedFixed = 256
|
||||
-- Place on a rising wave pattern
|
||||
mg12.cols[5] = { pat = SurfingMinigame.WAVE_PATTERNS[0x06], hl = 110, hr = 100 }
|
||||
mg12.bgMap[5] = mg12.cols[5]
|
||||
mg12.waveHeight[8] = 110
|
||||
mg12.waveHeight[9] = 100
|
||||
mg12.bgMapReadTile = 0x06
|
||||
mg12.advanceScx = function() end
|
||||
mg12.generateBgMapIfNeeded = function() end
|
||||
mg12.distanceFixed = (5 * 16 - 80) * 256
|
||||
local startY = mg12.pikaY
|
||||
mg12:update()
|
||||
assert(mg12.pikaY ~= startY, "pikaY must continuously follow wave surface height while in PIKA_STATE_LANDING")
|
||||
print("✓ Landing slope height tracking continuity test passed")
|
||||
|
||||
-- Test 12b: Clean flat landing preserves speed; rough/hard use pret penalties
|
||||
local mg12b = SurfingMinigame.new(mockGame, nil, true)
|
||||
mg12b.routine = 1
|
||||
mg12b.frameSet = 4
|
||||
mg12b.bgMapReadTile = 0x0b
|
||||
mg12b.speedFixed = 320
|
||||
local cleanSpeed = mg12b.speedFixed
|
||||
mg12b:handleLanding()
|
||||
assert_eq(mg12b.speedFixed, cleanSpeed, "Clean flat landing must not reduce speed")
|
||||
mg12b.speedFixed = 320
|
||||
mg12b.frameSet = 5
|
||||
mg12b.bgMapReadTile = 0x0b
|
||||
mg12b:handleLanding()
|
||||
assert_eq(mg12b.speedFixed, 320 - 64, "Rough flat landing must reduce speed by 0.25")
|
||||
mg12b.speedFixed = 96
|
||||
mg12b.frameSet = 6
|
||||
mg12b.bgMapReadTile = 0x0b
|
||||
mg12b:handleLanding()
|
||||
assert_eq(mg12b.speedFixed, 0, "Hard landing below 0.5 must zero speed (pret underflow guard)")
|
||||
print("✓ Landing speed penalty test passed")
|
||||
|
||||
-- Test 13: Fixed speed enforcement (minigames must always run at 1X speed)
|
||||
assert(mg12.isFixedSpeed == true, "SurfingMinigame must have isFixedSpeed flag enabled")
|
||||
assert(mg12.isMinigame == true, "SurfingMinigame must have isMinigame flag enabled")
|
||||
@@ -274,4 +322,91 @@ local Game = require("src.core.Game")
|
||||
assert(Game.isFixedSpeedInStack(mockStack) == true, "Game.isFixedSpeedInStack must return true for SurfingMinigame")
|
||||
print("✓ Minigame fixed speed enforcement test passed")
|
||||
|
||||
-- Test 14: Course duration matches pret distance model (~24 sections, 6000 HP cap)
|
||||
local mg14 = SurfingMinigame.new(mockGame, nil, true)
|
||||
mg14.routine = 1
|
||||
local runFrames = 0
|
||||
for _ = 1, 7000 do
|
||||
mg14:update()
|
||||
runFrames = runFrames + 1
|
||||
if mg14.distanceSection >= 24 then break end
|
||||
end
|
||||
assert(runFrames >= 2500 and runFrames <= 5500,
|
||||
"Full course should finish in pret-like frame window (got " .. runFrames .. ")")
|
||||
assert(mg14.hp > 0, "Typical run should reach shore before HP timer expires")
|
||||
print("✓ Course duration window test passed (" .. runFrames .. " frames)")
|
||||
|
||||
-- Test 15: Pret 2-frame joy sampling allows triple flips at max jump arc
|
||||
mockInput.keysDown = { left = true }
|
||||
local mg15 = SurfingMinigame.new(mockGame, nil, true)
|
||||
mg15.routine = 1
|
||||
mg15.speedFixed = 512
|
||||
mg15.pikaState = 1
|
||||
mg15.jumpArcMagnitude = 16
|
||||
mg15.jumpArcFraction = 0
|
||||
mg15.jumpDescending = false
|
||||
mg15.pikaY = 84
|
||||
mg15.frameSet = 4
|
||||
mg15.radnessMeter = 0
|
||||
mg15.trickFlags = 0
|
||||
local airFrames = 0
|
||||
while mg15.pikaState == 1 and airFrames < 200 do
|
||||
mg15:update()
|
||||
airFrames = airFrames + 1
|
||||
end
|
||||
assert_true(mg15.radnessMeter >= 3,
|
||||
"Max-arc jump with held left must register at least 3 flips (got " .. tostring(mg15.radnessMeter) .. ")")
|
||||
assert_true(airFrames >= 60, "Max-arc jump should stay airborne long enough for triple flips")
|
||||
print("✓ Triple flip airtime and joy sampling test passed")
|
||||
|
||||
-- Test 16: Pret music tempo tiers (index = high byte of ((speed & $3ff) << 1))
|
||||
local mg16 = SurfingMinigame.new(mockGame, nil, true)
|
||||
mg16.routine = 1
|
||||
local tempoCases = {
|
||||
{ speed = 64, tier = 1 },
|
||||
{ speed = 127, tier = 1 },
|
||||
{ speed = 128, tier = 2 },
|
||||
{ speed = 255, tier = 2 },
|
||||
{ speed = 256, tier = 3 },
|
||||
{ speed = 383, tier = 3 },
|
||||
{ speed = 384, tier = 4 },
|
||||
{ speed = 511, tier = 4 },
|
||||
{ speed = 512, tier = 5 },
|
||||
}
|
||||
for _, c in ipairs(tempoCases) do
|
||||
mg16.speedFixed = c.speed
|
||||
mg16:updateTempo()
|
||||
local wantPitch = ({ 1.0, 117 / 109, 117 / 101, 117 / 93, 117 / 85 })[c.tier]
|
||||
assert_eq(mg16.currentPitch, wantPitch,
|
||||
string.format("Tempo tier %d at speed %d/256", c.tier, c.speed))
|
||||
end
|
||||
print("✓ Pret music tempo tier boundaries test passed")
|
||||
|
||||
-- Test 17: GetJoypad_3FrameBuffer duty cycle (hFrameCounter reload $2 → sample when counter hits 0)
|
||||
mockInput.keysDown = { left = true }
|
||||
local mg17 = SurfingMinigame.new(mockGame, nil, true)
|
||||
mg17.routine = 1
|
||||
local samples = 0
|
||||
for _ = 1, 9 do
|
||||
mg17:update()
|
||||
if mg17.joy5Left then samples = samples + 1 end
|
||||
end
|
||||
-- Counter reloads to 2 then VBlank decrements twice → active on frames 1,3,5,7,9 of each 9-frame window
|
||||
assert_eq(samples, 5, "Held input must register 5 sample frames per 9 ticks (got " .. samples .. ")")
|
||||
print("✓ Pret joypad 3-frame buffer duty cycle test passed")
|
||||
|
||||
-- Test 18: Results card keeps Pikachu on the beach (pret DrawResultsScreen + UpdateResultsPikachu)
|
||||
local mg18 = SurfingMinigame.new(mockGame, nil, true)
|
||||
mg18:beginResultsCard()
|
||||
assert_eq(mg18.pikaY, 116 - 16, "Results Pikachu Y must anchor at flat waterline")
|
||||
assert_eq(mg18.frameSet, 4, "Results pose must use flat ride frameset")
|
||||
assert_true(mg18.showResultsCard, "Results card must be active")
|
||||
assert_eq(mg18.cloudSpriteX, nil, "Cloud OAM must be cleared on results screen")
|
||||
mg18.pikaState = 6 -- PIKA_STATE_RESULTS (pret sets this on hi-score)
|
||||
mg18.resultsBobTimer = 62
|
||||
mg18:updateResultsPikachu()
|
||||
assert_true(mg18.pikaYOffset ~= 0 or mg18.resultsBobTimer >= 64,
|
||||
"Results bob must run once PIKA_STATE_RESULTS is active")
|
||||
print("✓ Results beach Pikachu visibility test passed")
|
||||
|
||||
print("All SurfingMinigame unit tests passed successfully!")
|
||||
|
||||
@@ -2064,10 +2064,14 @@ def extract_field(rom, symbols, manifest, out_dir, assets_dir):
|
||||
raw_2bpp("SurfingPikachu1Graphics2", 128, 128, "minigame/surf_1b.png", transparent=True)
|
||||
raw_2bpp("SurfingPikachu1Graphics3", 96, 96, "minigame/surf_1c.png", transparent=True)
|
||||
|
||||
beach_intro = rom.bytes(62, 0x50bc, 240)
|
||||
use_ctrl_pad = rom.bytes(62, 0x51ac, 15)
|
||||
to_surf_rad = rom.bytes(62, 0x51bb, 13)
|
||||
title_map = rom.bytes(62, 0x51c8, 72)
|
||||
beach_sym = _symbol(symbols, "SurfingMinigame_BeachIntroTilemap")
|
||||
use_ctrl_sym = _symbol(symbols, "SurfingMinigame_UseControlPadTilemap")
|
||||
to_surf_sym = _symbol(symbols, "SurfingMinigame_ToSurfRadTilemap")
|
||||
title_sym = _symbol(symbols, "SurfingMinigame_TitleTilemap")
|
||||
beach_intro = rom.bytes(beach_sym.bank, beach_sym.address, 240)
|
||||
use_ctrl_pad = rom.bytes(use_ctrl_sym.bank, use_ctrl_sym.address, 15)
|
||||
to_surf_rad = rom.bytes(to_surf_sym.bank, to_surf_sym.address, 13)
|
||||
title_map = rom.bytes(title_sym.bank, title_sym.address, 72)
|
||||
screen = [0xff] * (20 * 18)
|
||||
for i in range(240):
|
||||
screen[6 * 20 + i] = beach_intro[i]
|
||||
|
||||
@@ -98,6 +98,12 @@ YELLOW_EXTRA_SYMBOLS = (
|
||||
"SurfingPikachu1Graphics1",
|
||||
"SurfingPikachu1Graphics2",
|
||||
"SurfingPikachu1Graphics3",
|
||||
# Surfing Pikachu title screen tilemaps
|
||||
# (engine/minigame/surfing_pikachu.asm DrawSurfingPikachuMinigameIntroBackground)
|
||||
"SurfingMinigame_BeachIntroTilemap",
|
||||
"SurfingMinigame_TitleTilemap",
|
||||
"SurfingMinigame_ToSurfRadTilemap",
|
||||
"SurfingMinigame_UseControlPadTilemap",
|
||||
# Oak's own battle back pic. LoadPlayerBackPic (engine/battle/core.asm)
|
||||
# picks OldManPicBack for BATTLE_TYPE_OLD_MAN but ProfOakPicBack for
|
||||
# BATTLE_TYPE_PIKACHU, the Pallet Town catch scene (#557).
|
||||
@@ -505,6 +511,41 @@ def derive(red, pokeyellow, symbols_path):
|
||||
if "SUMMER_BEACH_HOUSE" not in locations and "ROUTE_19" in locations:
|
||||
locations["SUMMER_BEACH_HOUSE"] = dict(locations["ROUTE_19"])
|
||||
|
||||
# Surfing Pikachu minigame asset index (src/ui/SurfingMinigame.lua).
|
||||
yellow["field"]["surfingPikachu"] = {
|
||||
"music": "Music_SurfingPikachu",
|
||||
"sheets": {
|
||||
"bg": {
|
||||
"path": "assets/generated/minigame/surf_1a.png",
|
||||
"width": 40, "height": 104,
|
||||
},
|
||||
"oam": {
|
||||
"path": "assets/generated/minigame/surf_1b.png",
|
||||
"width": 128, "height": 128,
|
||||
},
|
||||
"intro": {
|
||||
"path": "assets/generated/minigame/surf_1c.png",
|
||||
"width": 96, "height": 96,
|
||||
"introPikaFrames": [
|
||||
"assets/generated/minigame/intro_pika_0.png",
|
||||
"assets/generated/minigame/intro_pika_1.png",
|
||||
"assets/generated/minigame/intro_pika_2.png",
|
||||
"assets/generated/minigame/intro_pika_3.png",
|
||||
],
|
||||
"source": "data/sprite_anims/surfing_pikachu_oam.asm .IntroPikachu",
|
||||
},
|
||||
"titleBg": {
|
||||
"path": "assets/generated/minigame/title_bg.png",
|
||||
"width": 160, "height": 144,
|
||||
},
|
||||
},
|
||||
"source": (
|
||||
"engine/minigame/surfing_pikachu.asm "
|
||||
"(DrawSurfingPikachuMinigameIntroBackground), "
|
||||
"gfx/surfing_pikachu.asm"
|
||||
),
|
||||
}
|
||||
|
||||
meta = {
|
||||
"aliasCount": alias_hits,
|
||||
"omittedIntro": list(OMIT_INTRO_SYMBOLS),
|
||||
|
||||
@@ -8861,6 +8861,39 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"surfingPikachu": {
|
||||
"music": "Music_SurfingPikachu",
|
||||
"sheets": {
|
||||
"bg": {
|
||||
"height": 104,
|
||||
"path": "assets/generated/minigame/surf_1a.png",
|
||||
"width": 40
|
||||
},
|
||||
"intro": {
|
||||
"height": 96,
|
||||
"introPikaFrames": [
|
||||
"assets/generated/minigame/intro_pika_0.png",
|
||||
"assets/generated/minigame/intro_pika_1.png",
|
||||
"assets/generated/minigame/intro_pika_2.png",
|
||||
"assets/generated/minigame/intro_pika_3.png"
|
||||
],
|
||||
"path": "assets/generated/minigame/surf_1c.png",
|
||||
"source": "data/sprite_anims/surfing_pikachu_oam.asm .IntroPikachu",
|
||||
"width": 96
|
||||
},
|
||||
"oam": {
|
||||
"height": 128,
|
||||
"path": "assets/generated/minigame/surf_1b.png",
|
||||
"width": 128
|
||||
},
|
||||
"titleBg": {
|
||||
"height": 144,
|
||||
"path": "assets/generated/minigame/title_bg.png",
|
||||
"width": 160
|
||||
}
|
||||
},
|
||||
"source": "engine/minigame/surfing_pikachu.asm (DrawSurfingPikachuMinigameIntroBackground), gfx/surfing_pikachu.asm"
|
||||
},
|
||||
"tilePairs": {
|
||||
"land": [
|
||||
{
|
||||
@@ -26288,6 +26321,22 @@
|
||||
32,
|
||||
25380
|
||||
],
|
||||
"SurfingMinigame_BeachIntroTilemap": [
|
||||
62,
|
||||
20668
|
||||
],
|
||||
"SurfingMinigame_TitleTilemap": [
|
||||
62,
|
||||
20936
|
||||
],
|
||||
"SurfingMinigame_ToSurfRadTilemap": [
|
||||
62,
|
||||
20923
|
||||
],
|
||||
"SurfingMinigame_UseControlPadTilemap": [
|
||||
62,
|
||||
20908
|
||||
],
|
||||
"SurfingPikachuSprite": [
|
||||
63,
|
||||
28143
|
||||
|
||||
Reference in New Issue
Block a user