mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-27 17:01:44 +02:00
153 lines
6.0 KiB
Lua
153 lines
6.0 KiB
Lua
-- The OPTION screen's grouped pages and its scrolling text.
|
|
--
|
|
-- The flat row list outgrew the four-box viewport, so the battle, audio,
|
|
-- video and speed rows collapse into one opener each. Grouping happens after
|
|
-- the ui.options.rows hook and only builds self.view, so self.rows stays the
|
|
-- flat list a mod reads and edits.
|
|
--
|
|
-- Text longer than its line used to run off the 160px panel with no clipping
|
|
-- (COLORS showed "1-A (Default) (B"); the highlighted row now scrolls instead.
|
|
-- luajit tests/engine/options_submenu_marquee_test.lua
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local T = require("tests.modkit")
|
|
local OptionsMenu = require("src.ui.OptionsMenu")
|
|
local Marquee = require("src.ui.Marquee")
|
|
|
|
local function stubGame()
|
|
local stack = { items = {} }
|
|
function stack:push(s) self.items[#self.items + 1] = s; return s end
|
|
function stack:pop() self.items[#self.items] = nil end
|
|
function stack:top() return self.items[#self.items] end
|
|
return {
|
|
data = { rulesets = { gen1_faithful = { name = "GEN 1" } }, constants = {} },
|
|
save = { options = {} },
|
|
stack = stack,
|
|
modStatus = { available = {} },
|
|
}
|
|
end
|
|
|
|
local function ids(rows)
|
|
local out = {}
|
|
for i, row in ipairs(rows) do out[i] = row.id end
|
|
return table.concat(out, ",")
|
|
end
|
|
|
|
local function find(rows, id)
|
|
for i, row in ipairs(rows) do if row.id == id then return row, i end end
|
|
end
|
|
|
|
-- ------------------------------------------------------------- grouping
|
|
|
|
local game = stubGame()
|
|
local menu = OptionsMenu.new(game)
|
|
|
|
T.check(find(menu.rows, "battleBg"), "self.rows keeps the flat list for mods")
|
|
T.check(find(menu.rows, "musicVol"), "self.rows keeps the audio rows too")
|
|
for _, id in ipairs({ "battleBg", "speedBattle", "uiLayout", "tilt", "zoom",
|
|
"voidFill", "colors", "shaderfx", "shaderfx2",
|
|
"musicVol", "videoMode" }) do
|
|
T.check(not find(menu.view, id), id .. " moved onto a page")
|
|
end
|
|
|
|
for _, spec in ipairs({
|
|
{ "group.battle", 6 }, { "group.audio", 3 }, { "group.video", 5 },
|
|
{ "group.speed", 4 }, { "group.graphics", 4 }, { "group.extras", 3 },
|
|
}) do
|
|
local row = find(menu.view, spec[1])
|
|
T.check(row, spec[1] .. " has an opener row")
|
|
T.eq(row.group, true, spec[1] .. " is marked as a group")
|
|
end
|
|
|
|
-- Six battle rows on this build (PIKACHU VOL is Yellow-only, so AUDIO is 3).
|
|
T.eq(select(1, find(menu.view, "group.battle")).value(game), "6 OPTIONS",
|
|
"BATTLE OPTIONS counts its six rows")
|
|
|
|
-- The top level is explicitly ordered, groups and singles interleaved.
|
|
T.eq(ids(menu.view):gsub("group%.", ""),
|
|
"speed,video,graphics,audio,performance,ruleset,battle,extras,mods," ..
|
|
"controls,dateFormat,timeFormat",
|
|
"the top level reads in the order ORDER names")
|
|
T.check(not find(menu.view, "textSpeed"), "TEXT SPEED moved onto the SPEED page")
|
|
T.check(#menu.view < #menu.rows, "the view is shorter than the flat list")
|
|
|
|
-- A row in no group is untouched and keeps its order.
|
|
T.check(find(menu.view, "ruleset"), "RULESET stays on the top level")
|
|
T.check(find(menu.view, "performance"), "so does PERFORMANCE")
|
|
T.check(find(menu.view, "mods"), "and MODS")
|
|
|
|
-- ------------------------------------------------------------- submenus
|
|
|
|
local graphics = find(menu.view, "group.graphics")
|
|
graphics.activate(game)
|
|
T.eq(ids(game.stack:top().view), "colors,uiLetterbox,shaderfx,shaderfx2",
|
|
"GRAPHICS carries COLORS, UI LETTERBOX and both SHADER FX slots")
|
|
game.stack:pop()
|
|
|
|
local extras = find(menu.view, "group.extras")
|
|
extras.activate(game)
|
|
T.eq(ids(game.stack:top().view), "tilt,zoom,voidFill",
|
|
"EXTRAS carries TILT, ZOOM and VOID FILL")
|
|
game.stack:pop()
|
|
|
|
local video = find(menu.view, "group.video")
|
|
video.activate(game)
|
|
T.eq(ids(game.stack:top().view):match("^uiLayout"), "uiLayout",
|
|
"UI LAYOUT heads the VIDEO page")
|
|
game.stack:pop()
|
|
|
|
local speed = find(menu.view, "group.speed")
|
|
speed.activate(game)
|
|
T.eq(ids(game.stack:top().view),
|
|
"textSpeed,speedOverworld,speedBattle,speedMenu",
|
|
"TEXT SPEED heads the SPEED page")
|
|
game.stack:pop()
|
|
|
|
local battle, at = find(menu.view, "group.battle")
|
|
menu.index = at
|
|
battle.activate(game)
|
|
local sub = game.stack:top()
|
|
T.check(sub ~= menu, "opening a group pushes a screen")
|
|
T.eq(ids(sub.view),
|
|
"animations,battleStyle,battleLayout,battleFit,battleHud,battleBg",
|
|
"the page carries exactly its six battle rows, in order")
|
|
T.eq(sub.sub, true, "the page knows it is a submenu")
|
|
T.eq(sub.onCancel, nil, "BACK out of a page never fires the caller's close hook")
|
|
|
|
-- focusRow reaches a grouped row by opening its page.
|
|
local game2 = stubGame()
|
|
local menu2 = OptionsMenu.new(game2)
|
|
local landed = menu2:focusRow("battleHud")
|
|
T.check(landed and landed ~= menu2, "focusRow opens the page a grouped row lives on")
|
|
T.eq(landed.view[landed.index].id, "battleHud", "and lands the cursor on it")
|
|
T.eq(menu2:focusRow("ruleset"), menu2, "an ungrouped row focuses in place")
|
|
T.eq(menu2.view[menu2.index].id, "ruleset", "on the row asked for")
|
|
T.eq(menu2:focusRow("nope"), nil, "an unknown id focuses nothing")
|
|
|
|
-- ------------------------------------------------------------- marquee
|
|
|
|
-- 16 characters fit beside a value's x=24 before the box frame at x=152;
|
|
-- the label's x=16 fits 17.
|
|
local long = "1-A (Default) (BGB)"
|
|
T.check(#long > 16, "the COLORS value really is too long for the line")
|
|
|
|
-- 19 characters, 16 fit: three steps of overflow. Hold 1.0s, one char per
|
|
-- 0.25s, hold 1.0s on the tail, then repeat -- a 2.75s cycle.
|
|
T.eq(Marquee.at(long, 16, 0), long:sub(1, 16), "it holds at the start")
|
|
T.eq(Marquee.at(long, 16, 1.3), long:sub(2, 17), "then steps one char")
|
|
T.eq(Marquee.at(long, 16, 1.6), long:sub(3, 18), "then another")
|
|
T.eq(Marquee.at(long, 16, 2.4), long:sub(4, 19), "then holds on the tail")
|
|
T.eq(Marquee.at(long, 16, 2.8), long:sub(1, 16), "before wrapping back")
|
|
T.eq(Marquee.at(long, 16, 2.4):sub(-4), "BGB)",
|
|
"so the tail the panel used to clip becomes readable")
|
|
for _, at2 in ipairs({ 0, 1.3, 1.6, 2.4, 2.8 }) do
|
|
T.eq(#Marquee.at(long, 16, at2), 16,
|
|
"every frame fills the line exactly")
|
|
end
|
|
|
|
T.eq(Marquee.at("SHORT", 16, 2.0), "SHORT",
|
|
"text that fits is never scrolled")
|
|
|
|
T.finish("options submenu + marquee")
|