Files
gen1recomp/tests/engine/poison_flicker_bug1872.lua
T

189 lines
6.8 KiB
Lua

-- engine/gfx/screen_effects.asm:1-12 (#1872)
package.path = "./?.lua;./?/init.lua;" .. package.path
if not _G.love then _G.love = require("tests.love_stub") end
local T = require("tests.modkit")
local check, eq = T.check, T.eq
local Data = T.fixtures.fresh()
local SaveData = require("src.core.SaveData")
local Pokemon = require("src.pokemon.Pokemon")
local PaletteFX = require("src.render.PaletteFX")
local OW = require("src.world.OverworldController")
local function setUpvalue(fn, name, val)
local i = 1
while true do
local n = debug.getupvalue(fn, i)
if not n then return false end
if n == name then debug.setupvalue(fn, i, val); return true end
i = i + 1
end
end
do
local bgp = 0xE4
local want = {}
for i = 0, 3 do
local shade = math.floor(bgp / (4 ^ i)) % 4
want[i] = (i == 0) and (shade % 4 >= 2 and shade or shade + 2) or shade
end
for i = 0, 3 do
eq(PaletteFX.POISON_BGP[i], want[i],
("POISON_BGP color %d matches `or $2` on rBGP"):format(i))
end
local colors = { { 1, 1, 1 }, { 2, 2, 2 }, { 3, 3, 3 }, { 4, 4, 4 } }
local out = PaletteFX.permute(colors, PaletteFX.POISON_BGP)
eq(out[1][1], 3, "poison shows DMG white as shade 2")
eq(out[2][1], 2, "poison leaves shade 1 alone")
eq(out[4][1], 4, "poison leaves shade 3 alone")
end
do
local realSound = package.loaded["src.core.Sound"]
package.loaded["src.core.Sound"] = { play = function() end, playCry = function() end }
local save = SaveData.newGame()
save.party = { Pokemon.new(Data, "FIXMON_A", 20) }
save.party[1].status = "PSN"
save.party[1].hp = 20
save.poisonSteps = 3
local game = { data = Data, save = save, stack = { push = function() end } }
setUpvalue(OW.applyFieldPoison, "Game", game)
local self_ = setmetatable({}, { __index = OW })
self_:applyFieldPoison()
eq(self_.poisonFlash, 4, "a poison tick arms four frames of flicker")
-- engine/events/poison.asm:75-91 .countPoisonedLoop
local sounds = {}
package.loaded["src.core.Sound"] = {
play = function(_, id) sounds[#sounds + 1] = id end,
playCry = function() end,
}
save.party = { Pokemon.new(Data, "FIXMON_A", 20), Pokemon.new(Data, "FIXMON_A", 20) }
save.party[1].status = "PSN"
save.party[1].hp = 1
save.party[2].status = nil
save.party[2].hp = 20
save.poisonSteps = 3
local realTextBox = require("src.render.TextBox")
setUpvalue(OW.applyFieldPoison, "TextBox", { new = function() return {} end })
local fainter = setmetatable({}, { __index = OW })
fainter:applyFieldPoison()
setUpvalue(OW.applyFieldPoison, "TextBox", realTextBox)
eq(save.party[1].hp, 0, "the last poisoned mon faints on this tick")
eq(fainter.poisonFlash, nil, "a party with nothing left poisoned never flashes")
eq(#sounds, 0, "and plays no SFX_POISONED")
save.party[2].status = "PSN"
save.poisonSteps = 3
local other = setmetatable({}, { __index = OW })
other:applyFieldPoison()
eq(other.poisonFlash, 4, "a mon still poisoned after the loop keeps the flash")
eq(sounds[1], "Poisoned", "and the sound")
package.loaded["src.core.Sound"] = realSound
end
do
local rects = {}
local savedRect = love.graphics.rectangle
love.graphics.rectangle = function(mode, x, y, w, h)
rects[#rects + 1] = { mode = mode, x = x, y = y, w = w, h = h }
end
local savedShader = PaletteFX.shader
PaletteFX.shader = function() return { send = function() end } end
local renderer = {}
setUpvalue(OW.drawUI, "Game", { renderer = renderer })
local self_ = setmetatable({ poisonFlash = 4 }, { __index = OW })
PaletteFX.setShadeMap(nil)
self_:drawUI()
eq(#rects, 0, "the poison flicker paints no rect on the UI canvas")
check(PaletteFX.shadeMap() == PaletteFX.POISON_BGP,
"the flicker arms the poison BGP map, which the world and UI blits both read")
-- engine/gfx/screen_effects.asm:1-12, DelayFrames counts vblanks (#1908)
for _ = 1, 10 do
PaletteFX.setShadeMap(nil)
self_:drawUI()
end
eq(self_.poisonFlash, 4, "drawing never spends the flicker")
-- home/fade.asm:66
PaletteFX.setShadeMap(PaletteFX.DARK_BGP)
self_:drawUI()
check(PaletteFX.shadeMap() == PaletteFX.DARK_BGP,
"the flicker leaves a dark map's shade map in place")
eq(#rects, 0, "and still paints no rect")
PaletteFX.shader = function() return nil end
PaletteFX.setShadeMap(nil)
renderer.screenVeil = nil
self_:drawUI()
check(renderer.screenVeil ~= nil and renderer.screenVeil[1] == 0
and renderer.screenVeil[2] > 0,
"with no shader the flicker becomes a screen-space veil")
eq(#rects, 0, "and still not a 160x144 fill")
PaletteFX.shader = function() return { send = function() end } end
local savedMode = PaletteFX.mode
PaletteFX.mode = "redpp"
self_.map = { renderer = { gbcAtlas = {} } }
PaletteFX.setShadeMap(nil)
renderer.screenVeil = nil
self_:drawUI()
check(renderer.screenVeil ~= nil and renderer.screenVeil[2] > 0,
"a baked GBC atlas falls back to the screen-space veil")
check(PaletteFX.shadeMap() == nil, "and does not arm a map nothing reads")
eq(self_:poisonShadeMap(), nil,
"and drawWorld arms none either, so the veil is not doubled up")
self_.map = { renderer = {} }
PaletteFX.setShadeMap(nil)
renderer.screenVeil = nil
self_:drawUI()
eq(renderer.screenVeil, nil, "a zoned world needs no veil")
check(PaletteFX.shadeMap() == PaletteFX.POISON_BGP,
"it arms the poison map instead")
check(self_:poisonShadeMap() == PaletteFX.POISON_BGP,
"and drawWorld arms the same one")
PaletteFX.mode = savedMode
self_.map = nil
-- engine/events/poison.asm:57, :93
local other = {}
setUpvalue(OW.drawUI, "Game",
{ renderer = renderer, stack = { top = function() return other end } })
PaletteFX.setShadeMap(nil)
renderer.screenVeil = nil
self_:drawUI()
eq(renderer.screenVeil, nil, "a flicker under a text box paints nothing")
check(PaletteFX.shadeMap() == nil, "and arms no shade map")
eq(self_.poisonFlash, 4, "and is still there for when the box closes")
eq(self_:poisonShadeMap(), nil, "and drawWorld holds the map back too")
setUpvalue(OW.drawUI, "Game",
{ renderer = renderer, stack = { top = function() return self_ end } })
PaletteFX.setShadeMap(nil)
self_:drawUI()
check(PaletteFX.shadeMap() == PaletteFX.POISON_BGP,
"and flashes once the overworld owns the loop again")
setUpvalue(OW.drawUI, "Game", { renderer = renderer })
-- `ld c, 4 / call DelayFrames` (engine/gfx/screen_effects.asm:1-12)
local ticker = setmetatable({ poisonFlash = 4 }, { __index = OW })
for i = 3, 0, -1 do
OW.tickPoisonFlash(ticker)
eq(ticker.poisonFlash, i, "update spends one flicker frame per logic tick")
end
OW.tickPoisonFlash(ticker)
eq(ticker.poisonFlash, 0, "and stops at zero")
eq(ticker:poisonShadeMap(), nil, "a spent flash arms no shade map")
PaletteFX.setShadeMap(nil)
PaletteFX.shader = savedShader
love.graphics.rectangle = savedRect
end