mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
189 lines
6.8 KiB
Lua
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
|