Files
gen1recomp/tests/engine/gen2_trainer_ball_lifetime_bug2021.lua

173 lines
5.8 KiB
Lua

-- ../pokecrystal/engine/battle/battle_transition.asm:584
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 World = require("src.world.gen2.World")
local ballCalls = 0
local realCells = Transition.pokeballCells
Transition.pokeballCells = function(...)
ballCalls = ballCalls + 1
return realCells(...)
end
local function fakeWorld()
local world = {
map = { id = "NEW_BARK_TOWN", def = {} }, daytime = "DAY",
npcs = {}, ghosts = {}, log = {},
}
function world:gbScreenOrigin() return 0, 0 end
function world:fitScale() return 1 end
function world:zoomScale() return 1 end
function world:drawGround() self.log[#self.log + 1] = "ground" end
function world:drawPeople() self.log[#self.log + 1] = "people" end
world.drawWorldBody = World.drawWorldBody
function world:draw() self:drawWorldBody(1) end
return world
end
local function make(opts)
opts.environment = opts.environment or "TOWN"
local world = fakeWorld()
opts.world = world
local state = Transition.new(nil, opts)
local inner = world.bgOverlay
if inner then
world.bgOverlay = function(s)
world.log[#world.log + 1] = world.peopleHidden and "bgonly" or "overlay"
inner(s)
end
end
return state, world
end
local function frame(state)
local world = state.world
world.log = {}
ballCalls = 0
state:drawWidescreen(160, 144)
local seq = table.concat(world.log, ",")
local layered = seq:find("ground,overlay,people", 1, true) ~= nil
return layered, ballCalls > 0, seq
end
local function pastBall(state)
state:update()
state:update()
end
do
local cells = Transition.pokeballCells()
T.eq(#cells, 112, ".PokeBallTransition sets 112 bits")
local minX, minY, maxY = 20, 18, -1
for _, cell in ipairs(cells) do
minX = math.min(minX, cell[1])
minY = math.min(minY, cell[2])
maxY = math.max(maxY, cell[2])
end
T.eq(minX, Transition.POKEBALL_X, "the stamp starts at hlcoord 2, 1 (x)")
T.eq(minY, Transition.POKEBALL_Y, "the stamp starts at hlcoord 2, 1 (y)")
T.eq(maxY - minY + 1, 16, "and it is sixteen rows tall")
end
do
local state, world = make({ trainer = true })
T.eq(state.phase, "pokeball", "a trainer battle stamps the ball first")
T.check(world.bgOverlay ~= nil, "the ball is BG content: an overlay seam")
local layered, ball, seq = frame(state)
T.check(layered, "drawn between the ground and the people: " .. seq)
T.check(ball, "and it is the ball that is painted there")
state:update()
T.eq(state.phase, "pokeball", "two frames of ball before the flash")
T.check((frame(state)), "...both with the ball under the sprites")
state:update()
T.eq(state.phase, "flash", "then the flash starts")
local seen = 0
for _ = 1, Transition.FLASH_FRAMES do
local l, b = frame(state)
if l and b then seen = seen + 1 end
state:update()
end
T.eq(seen, Transition.FLASH_FRAMES,
"the ball is still in the tilemap for every flash frame")
T.eq(state.phase, "outro", "the flash hands off to the outro")
local drawn, missed, guard = 0, 0, 0
local filtered = 0
while state.phase == "outro" and guard < 400 do
local l, b = frame(state)
if l and b then drawn = drawn + 1 else missed = missed + 1 end
if world.spriteFilter then filtered = filtered + 1 end
state:update()
guard = guard + 1
end
T.check(drawn > 0, "and the outro paints over a ball that is still there")
T.eq(missed, 0, "no outro frame drops it early")
T.eq(filtered, drawn - 1,
"RespawnPlayerAndOpponent hides the bystanders from the second outro frame")
T.eq(state.phase, "black", "the outro ends on the black hold")
local l, b, blackSeq = frame(state)
T.eq(blackSeq, "", "which is solid black, ball included")
T.eq(l or b, false, "...no world draw at all")
while not state.finished do state:update() end
T.eq(world.bgOverlay, nil, "finish takes the seam back down")
T.eq(world.spriteFilter, nil, "...and the sprite filter with it")
end
do
local state, world = make({ trainer = false })
T.eq(state.phase, "flash", "a wild battle has no ball to stamp")
local drew, guard = false, 0
while not state.finished and guard < 400 do
local _, b = frame(state)
if b then drew = true end
state:update()
guard = guard + 1
end
T.eq(drew, false, "and never draws one")
T.eq(world.bgOverlay, nil, "the seam still comes down at the end")
end
do
local state = make({ trainer = true, dark = true })
T.eq(state.phase, "pokeball", "a DARKNESS trainer battle still stamps it")
pastBall(state)
T.eq(state.phase, "flash", "DARKNESS still walks the three Flash slots")
T.eq(state:flashPal(), nil, "...each returning on its first frame")
T.check((frame(state)), "...with the ball still on screen")
for _ = 1, Transition.FLASH_CYCLES do state:update() end
T.eq(state.phase, "outro", "...one frame per slot, then the outro")
end
do
local state, world = make({
trainer = true, environment = "CAVE", playerLevel = 10, enemyLevel = 5,
})
T.eq(state.style, "sine", "a cave trainer battle wobbles")
pastBall(state)
local guard = 0
while state.phase == "flash" and guard < 400 do
state:update()
guard = guard + 1
end
T.eq(state:lyOverrides(), nil, "NextScene: no wave yet")
state:update()
T.eq(state:lyOverrides(), nil, "SetUpForWavyOutro: still flat")
state:update()
T.check(state:lyOverrides() ~= nil, "SineWave from the third outro frame")
state.quad = { setViewport = function() end }
local layered, ball, seq = frame(state)
T.check(ball, "the ball rides the sine outro")
T.eq(seq, "ground,bgonly,people",
"the BG is captured without the people, who are drawn flat after")
T.eq(world.peopleHidden, nil, "and the people flag is not left set")
end
T.finish("gen2 trainer ball lifetime bug 2021")