mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 08:21:02 +02:00
195 lines
7.5 KiB
Lua
195 lines
7.5 KiB
Lua
-- The egg hatch cutscene's clock, sprite lifetimes and pic placement.
|
|
--
|
|
-- luajit tests/gen2_egg_hatch_anim_test.lua
|
|
--
|
|
-- Everything here is geometry or bookkeeping the ASM states outright, so it
|
|
-- runs with no cache and no window: EggHatchAnim only reaches love.graphics
|
|
-- from its draw path, and the stub below is enough for that.
|
|
--
|
|
-- What it pins:
|
|
--
|
|
-- * AnimSeq_RevealNewMon's `cp $80 / jr nc, .finish_EggShell` really does
|
|
-- retire the shell fragment (engine/sprite_anims/functions.asm:1270-1305).
|
|
-- They used to keep drawing, frozen at their last offset, for the ~113
|
|
-- frames left in Hatch_ShellFragmentLoop.
|
|
-- * .OAMData_1x1_Palette0's own -4 on each axis is in the screen position
|
|
-- (data/sprite_anims/oam.asm:112-114).
|
|
-- * The pic sits where PadFrontpic put it (engine/gfx/load_pics.asm:342),
|
|
-- which for a 6-wide pic is a whole tile in, not half of one.
|
|
-- * hSCX and wGlobalAnimXOffset move the background and the objects the SAME
|
|
-- way during a wobble (engine/pokemon/breeding.asm:707-719), so a crack
|
|
-- does not slide across the shell it is sitting on.
|
|
-- * The EGG has a palette row of its own (data/pokemon/palettes.asm:530).
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local S = require("tests.harness").suite("gen2 egg hatch anim")
|
|
local check, eq = S.check, S.eq
|
|
|
|
local EggHatchAnim = require("src.ui.gen2.EggHatchAnim")
|
|
|
|
--------------------------------------------------------------------------
|
|
-- A love.graphics that records instead of drawing. No newShader, so
|
|
-- GbcPalette.available() is false and the palette shader stays out of the way.
|
|
--------------------------------------------------------------------------
|
|
|
|
local draws = {}
|
|
local ox, oy = 0, 0
|
|
local saved = {}
|
|
local function fakeImage(w, h)
|
|
return {
|
|
getWidth = function() return w end,
|
|
getHeight = function() return h end,
|
|
getDimensions = function() return w, h end,
|
|
}
|
|
end
|
|
|
|
_G.love = {
|
|
graphics = {
|
|
setColor = function() end,
|
|
rectangle = function() end,
|
|
push = function() saved[#saved + 1] = { ox, oy } end,
|
|
pop = function()
|
|
local t = table.remove(saved)
|
|
ox, oy = t[1], t[2]
|
|
end,
|
|
translate = function(x, y) ox = ox + (x or 0) oy = oy + (y or 0) end,
|
|
scale = function() end,
|
|
newQuad = function(x, y, w, h) return { x = x, y = y, w = w, h = h } end,
|
|
draw = function(_image, a, b, c)
|
|
if type(a) == "table" then
|
|
-- G.draw(image, quad, x, y, ...): a sprite object.
|
|
draws[#draws + 1] = { kind = "sprite", quad = a, x = ox + b, y = oy + c }
|
|
else
|
|
draws[#draws + 1] = { kind = "pic", x = ox + a, y = oy + b }
|
|
end
|
|
end,
|
|
},
|
|
}
|
|
|
|
local function render(anim)
|
|
draws = {}
|
|
anim:drawPanel()
|
|
local out = { pic = nil, sprites = {} }
|
|
for _, d in ipairs(draws) do
|
|
if d.kind == "pic" then out.pic = d
|
|
else out.sprites[#out.sprites + 1] = d end
|
|
end
|
|
return out
|
|
end
|
|
|
|
--------------------------------------------------------------------------
|
|
-- A game with a 5x5 egg pic, a 6x6 hatchling and the two-tile shell sheet.
|
|
--------------------------------------------------------------------------
|
|
|
|
local EGG_COLORS = { { 240, 208, 88 }, { 184, 128, 0 } }
|
|
local MON_COLORS = { { 100, 100, 100 }, { 40, 40, 40 } }
|
|
|
|
local function newAnim()
|
|
local data = {
|
|
-- SENTRET's frontpic is 48px, the width the old centring rule got wrong.
|
|
pokemon = { SENTRET = { spriteFront = "sentret.png" } },
|
|
gen2MenuGfx = { eggHatch = {
|
|
egg = "egg.png", shell = "shell.png", shellTiles = 2,
|
|
} },
|
|
gen2Palettes = { pokemon = {
|
|
EGG = { normal = EGG_COLORS, shiny = EGG_COLORS },
|
|
SENTRET = { normal = MON_COLORS, shiny = MON_COLORS },
|
|
} },
|
|
}
|
|
local anim = EggHatchAnim.new({ data = data }, {
|
|
mon = { species = "SENTRET", shiny = false },
|
|
species = "SENTRET",
|
|
})
|
|
anim.picCache["egg.png"] = fakeImage(40, 40)
|
|
anim.picCache["sentret.png"] = fakeImage(48, 48)
|
|
anim.picCache["shell.png"] = fakeImage(8, 16)
|
|
return anim
|
|
end
|
|
|
|
--------------------------------------------------------------------------
|
|
-- Sprite lifetimes
|
|
--------------------------------------------------------------------------
|
|
|
|
local anim = newAnim()
|
|
|
|
-- Hatch_InitShellFragments lays ten `shell_fragment` rows, and `.done` has
|
|
-- already run ClearSprites over the cracks by then.
|
|
local frames, spawned, cleared = 0, nil, nil
|
|
while not anim.done and frames < 5000 do
|
|
anim:update(1 / 60)
|
|
frames = frames + 1
|
|
if not spawned and #anim.sprites == 10 and anim.showMon then
|
|
spawned = frames
|
|
end
|
|
if spawned and not cleared and #anim.sprites == 0 then cleared = frames end
|
|
end
|
|
|
|
eq(frames, 482, "the whole sequence is 482 frames of DelayFrames operands")
|
|
check(spawned ~= nil, "the ten shell fragments go up at `.done`")
|
|
check(cleared ~= nil, "and every one of them leaves the screen again")
|
|
-- var1 starts at 0 and gains 8 a frame; the seventeenth frame is the first to
|
|
-- see $80 at the top of AnimSeq_RevealNewMon and take .finish_EggShell.
|
|
eq(cleared and (cleared - spawned), 17,
|
|
"sixteen frames of flight, then DeinitializeSprite")
|
|
eq(#anim.sprites, 0, "and nothing is left drawing when the loop ends")
|
|
|
|
--------------------------------------------------------------------------
|
|
-- Screen positions
|
|
--------------------------------------------------------------------------
|
|
|
|
anim = newAnim()
|
|
|
|
-- The egg at hlcoord 7, 4 with PadFrontpic's `.five` offset of (1, 2) tiles.
|
|
local frame = render(anim)
|
|
check(frame.pic ~= nil, "the egg pic draws while the shell is up")
|
|
eq(frame.pic and frame.pic.x, 7 * 8 + 8, "the 5-wide egg's x")
|
|
eq(frame.pic and frame.pic.y, 4 * 8 + 16, "the 5-wide egg's y")
|
|
|
|
-- EggHatch_CrackShell's first surviving round: `ld e, 11 * TILE_WIDTH` and
|
|
-- `add 9 * TILE_WIDTH`, less the -8 / -16 OAM bias and the oamset's own -4.
|
|
anim:crackShell(2)
|
|
eq(#anim.sprites, 1, "round 2 is the first round to crack the shell")
|
|
frame = render(anim)
|
|
eq(#frame.sprites, 1, "and the crack draws")
|
|
eq(frame.sprites[1] and frame.sprites[1].x, 11 * 8 - 8 - 4, "the crack's x")
|
|
eq(frame.sprites[1] and frame.sprites[1].y, 9 * 8 - 16 - 4, "the crack's y")
|
|
|
|
-- A wobble half moves BOTH layers two pixels left, so the crack keeps its
|
|
-- place on the shell instead of sliding across it.
|
|
local still = frame
|
|
anim.shakeX = 2
|
|
local shaken = render(anim)
|
|
eq(shaken.pic.x - still.pic.x, -2, "hSCX = +2 slides the background left")
|
|
eq(shaken.sprites[1].x - still.sprites[1].x, -2,
|
|
"and wGlobalAnimXOffset = -2 takes the crack with it")
|
|
eq(shaken.pic.y, still.pic.y, "the wobble is horizontal only")
|
|
anim.shakeX = 0
|
|
|
|
-- The hatchling at hlcoord 6, 3 with `.six`'s offset of (1, 1) tiles: a whole
|
|
-- blank column, then one blank tile above each pic column.
|
|
anim.showMon = true
|
|
frame = render(anim)
|
|
eq(frame.pic and frame.pic.x, 6 * 8 + 8, "the 6-wide hatchling's x")
|
|
eq(frame.pic and frame.pic.y, 3 * 8 + 8, "the 6-wide hatchling's y")
|
|
|
|
--------------------------------------------------------------------------
|
|
-- Palettes
|
|
--------------------------------------------------------------------------
|
|
|
|
anim = newAnim()
|
|
local colors = anim:picColors()
|
|
check(colors ~= nil, "the egg has a palette of its own, not flat GB greys")
|
|
eq(colors and colors[2] and colors[2][1], EGG_COLORS[1][1],
|
|
"and it is the EGG row _CGB_Evolution loads for wPlayerHPPal = EGG")
|
|
anim.showMon = true
|
|
colors = anim:picColors()
|
|
eq(colors and colors[2] and colors[2][1], MON_COLORS[1][1],
|
|
"the hatchling brings its own once the pic swaps")
|
|
|
|
-- A cache built before the extractor grew the EGG row still runs.
|
|
anim = newAnim()
|
|
anim.palettes.pokemon.EGG = nil
|
|
eq(anim:picColors(), nil, "an older cache simply draws in the raw shades")
|
|
|
|
S.finish()
|