Files
gen1recomp/tests/drivers/gold_pipeline_shots.lua
T

160 lines
6.2 KiB
Lua

-- Gold's world-pipeline seam (World:drawPipeline), the Gen 2 peer of the
-- render_pipelines path src/world/OverworldController.lua:4867 gives Gen 1.
--
-- POKEPORT_IDENTITY=gold-dev POKEPORT_GAME=gold \
-- POKEPORT_DRIVER=tests/drivers/gold_pipeline_shots.lua love .
-- POKEPORT_SHOT_DIR=/tmp/gold-pipeline (default)
--
-- Registers a pipeline that paints an unmistakable magenta field, switches it
-- on, and asserts what the seam is supposed to guarantee: drawWorld owns the
-- frame, ctx carries the Gen 1 keys, ctx.drawFx anchors the standing FX,
-- worldPresent folds over the result, tilt is forced off, and a declined
-- frame falls back to the vanilla 2D draw instead of a blank screen.
local U = require("tests.drivers.util")
local Pipelines = require("src.render.Pipelines")
local Tilt = require("src.render.Tilt")
return function(game)
local out = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/gold-pipeline"
local failures = 0
local function ok(label, condition, detail)
if condition then
print("[pipeline] ok " .. label)
else
failures = failures + 1
print("[pipeline] FAIL " .. label .. " " .. tostring(detail))
end
end
U.wait(45)
local world = game.world
assert(world and world.map, "gold world did not boot")
world:setMap("ROUTE_30", 10, 10, "down")
U.wait(10)
U.shot(game, out .. "/00-vanilla.png")
-- ---------------------------------------------------------------- record
local seen, canvas = {}, nil
local decline = false
local pipeline = {
label = "TESTPIPE",
levels = { "OFF", "ON" },
drawWorld = function(ctx)
seen.ctx = ctx
seen.drawWorld = (seen.drawWorld or 0) + 1
if decline then return nil end
local G = love.graphics
local w, h = ctx.width, ctx.height
if not canvas or canvas:getWidth() ~= w or canvas:getHeight() ~= h then
canvas = G.newCanvas(w, h)
end
local previous = G.getCanvas()
G.push("all")
G.origin()
G.setCanvas(canvas)
G.clear(0.8, 0.1, 0.6, 1)
G.setColor(1, 1, 1, 1)
for x = 0, w, 32 do G.rectangle("fill", x, 0, 1, h) end
for y = 0, h, 32 do G.rectangle("fill", 0, y, w, 1) end
-- the standing FX, anchored under this pipeline's own (identity) camera
ctx.drawFx(function(wx, wy)
return (wx - ctx.cam.x) * ctx.scale, (wy - ctx.cam.y) * ctx.scale
end, ctx.scale)
G.setCanvas(previous)
G.pop()
seen.drew = true
return canvas
end,
worldPresent = function(image, ctx)
seen.worldPresent = (seen.worldPresent or 0) + 1
seen.presentCtx = ctx
return image
end,
}
-- A fresh table so Pipelines.list()'s identity-keyed memo re-sorts; the
-- mod merge hands it a new one for the same reason.
game.data.render_pipelines = { testpipe = pipeline }
Pipelines.install(game.data)
ok("registered", Pipelines.get("testpipe") ~= nil, "not in the registry")
-- ------------------------------------------------------------ switched on
Tilt.setLevel(1)
Pipelines.setLevel("testpipe", 1)
ok("tilt forced off", Tilt.level == 0, "tilt still " .. tostring(Tilt.level))
ok("world pipeline claimed", Pipelines.worldPipeline() == "testpipe",
tostring(Pipelines.worldPipeline()))
U.wait(5)
U.shot(game, out .. "/01-pipeline-on.png")
ok("drawWorld ran", (seen.drawWorld or 0) > 0, "never called")
ok("drawWorld drew", seen.drew == true, "declined every frame")
ok("worldPresent ran", (seen.worldPresent or 0) > 0, "never called")
local ctx = seen.ctx
ok("ctx.state is the world", ctx and ctx.state == world, "wrong state")
ok("ctx.cam is the camera", ctx and ctx.cam == world.camera, "wrong camera")
ok("ctx.scale is zoomScale", ctx and ctx.scale == world:zoomScale(),
ctx and tostring(ctx.scale))
ok("ctx.bgY is the camera row", ctx and ctx.bgY == world.camera.y,
ctx and tostring(ctx.bgY))
ok("ctx.vw/vh are the view", ctx and ctx.vw == world.viewW
and ctx.vh == world.viewH, ctx and tostring(ctx.vw))
ok("ctx.level is the ladder", ctx and ctx.level == 1, ctx and tostring(ctx.level))
ok("ctx.width/height are the window",
ctx and ctx.width == love.graphics.getWidth()
and ctx.height == love.graphics.getHeight(), "mismatch")
ok("ctx.paletteFor is nil-valued (art is baked)",
ctx and ctx.paletteFor and ctx.paletteFor(world.map) == nil, "returned colours")
ok("ctx.spriteColors is nil-valued",
ctx and ctx.spriteColors and ctx.spriteColors() == nil, "returned colours")
ok("ctx.fx has Gold's two effects",
ctx and ctx.fx and type(ctx.fx.emote) == "function"
and type(ctx.fx.heal) == "function", "missing fx")
ok("ctx.drawFx is callable", ctx and type(ctx.drawFx) == "function", "missing")
ok("worldPresent got the same ctx", seen.presentCtx == seen.ctx, "different ctx")
-- ------------------------------------------------- the FX composite path
-- An emote over the player exercises ctx.drawFx end to end: it must be the
-- pipeline that composites it, and the vanilla drawPeople must not also.
local sheet
for _, img in pairs(world.emoteImages or {}) do sheet = img break end
if sheet then
world.emote = { image = sheet, entity = world.player, left = 240 }
end
U.wait(4)
ok("emote is up", world.emote ~= nil, "no emote sheet loaded")
local fxOk = pcall(function()
-- the same call the pipeline made, run again outside the guard so a throw
-- surfaces here rather than only retiring the pipeline
seen.ctx.drawFx(function(wx, wy) return wx, wy end, 1)
end)
ok("drawFx composites without throwing", fxOk, "threw")
U.shot(game, out .. "/02-pipeline-emote.png")
-- ----------------------------------------------------- a declined frame
decline = true
U.wait(5)
U.shot(game, out .. "/03-pipeline-declined.png")
ok("declined frames still call drawWorld", (seen.drawWorld or 0) > 1, "stopped")
decline = false
-- ------------------------------------------------------------ switched off
Pipelines.setLevel("testpipe", 0)
local before = seen.drawWorld
U.wait(5)
ok("off means not called", seen.drawWorld == before,
"still drawing at level 0")
U.shot(game, out .. "/04-pipeline-off.png")
if failures == 0 then
print("[pipeline] PASS")
else
print("[pipeline] FAILURES: " .. failures)
end
love.event.quit(failures == 0 and 0 or 1)
end