mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-19 12:15:31 +02:00
skin studio updates, save sync CLOSES #1533
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
package.path = "./?.lua;./?/init.lua;" .. package.path
|
||||
|
||||
local T = require("tests.harness")
|
||||
local check, eq = T.check, T.eq
|
||||
love = love or require("tests.love_stub")
|
||||
|
||||
local TouchSkin = require("src.core.TouchSkin")
|
||||
local Playfield = require("src.render.Playfield")
|
||||
local Renderer = require("src.render.Renderer")
|
||||
local Chrome = require("src.ui.gen2.Chrome")
|
||||
local Zoom = require("src.render.Zoom")
|
||||
|
||||
local EPS = 1e-6
|
||||
|
||||
local function setWindow(w, h)
|
||||
love.graphics.getDimensions = function() return w, h end
|
||||
love.graphics.getPixelDimensions = function() return w, h end
|
||||
end
|
||||
|
||||
local function cfg(viewport, extra)
|
||||
return ([[
|
||||
overlays = 1
|
||||
overlay0_name = "bezel"
|
||||
overlay0_full_screen = true
|
||||
overlay0_normalized = true
|
||||
overlay0_viewport = "%s"
|
||||
%s
|
||||
overlay0_descs = 1
|
||||
overlay0_desc0 = "nul,0.5,0.5,rect,0.02,0.02"
|
||||
]]):format(viewport, extra or "")
|
||||
end
|
||||
|
||||
local function useSkin(viewport, extra)
|
||||
local skin = assert(TouchSkin.parse(cfg(viewport, extra)))
|
||||
TouchSkin.setActive(skin)
|
||||
TouchSkin.setOverlayLive(false)
|
||||
return skin
|
||||
end
|
||||
|
||||
local function inside(x, y, w, h, bx, by, bw, bh)
|
||||
return x >= bx - EPS and y >= by - EPS
|
||||
and x + w <= bx + bw + EPS and y + h <= by + bh + EPS
|
||||
end
|
||||
|
||||
setWindow(640, 576)
|
||||
TouchSkin.setActive(nil)
|
||||
Renderer:init()
|
||||
local plain = Renderer:frameRects()
|
||||
eq(plain.cut, false, "no skin: no cutout")
|
||||
eq(plain.vux, 0, "no skin: the picture starts at the window origin")
|
||||
eq(plain.vuw, 640, "no skin: the picture is the whole window")
|
||||
eq(plain.Sp, 4, "no skin: 640x576 fits four whole GB pixels")
|
||||
eq(plain.uox, 0, "no skin: the UI letterbox fills the window")
|
||||
eq(select(3, Playfield.rect(640, 576)), 640, "no skin: the playfield is the window")
|
||||
eq(Chrome.fitScale(640, 576), 4, "no skin: Gold fits the window the same way")
|
||||
|
||||
local WINDOWS = {
|
||||
{ 640, 576 }, { 1280, 720 }, { 1920, 1080 },
|
||||
{ 800, 480 }, { 480, 800 }, { 360, 640 },
|
||||
}
|
||||
local VIEWPORTS = {
|
||||
"0.2335,0.0855,0.5335,0.830",
|
||||
"0.2,0.15,0.6,0.5",
|
||||
"0.05,0.05,0.9,0.35",
|
||||
"0.3,0.1,0.4,0.8",
|
||||
}
|
||||
local UI_SIZES = { { 160, 144 }, { 304, 144 } }
|
||||
|
||||
local escapes, uncut, cases = 0, 0, 0
|
||||
for _, win in ipairs(WINDOWS) do
|
||||
setWindow(win[1], win[2])
|
||||
for _, vp in ipairs(VIEWPORTS) do
|
||||
useSkin(vp)
|
||||
Renderer:init()
|
||||
for _, size in ipairs(UI_SIZES) do
|
||||
Renderer:setUISize(size[1], size[2])
|
||||
for off = -8, 8 do
|
||||
Zoom.offset = off
|
||||
for _, fill in ipairs({ false, true }) do
|
||||
for _, centered in ipairs({ true, false }) do
|
||||
Renderer.uiFill = fill
|
||||
Renderer.uiCentered = centered
|
||||
Renderer.worldActive = true
|
||||
cases = cases + 1
|
||||
local r = Renderer:frameRects()
|
||||
local ux, uy, uw, uh = Renderer.clipToView(r, r.uox, r.uoy,
|
||||
r.uvpw, r.uvph)
|
||||
if not inside(ux, uy, uw, uh, r.vux, r.vuy, r.vuw, r.vuh) then
|
||||
escapes = escapes + 1
|
||||
end
|
||||
if uw < r.uvpw - EPS or uh < r.uvph - EPS then uncut = uncut + 1 end
|
||||
local vw, vh = Renderer:worldViewSize()
|
||||
local sp = Zoom.scale(r.Sp)
|
||||
if vw * sp > r.vuw + 2 * sp + EPS
|
||||
or vh * sp > r.vuh + 2 * sp + EPS then
|
||||
escapes = escapes + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
check(cases > 1000, "the sweep covers every window x cutout x zoom x layout")
|
||||
eq(escapes, 0, "no zoom, battle surface or UI layout puts a rect past the cutout")
|
||||
eq(uncut, 0, "and the UI was sized to fit, so the clip never has to cut it")
|
||||
|
||||
setWindow(1280, 720)
|
||||
useSkin("0.25,0.1,0.5,0.6")
|
||||
Renderer:init()
|
||||
Renderer:setUISize(160, 144)
|
||||
Renderer.uiFill, Renderer.uiCentered = false, true
|
||||
Zoom.offset = 0
|
||||
local r = Renderer:frameRects()
|
||||
eq(r.cut, true, "the skin's cutout is folded into the frame")
|
||||
eq(r.vux, 320, "cutout x")
|
||||
eq(r.vuy, 72, "cutout y")
|
||||
eq(r.vuw, 640, "cutout width")
|
||||
eq(r.vuh, 432, "cutout height")
|
||||
eq(r.Sp, 3, "the fit is measured against the cutout, not the window")
|
||||
check(inside(r.uox, r.uoy, r.uvpw, r.uvph, r.vux, r.vuy, r.vuw, r.vuh),
|
||||
"the UI letterbox sits inside the cutout")
|
||||
check(inside(r.ox, r.oy, r.vpw, r.vph, r.vux, r.vuy, r.vuw, r.vuh),
|
||||
"so does the world letterbox")
|
||||
|
||||
local lo, hi = Zoom.offsetRange(r.Sp)
|
||||
for off = lo, hi do
|
||||
Zoom.offset = off
|
||||
local z = Renderer:frameRects()
|
||||
check(inside(z.uox, z.uoy, z.uvpw, z.uvph, z.vux, z.vuy, z.vuw, z.vuh),
|
||||
"zoom " .. Zoom.offsetLabel(off) .. " keeps the UI in the cutout")
|
||||
local vw, vh = Renderer:worldViewSize()
|
||||
local sp = Zoom.scale(z.Sp)
|
||||
check(vw * sp <= z.vuw + 2 * sp and vh * sp <= z.vuh + 2 * sp,
|
||||
"zoom " .. Zoom.offsetLabel(off) .. " keeps the world pass capped")
|
||||
end
|
||||
Zoom.offset = 0
|
||||
|
||||
local capped = select(1, Renderer:worldViewSize())
|
||||
useSkin("0.25,0.1,0.5,0.6", "overlay0_viewport_expand = true")
|
||||
local expanded = select(1, Renderer:worldViewSize())
|
||||
check(expanded > capped,
|
||||
"viewport_expand lets the survey world fill the cutout instead of the GB box")
|
||||
useSkin("0.25,0.1,0.5,0.6")
|
||||
|
||||
setWindow(480, 800)
|
||||
useSkin("0.3,0.1,0.4,0.3")
|
||||
Renderer:init()
|
||||
Renderer:setUISize(304, 144)
|
||||
Renderer.uiFill, Renderer.uiCentered = false, true
|
||||
local tight = Renderer:frameRects()
|
||||
check(tight.vuw < 304, "the cutout cannot hold the WIDE battle at 1x")
|
||||
check(inside(tight.uox, tight.uoy, tight.uvpw, tight.uvph,
|
||||
tight.vux, tight.vuy, tight.vuw, tight.vuh),
|
||||
"so the surface is scaled down to the cutout rather than over the bezel")
|
||||
Renderer:setUISize(160, 144)
|
||||
|
||||
setWindow(1280, 720)
|
||||
useSkin("0.25,0.1,0.5,0.6")
|
||||
local px, py, pw, ph, active = Playfield.rect(1280, 720)
|
||||
eq(active, true, "Gold sees the cutout too")
|
||||
eq(pw, 480, "the playfield is a whole multiple of 160")
|
||||
eq(ph, 432, "and of 144")
|
||||
check(inside(px, py, pw, ph, 320, 72, 640, 432),
|
||||
"centred inside the cutout")
|
||||
eq(Chrome.fitScale(1280, 720), 3, "Chrome fits the playfield")
|
||||
local cox, coy = Chrome.fitOrigin(1280, 720)
|
||||
eq(cox, px, "and centres the panel on it")
|
||||
eq(coy, py, "on both axes")
|
||||
|
||||
useSkin("0.25,0.1,0.5,0.6", "overlay0_viewport_expand = true")
|
||||
local ex, ey, ew, eh = Playfield.rect(1280, 720)
|
||||
eq(ew, 640, "expand hands the picture the full cutout width")
|
||||
eq(eh, 432, "and its full height")
|
||||
eq(ex, 320, "at the cutout origin")
|
||||
eq(ey, 72, "on both axes")
|
||||
useSkin("0.25,0.1,0.5,0.6")
|
||||
|
||||
local ew2, eh2, ex2, ey2, act2 = Playfield.push(1280, 720)
|
||||
eq(act2, true, "push reports the frame is contained")
|
||||
eq(ex2, px, "push translates to the playfield origin")
|
||||
eq(ey2, py, "on both axes")
|
||||
eq(ew2, pw, "and hands the scene the playfield size")
|
||||
eq(Playfield.cutout(ew2, eh2), nil, "inside the frame there is no cutout left")
|
||||
eq(select(3, Playfield.rect(ew2, eh2)), pw, "so the playfield is the surface")
|
||||
eq(Chrome.fitScale(ew2, eh2), 3, "and Chrome fits it without re-applying")
|
||||
eq(select(1, Chrome.fitOrigin(ew2, eh2)), 0, "at a local origin")
|
||||
eq(select(1, Playfield.dimensions()), pw, "screens read the playfield as the display")
|
||||
Playfield.pop()
|
||||
eq(Playfield.entered, false, "pop leaves the frame")
|
||||
eq(select(1, Playfield.cutout(1280, 720)), 320, "and the cutout is visible again")
|
||||
|
||||
useSkin("0.4,0.4,0.1,0.1")
|
||||
local sx, sy, sw, sh = Playfield.rect(1280, 720)
|
||||
check(inside(sx, sy, sw, sh, 512, 288, 128, 72),
|
||||
"a cutout smaller than 160x144 still bounds the playfield")
|
||||
check(sw <= 128 and sh <= 72, "the playfield never exceeds the cutout")
|
||||
|
||||
TouchSkin.setActive(nil)
|
||||
eq(Playfield.cutout(1280, 720), nil, "no skin, no cutout")
|
||||
eq(select(3, Playfield.rect(1280, 720)), 1280, "and the playfield is the window")
|
||||
local saved = TouchSkin.viewport
|
||||
TouchSkin.viewport = function() error("boom") end
|
||||
eq(Playfield.cutout(1280, 720), nil, "a throwing viewport is no cutout")
|
||||
TouchSkin.viewport = function() return 10, 10, 0, 0 end
|
||||
eq(Playfield.cutout(1280, 720), nil, "a zero-sized cutout is no cutout")
|
||||
TouchSkin.viewport = function() return -50, -50, 200, 200 end
|
||||
eq(select(1, Playfield.cutout(1280, 720)), 0, "a cutout off the surface is clamped")
|
||||
eq(select(3, Playfield.cutout(1280, 720)), 150, "to what is left of it")
|
||||
TouchSkin.viewport = saved
|
||||
TouchSkin.setActive(nil)
|
||||
setWindow(640, 576)
|
||||
|
||||
T.finish("skin_viewport_containment")
|
||||
Reference in New Issue
Block a user