mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
23617f43b4
CLOSES #2290, CLOSES #2338, CLOSES #2186, CLOSES #2305, CLOSES #2332, CLOSES #2320, CLOSES #2344, CLOSES #2291, CLOSES #2335, CLOSES #2340, CLOSES #2385, CLOSES #2352, CLOSES #2378, CLOSES #2403, CLOSES #2404, CLOSES #2405, CLOSES #2367, CLOSES #2037, CLOSES #2177, CLOSES #2368, CLOSES #2382, CLOSES #1951
209 lines
6.8 KiB
Lua
209 lines
6.8 KiB
Lua
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local T = require("tests.harness")
|
|
local check, eq = T.check, T.eq
|
|
love = love or require("tests.love_stub")
|
|
|
|
local Sound = require("src.core.Sound")
|
|
local Timing = require("src.core.Timing")
|
|
local IntroMovie = require("src.ui.IntroMovie")
|
|
|
|
local realPlay = Sound.play
|
|
Sound.play = function() end
|
|
|
|
local WAVES_START, SPLASH_FRAMES = 134, 318
|
|
|
|
local function newGame()
|
|
local input = { pressed = {}, down = {} }
|
|
function input:wasPressed(b) return self.pressed[b] or false end
|
|
function input:isDown(b) return self.down[b] or false end
|
|
return {
|
|
input = input,
|
|
data = { field = { intro = {
|
|
fallingStar = { path = "tests/fixtures/missing_fallingstar_2385.png" },
|
|
fallingStarBlink = { path = "tests/fixtures/missing_fallingstarblink_2385.png" },
|
|
} } },
|
|
stack = { pop = function() end },
|
|
}
|
|
end
|
|
|
|
local function starDraws(m, t)
|
|
m.phase, m.timer = 2, t
|
|
local calls = {}
|
|
local realDraw = love.graphics.draw
|
|
love.graphics.draw = function(img, x, y, ...)
|
|
if img == m.smallStar or img == m.smallStarBlink then
|
|
calls[#calls + 1] = { img = img, x = x, y = y }
|
|
end
|
|
end
|
|
local ok, err = pcall(m.drawSplash, m)
|
|
love.graphics.draw = realDraw
|
|
check(ok, "drawSplash runs headless" .. (ok and "" or (": " .. tostring(err))))
|
|
return calls
|
|
end
|
|
|
|
local function at(calls, x)
|
|
for _, c in ipairs(calls) do if c.x == x then return c end end
|
|
end
|
|
|
|
do
|
|
local m = IntroMovie.new(newGame())
|
|
check(m.smallStar and m.smallStarBlink and m.smallStar ~= m.smallStarBlink,
|
|
"both falling-star looks load")
|
|
local c = starDraws(m, WAVES_START)
|
|
local s = at(c, 40)
|
|
eq(s and s.y, 89, "wave 1 first shows one row below $68 (MoveDownSmallStars runs before the delay)")
|
|
eq(s and s.img, m.smallStarBlink, "first substep shows rOBP1 $04 (toggled once before the delay)")
|
|
s = at(starDraws(m, WAVES_START + 3), 40)
|
|
eq(s and s.y, 90, "second substep moves one more row")
|
|
eq(s and s.img, m.smallStar, "second substep is back on rOBP1 $A4")
|
|
s = at(starDraws(m, WAVES_START + 24), 48)
|
|
eq(s and s.y, 89, "wave 2 enters one row below $68 on its first substep")
|
|
s = at(starDraws(m, SPLASH_FRAMES - 1), 52)
|
|
eq(s and s.y, 88 + 48 - 24, "wave 4 rests after 24 moves")
|
|
eq(s and s.img, m.smallStar, "the 40-frame tail holds rOBP1 $A4 after 48 toggles")
|
|
end
|
|
|
|
local function fightMovie()
|
|
local game = newGame()
|
|
local m = IntroMovie.new(game)
|
|
m:startPhase(3)
|
|
return m, game.input
|
|
end
|
|
|
|
local function tick(m, input, n, press)
|
|
for i = 1, n or 1 do
|
|
input.pressed = (i == 1 and press) or {}
|
|
m:update(1 / 60)
|
|
end
|
|
input.pressed = {}
|
|
end
|
|
|
|
local function runToOp(m, input, idx)
|
|
for _ = 1, 2000 do
|
|
if m.opIndex >= idx then return true end
|
|
tick(m, input, 1)
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function fadeRun(m, input)
|
|
local steps = {}
|
|
for _ = 1, 40 do
|
|
tick(m, input, 1)
|
|
if m.phase == 4 then steps[#steps + 1] = 4 break end
|
|
steps[#steps + 1] = m.fadeStep or 0
|
|
end
|
|
return steps
|
|
end
|
|
|
|
local function isStepped(steps)
|
|
if #steps ~= 24 then return false end
|
|
for i = 1, 23 do
|
|
if steps[i] ~= math.floor((i - 1) / 8) + 1 then return false end
|
|
end
|
|
return steps[24] == 4
|
|
end
|
|
|
|
do
|
|
local m, input = fightMovie()
|
|
tick(m, input, Timing.DELAY3, { a = true })
|
|
eq(m.opIndex, 1, "A during PlayShootingStar's Delay3 is not polled")
|
|
eq(m.fadeStep, nil, "the deaf Delay3 starts no fade")
|
|
tick(m, input, 1)
|
|
local x = m.gengarX
|
|
tick(m, input, 1, { a = true })
|
|
eq(m.gengarX, x, "scroll-in odd frame moves nothing")
|
|
local steps = fadeRun(m, input)
|
|
check(isStepped(steps), "an interrupted scroll-in still runs GBFadeOutToWhite: 8 frames FadePal6, 8 FadePal7, then white ("
|
|
.. table.concat(steps, ",") .. ")")
|
|
end
|
|
|
|
do
|
|
local m, input = fightMovie()
|
|
check(runToOp(m, input, 3), "reached the first hip animation")
|
|
tick(m, input, 1, { a = true })
|
|
tick(m, input, 3)
|
|
eq(m.fadeStep, nil, "AnimateIntroNidorino's DelayFrames 5 is deaf")
|
|
eq(m.phase, 3, "the fight goes on after a press inside an animation")
|
|
end
|
|
|
|
do
|
|
local m, input = fightMovie()
|
|
check(runToOp(m, input, 6), "reached the 10-frame wait")
|
|
tick(m, input, 1, { start = true })
|
|
check(isStepped(fadeRun(m, input)), "START inside a CheckForUserInterruption wait ends the scene into the stepped fade")
|
|
end
|
|
|
|
do
|
|
local m, input = fightMovie()
|
|
check(runToOp(m, input, 12), "reached the raise pose")
|
|
local x = m.gengarX
|
|
tick(m, input, 1)
|
|
eq(m.opIndex, 14, "pose and SFX_INTRO_RAISE chain into the move on one frame")
|
|
eq(m.gengarX, x - 2, "the raise's first SCX step lands on that frame")
|
|
tick(m, input, 1, { a = true })
|
|
eq(m.gengarX, x - 2, "a press on the step's second poll frame stops the move")
|
|
eq(m.opIndex, 15, "IntroMoveMon returns early and the scene moves on to the next wait")
|
|
eq(m.fadeStep, nil, "an interrupted Gengar move does not end the scene")
|
|
end
|
|
|
|
do
|
|
local m, input = fightMovie()
|
|
check(runToOp(m, input, 6), "reached the 10-frame wait again")
|
|
input.down = { b = true }
|
|
tick(m, input, 2)
|
|
eq(m.fadeStep, nil, "B alone never interrupts")
|
|
input.down = { up = true, select = true, b = true }
|
|
tick(m, input, 1)
|
|
input.down = {}
|
|
check(isStepped(fadeRun(m, input)), "holding exactly UP+SELECT+B interrupts (home/overworld.asm:2404)")
|
|
end
|
|
|
|
do
|
|
local m, input = fightMovie()
|
|
m.lastPollHeld = { a = true }
|
|
input.down = { up = true, select = true, b = true, a = true }
|
|
check(runToOp(m, input, 6), "reached the wait with A held too")
|
|
tick(m, input, 2)
|
|
eq(m.fadeStep, nil, "UP+SELECT+B with another button held is not the combo")
|
|
end
|
|
|
|
do
|
|
local m, input = fightMovie()
|
|
check(runToOp(m, input, 3), "reached the first hip animation for the held press")
|
|
input.down = { a = true }
|
|
tick(m, input, 3)
|
|
eq(m.fadeStep, nil, "A pressed and held inside a deaf animation is not seen there")
|
|
check(runToOp(m, input, 6), "reached the next CheckForUserInterruption wait")
|
|
tick(m, input, 1)
|
|
input.down = {}
|
|
check(isStepped(fadeRun(m, input)),
|
|
"A still held at the next poll is new against hJoyLast and interrupts (home/joypad2.asm:16)")
|
|
end
|
|
|
|
do
|
|
local m, input = fightMovie()
|
|
m.lastPollHeld = { a = true }
|
|
input.down = { a = true }
|
|
check(runToOp(m, input, 7), "A held since the previous poll runs straight through the wait")
|
|
eq(m.fadeStep, nil, "a button already held at the last poll is not a new press")
|
|
input.down = {}
|
|
end
|
|
|
|
local function modMovie()
|
|
local game = newGame()
|
|
game.data.field.intro.gamefreakLogo = { path = "mods/demo/gamefreak_logo.png" }
|
|
game.data.field.intro.gamefreakText = { path = "tests/fixtures/missing_gftext_2385.png" }
|
|
return IntroMovie.new(game)
|
|
end
|
|
|
|
do
|
|
local m = modMovie()
|
|
eq(m.logoPath, nil, "mod-supplied logo art keeps the raw draw path")
|
|
eq(m.gfTextPath, "tests/fixtures/missing_gftext_2385.png", "ROM text art still bakes through rOBP0")
|
|
end
|
|
|
|
Sound.play = realPlay
|
|
T.finish("intro_splash_fight_timing_bug2385")
|