mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
153 lines
8.1 KiB
Lua
153 lines
8.1 KiB
Lua
#!/usr/bin/env luajit
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
require("tests.game3_cache").mountOrSkip("game3_battle_catch_ball_open_test")
|
|
require("tests.fixture_data.game3_items").install()
|
|
|
|
local failed = 0
|
|
local function check(cond, msg)
|
|
if cond then
|
|
print("[ok] " .. msg)
|
|
else
|
|
failed = failed + 1
|
|
print("[FAIL] " .. msg)
|
|
end
|
|
end
|
|
|
|
local BallOpen = require("src.core.game3.battle.ball_open")
|
|
local sine = {}
|
|
for i = 0, 319 do sine[i + 1] = math.floor(math.sin(i * math.pi / 128) * 256 + 0.5) end
|
|
local colors = {
|
|
{ 31, 22, 30 }, { 16, 23, 30 }, { 23, 30, 20 }, { 31, 31, 15 }, { 23, 20, 28 }, { 21, 31, 25 },
|
|
{ 12, 25, 30 }, { 30, 27, 10 }, { 31, 24, 16 }, { 29, 30, 30 }, { 31, 17, 10 }, { 31, 9, 10 },
|
|
}
|
|
BallOpen.setData({ fadeColors = colors, sine = sine, sheetW = 64, sheetH = 8 }, false)
|
|
|
|
local Audio = require("src.core.game3.audio")
|
|
local SE = require("src.core.game3.se_ids")
|
|
local Catching = require("src.core.game3.battle.catching")
|
|
local Anim = require("src.core.game3.battle.anim")
|
|
local CatchSeq = require("src.core.game3.battle.catch_seq")
|
|
local Ui = require("src.core.game3.battle.ui")
|
|
|
|
local frame = 0
|
|
local sounds = {}
|
|
Audio.playSe = function(id) sounds[#sounds + 1] = { id = id, f = frame } return true end
|
|
Audio.stopAll = function() sounds[#sounds + 1] = { stop = true, f = frame } end
|
|
Audio.waitSe = function(id, cb) sounds[#sounds + 1] = { waitSe = id, f = frame, cb = cb } end
|
|
Audio.playSong = function(id) sounds[#sounds + 1] = { song = id, f = frame } return true end
|
|
Catching.storeCaught = function() return { firstTimeCaught = false } end
|
|
Ui.battlerSpriteCenter = function(_, _, base) return base.x, base.y end
|
|
|
|
local function se_frames(id)
|
|
local out = {}
|
|
for _, s in ipairs(sounds) do
|
|
if s.id == id then out[#out + 1] = s.f end
|
|
end
|
|
return table.concat(out, ",")
|
|
end
|
|
|
|
local function run(caught, shakes)
|
|
Anim.reset({ headless = false })
|
|
Anim.present("enemy").visible = true
|
|
sounds = {}
|
|
local msgs = {}
|
|
frame = 0
|
|
local st = { enemy = { species = 16, mon = { species = 16, name = "PIDGEY" } } }
|
|
CatchSeq.begin(st, 4, caught, shakes, {
|
|
pushMsg = function(t) msgs[#msgs + 1] = { t = t, f = frame } end,
|
|
headless = false,
|
|
session = { name = "RED" },
|
|
})
|
|
CatchSeq.update()
|
|
CatchSeq._waitingMsg = false
|
|
CatchSeq.update()
|
|
local log = {}
|
|
for f = 1, 1200 do
|
|
frame = f
|
|
Anim.update(1 / 60)
|
|
local b = CatchSeq._ball
|
|
local s = Anim.stage().ball
|
|
local p = Anim.present("enemy")
|
|
log[f] = {
|
|
sx = s.x, sy = s.y, visible = s.visible, ballFrame = s.frame, x2 = b.x2,
|
|
rotation = b.aff.rotation, blend = b.blend.coeff, alpha = b.alpha,
|
|
scale = p.scale, oy = p.oy, monVisible = p.visible,
|
|
mon = (BallOpen.monBlend("enemy")), particles = BallOpen.particleCount(),
|
|
finished = b.finished, invisibleStars = 0,
|
|
}
|
|
for _, q in ipairs(BallOpen.particles()) do
|
|
if q.invisible then log[f].invisibleStars = log[f].invisibleStars + 1 end
|
|
end
|
|
CatchSeq.update()
|
|
if not CatchSeq.busy() then break end
|
|
end
|
|
return log, msgs
|
|
end
|
|
|
|
print("[test] throw arc and ball open (pokefirered/src/battle_anim_special.c:810-863)")
|
|
local log, msgs = run(true, 4)
|
|
check(sounds[1] and sounds[1].id == SE.SE_BALL_THROW and sounds[1].f == 0, "SE_BALL_THROW when the ball sprite is created")
|
|
check(log[1].sx == 32 and log[1].sy == 80, "ball starts at (32,80)")
|
|
check(log[35].ballFrame == 0 and log[36].ballFrame == 1 and log[36].sx == 175 and log[36].sy == 24,
|
|
"34-step arc ends at (175,24) and the ball opens on frame 36")
|
|
check(se_frames(SE.SE_BALL_OPEN) == "36", "SE_BALL_OPEN on the open frame only")
|
|
check(log[36].particles == 1 and log[51].particles == 16, "poke ball particles spawn one per frame from the open frame")
|
|
check(log[36].mon == 0 and log[44].mon == 8 and log[52].mon == 16 and log[90].mon == 16,
|
|
"mon fades to ball color 0..16 over 17 frames and holds (LaunchBallFadeMonTask unfadeLater=FALSE)")
|
|
|
|
print("[test] shrink into ball (pokefirered/src/battle_anim_special.c:865-919)")
|
|
check(log[47].scale == 1 and math.abs(log[48].scale - 256 / 288) < 1e-9, "shrink starts 11 frames after the open")
|
|
check(se_frames(SE.SE_BALL_TRADE) == "57", "SE_BALL_TRADE on the 11th shrink tick")
|
|
check(log[75].monVisible and log[75].oy == -16 and log[76].monVisible == false, "28 rot-scale steps rise 16px, mon hidden on frame 76")
|
|
check(log[77].ballFrame == 1 and log[82].ballFrame == 0, "ball anim 2 closes after 5 frames")
|
|
|
|
print("[test] bounce and shakes (pokefirered/src/battle_anim_special.c:921-1160)")
|
|
check(se_frames(SE.SE_BALL_BOUNCE_1) == "104" and se_frames(SE.SE_BALL_BOUNCE_2) == "130"
|
|
and se_frames(SE.SE_BALL_BOUNCE_3) == "152" and se_frames(SE.SE_BALL_BOUNCE_4) == "172", "four bounces at 104/130/152/172")
|
|
check(log[100].sy < 64 and log[172].sy == 64 and log[173].sy == 64, "ball settles at y=64")
|
|
check(se_frames(SE.SE_BALL) == "203,262,321", "three shakes 59 frames apart")
|
|
check(log[203].rotation == 64768 and log[204].rotation == 64000, "shake rotates -3 per frame (BALL_ROTATE_RIGHT)")
|
|
check(log[205].x2 == 0 and log[206].x2 == 1, "shake x2 steps by the 0xB0 subpixel counter")
|
|
|
|
print("[test] capture click and stars (pokefirered/src/battle_anim_special.c:1171-1303)")
|
|
check(se_frames(SE.SE_BALL_CLICK) == "390", "SE_BALL_CLICK 40 frames after the third shake ends")
|
|
check(log[390].particles == 3 and log[390].invisibleStars == 3 and log[391].invisibleStars == 0
|
|
and log[413].particles == 3 and log[414].particles == 0, "3 capture stars flicker for 24 arc steps")
|
|
check(log[389].blend == 0 and log[413].blend == 6 and log[414].blend == 4 and log[418].blend == 2 and log[422].blend == 0,
|
|
"ball blended 6 to black, then faded back with delay 2")
|
|
check(se_frames(319) == "445", "MUS_CAUGHT_INTRO 95 frames into the click")
|
|
check(log[667].alpha == 1 and log[668].alpha == 15 / 16 and log[698].alpha == 0 and log[698].visible and log[699].visible == false,
|
|
"ball alpha blends out over 32 frames then hides")
|
|
check(log[700].finished ~= true and log[701].finished == true, "anim signals end on frame 701")
|
|
check(msgs[2] and msgs[2].f == 701 and msgs[2].t:match("^Gotcha!"), "Gotcha printed after the anim ends")
|
|
local waited = false
|
|
for _, s in ipairs(sounds) do if s.waitSe == 319 and s.f == 701 then waited = s.cb end end
|
|
check(waited ~= false, "MUS_CAUGHT waits for MUS_CAUGHT_INTRO")
|
|
if waited then waited() end
|
|
check(sounds[#sounds].song == 322, "MUS_CAUGHT after the intro SE")
|
|
|
|
print("[test] breakout after 1 shake (pokefirered/src/battle_anim_special.c:1305-1354)")
|
|
log, msgs = run(false, 1)
|
|
check(se_frames(SE.SE_BALL) == "203" and se_frames(SE.SE_BALL_OPEN) == "36,263", "ball reopens 31 frames after the only shake")
|
|
check(log[262].monVisible == false and log[263].monVisible and log[263].mon == 16, "mon reappears fully tinted on the breakout frame")
|
|
check(math.abs(log[263].scale - 0x28 / 256) < 1e-9 and math.abs(log[264].scale - 0x3A / 256) < 1e-9 and log[275].scale == 1,
|
|
"BATTLER_AFFINE_EMERGE 0x28 + 12 x 0x12")
|
|
check(log[264].oy == 14 and log[275].oy == 2 and log[276].oy == 0, "mon y2 falls 288/256 per frame")
|
|
check(log[263].particles == 1 and log[264].particles == 2, "breakout burst spawns particles again")
|
|
check(log[273].visible and log[274].visible == false, "ball hides once open anim ends")
|
|
check(log[276].finished ~= true and log[277].finished == true and msgs[2] and msgs[2].f == 277
|
|
and msgs[2].t == "Aww!\nIt appeared to be caught!", "breakout text after the anim ends")
|
|
for _ = 1, 60 do BallOpen.tick() end
|
|
check((BallOpen.monBlend("enemy")) == 0 and BallOpen._mon.enemy == nil and not BallOpen.active(), "mon unfades after the breakout")
|
|
|
|
print("[test] breakout with no shakes and after 3 shakes")
|
|
log, msgs = run(false, 0)
|
|
check(se_frames(SE.SE_BALL) == "" and se_frames(SE.SE_BALL_OPEN) == "36,204" and msgs[2] and msgs[2].f == 218,
|
|
"BALL_NO_SHAKES breaks out 31 frames after the last bounce")
|
|
log, msgs = run(false, 3)
|
|
check(se_frames(SE.SE_BALL) == "203,262,321" and se_frames(SE.SE_BALL_OPEN) == "36,381" and msgs[2] and msgs[2].f == 395
|
|
and msgs[2].t == "Shoot!\nIt was so close, too!", "BALL_3_SHAKES_FAIL breaks out after the third shake")
|
|
|
|
print(failed == 0 and "[ok] all" or ("[FAIL] " .. failed .. " failed"))
|
|
os.exit(failed == 0 and 0 or 1)
|