mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
203 lines
7.4 KiB
Lua
203 lines
7.4 KiB
Lua
-- ../pokecrystal/engine/battle/battle_transition.asm:100-122
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
love = require("tests.love_stub")
|
|
|
|
local T = require("tests.harness")
|
|
local Transition = require("src.ui.gen2.BattleTransition")
|
|
local GbcPalette = require("src.render.GbcPalette")
|
|
|
|
local function row(t) return table.concat(t, "") end
|
|
|
|
do
|
|
local tile = Transition.squareTile()
|
|
T.eq(#tile, 8, "BATTLETRANSITION_SQUARE is one 8x8 tile")
|
|
T.eq(tile[1], "33333333", "row 1 is the colour-3 border")
|
|
T.eq(tile[8], "33333333", "row 8 too")
|
|
T.eq(tile[2], "30000003", "row 2: border, then colour 0")
|
|
T.eq(tile[2]:sub(2, 2), "0", "...the interior starts at colour 0")
|
|
T.eq(tile[5], "31122113", "row 5 carries the 1/2 diagonal")
|
|
T.eq(tile[7], "32222223", "row 7 is all colour 2 inside the border")
|
|
end
|
|
|
|
do
|
|
local shades = Transition.squareShades(Transition.flashByte({ 3, 3, 2, 1 }))
|
|
T.eq(row(shades[1]), "44444444", "$F9: the border stays colour 3")
|
|
T.eq(row(shades[2]), "42222224", "$F9 row 2 == 31111113 (1-based shades)")
|
|
T.eq(row(shades[5]), "43344334", "$F9 row 5 == 32233223")
|
|
T.eq(row(shades[6]), "43444434", "$F9 row 6 == 32333323")
|
|
T.eq(row(shades[7]), "44444444", "$F9 row 7 == 33333333")
|
|
|
|
local black = Transition.squareShades(Transition.flashByte({ 3, 3, 3, 3 }))
|
|
for y = 1, 8 do
|
|
T.eq(row(black[y]), "44444444", "$FF row " .. y .. " is all colour 3")
|
|
end
|
|
local white = Transition.squareShades(Transition.flashByte({ 0, 0, 0, 0 }))
|
|
for y = 1, 8 do
|
|
T.eq(row(white[y]), "11111111", "$00 row " .. y .. " is all colour 0")
|
|
end
|
|
local id = Transition.squareShades(nil)
|
|
T.eq(row(id[2]), "41111114", "identity row 2 is the tile itself")
|
|
T.eq(row(id[6]), "42333324", "identity row 6 is the tile itself")
|
|
local ninety = Transition.squareShades(Transition.flashByte({ 2, 1, 0, 0 }))
|
|
T.eq(row(ninety[1]), "33333333", "$90: border colour 2")
|
|
T.eq(row(ninety[2]), "31111113", "$90: interior colour 0")
|
|
T.eq(row(ninety[7]), "32222223", "$90: the colour-2 row shows colour 1")
|
|
end
|
|
|
|
do
|
|
local ramp = Transition.TRAINER_PAL
|
|
local mode = GbcPalette.mode
|
|
GbcPalette.setMode("gbc")
|
|
local src, dst, count = GbcPalette.remapTable({},
|
|
Transition.flashByte({ 3, 3, 2, 1 }), {}, ramp)
|
|
GbcPalette.setMode(mode)
|
|
T.eq(count, 4, "the trainer ramp's own four colours enter the flash table")
|
|
local function key(c) return c[1] .. "," .. c[2] .. "," .. c[3] end
|
|
local got = {}
|
|
for i = 1, count do got[key(src[i])] = key(dst[i]) end
|
|
T.eq(got[key(ramp[1])], key(ramp[2]), "$F9: ramp c0 -> c1")
|
|
T.eq(got[key(ramp[2])], key(ramp[3]), "$F9: ramp c1 -> c2")
|
|
T.eq(got[key(ramp[3])], key(ramp[4]), "$F9: ramp c2 -> c3")
|
|
T.eq(got[key(ramp[4])], key(ramp[4]), "$F9: ramp c3 -> c3")
|
|
end
|
|
|
|
do
|
|
T.eq(Transition.FLASH_SLOT_FRAMES, 25,
|
|
"a Flash slot is 12 entries x 2 plus the terminator frame")
|
|
T.eq(Transition.FLASH_FRAMES, 75, "three slots: 75 frames, not 72")
|
|
end
|
|
|
|
local function make(opts)
|
|
opts.environment = opts.environment or "TOWN"
|
|
opts.world = { map = { def = {} }, daytime = "DAY", npcs = {}, ghosts = {},
|
|
vm = { lastTalked = 3, running = function() return true end } }
|
|
local state = Transition.new(nil, opts)
|
|
return state, opts.world
|
|
end
|
|
|
|
do
|
|
local state = make({ trainer = true })
|
|
state:update()
|
|
state:update()
|
|
T.eq(state.phase, "flash", "flash after two ball frames")
|
|
local function palAt(f)
|
|
state.frame = f
|
|
return state:flashPal()
|
|
end
|
|
T.same(palAt(0), { 3, 3, 2, 1 }, "frame 0 is $F9")
|
|
T.same(palAt(1), { 3, 3, 2, 1 }, "frame 1 is still $F9")
|
|
T.same(palAt(2), { 3, 3, 3, 2 }, "frame 2 is $FE")
|
|
T.same(palAt(12), { 2, 1, 0, 0 }, "frame 12 is $90")
|
|
T.same(palAt(22), { 3, 2, 1, 0 }, "frame 22 is the identity")
|
|
T.same(palAt(24), { 3, 2, 1, 0 },
|
|
"frame 24: the terminator row holds the identity one more frame")
|
|
T.same(palAt(25), { 3, 3, 2, 1 }, "frame 25: the second slot restarts at $F9")
|
|
T.same(palAt(74), { 3, 2, 1, 0 }, "frame 74 is the last identity frame")
|
|
state.frame = 0
|
|
local frames = 0
|
|
while state.phase == "flash" do
|
|
frames = frames + 1
|
|
state:update()
|
|
end
|
|
T.eq(frames, 75, "75 flash frames are drawn before the outro")
|
|
end
|
|
|
|
local function outroTimeline(style, opts)
|
|
opts = opts or {}
|
|
opts.style = style
|
|
opts.trainer = opts.trainer ~= false
|
|
local state, world = make(opts)
|
|
while state.phase ~= "outro" do state:update() end
|
|
local events = {}
|
|
local function count(set)
|
|
local n = 0
|
|
for _ in pairs(set) do n = n + 1 end
|
|
return n
|
|
end
|
|
local lastBlack = 0
|
|
local firstFilter
|
|
local frame = 0
|
|
events.black = count(state.black)
|
|
while state.phase == "outro" do
|
|
if world.spriteFilter and not firstFilter then firstFilter = frame end
|
|
state:update()
|
|
frame = frame + 1
|
|
if state.phase == "outro" then
|
|
local n = count(state.black)
|
|
if n ~= lastBlack then
|
|
events[#events + 1] = frame
|
|
lastBlack = n
|
|
end
|
|
end
|
|
end
|
|
return events, frame, firstFilter, state
|
|
end
|
|
|
|
do
|
|
local steps, blackAt, filterAt = outroTimeline("spin")
|
|
T.eq(filterAt, 1, "spin: RespawnPlayerAndOpponent on the SetUpFor frame")
|
|
T.eq(steps[1], 2, "spin: first wedge on outro frame 2")
|
|
T.eq(steps[2], 5, "spin: three frames a wedge")
|
|
T.eq(#steps, 20, "spin: twenty wedges")
|
|
T.eq(steps[20], 59, "spin: last wedge on frame 59")
|
|
T.eq(blackAt, 66, "spin: .end holds four frames, black on 66")
|
|
end
|
|
|
|
do
|
|
local steps, blackAt, filterAt = outroTimeline("zoom")
|
|
T.eq(filterAt, 1, "zoom: respawn on the first box frame")
|
|
T.eq(steps[1], 1, "zoom: no SetUpFor slot, box 1 on frame 1")
|
|
T.eq(steps[2], 5, "zoom: one box per WaitBGMap, four frames")
|
|
T.eq(#steps, 9, "zoom: nine boxes")
|
|
T.eq(steps[9], 33, "zoom: last box on frame 33")
|
|
T.eq(blackAt, 38, "zoom: 36 box frames, one .done frame, black on 38")
|
|
end
|
|
|
|
do
|
|
local steps, blackAt = outroTimeline("speckle",
|
|
{ random = function(n) return math.random(n) - 1 end })
|
|
T.eq(steps[1], 2, "speckle: first pass on frame 2")
|
|
T.eq(#steps, 16, "speckle: sixteen passes, one a frame")
|
|
T.eq(steps[16], 17, "speckle: last pass on frame 17")
|
|
T.eq(blackAt, 22, "speckle: .done holds four frames, black on 22")
|
|
end
|
|
|
|
do
|
|
local state = make({ trainer = true, environment = "CAVE",
|
|
playerLevel = 10, enemyLevel = 5 })
|
|
T.eq(state.style, "sine", "cave + not stronger = sine")
|
|
while state.phase ~= "outro" do state:update() end
|
|
local waves = {}
|
|
local frame = 0
|
|
while state.phase == "outro" do
|
|
waves[frame] = state:lyOverrides() ~= nil
|
|
state:update()
|
|
frame = frame + 1
|
|
end
|
|
T.eq(waves[0], false, "sine: NextScene frame is flat")
|
|
T.eq(waves[1], false, "sine: SetUpForWavyOutro frame is flat")
|
|
T.eq(waves[2], true, "sine: the wave starts on frame 2")
|
|
T.eq(waves[16], true, "sine: fifteen wave frames end on 16")
|
|
T.eq(waves[17], true, "sine: the last wave holds through the FINISH frame")
|
|
T.eq(frame, 18, "sine: black on frame 18")
|
|
end
|
|
|
|
do
|
|
local state, world = make({ trainer = true })
|
|
local npc = { def = { index = 2 } }
|
|
local other = { def = { index = 5 } }
|
|
T.eq(state:keepsOpponent(npc), true,
|
|
"the hLastTalked object survives RespawnPlayerAndOpponent")
|
|
T.eq(state:keepsOpponent(other), false, "...and nobody else does")
|
|
world.vm.lastTalked = nil
|
|
T.eq(state:keepsOpponent(npc), false, "no hLastTalked: only the player")
|
|
local wild = make({ trainer = false })
|
|
wild.world.vm.running = function() return false end
|
|
T.eq(wild:keepsOpponent({ def = { index = 2 } }), false,
|
|
"an unscripted wild battle keeps nobody")
|
|
end
|
|
|
|
T.finish("gen2 trainer ball tile bug 2044")
|