Files
YataiDON8/Scripts/background/t12_clip.lua
T
2026-10-05 10:06:07 +08:00

161 lines
5.7 KiB
Lua

local M = {}
M.__index = M
local SCR = "background"
local DATA = {}
local OP_DEFAULTS = { nil, nil, nil, nil, nil, 0, 0, 1, 1, 1, 1, 0, 0, 0 }
local function expand(d)
local ops = d.ops
for _, op in ipairs(ops) do
for j = #op + 1, 14 do op[j] = OP_DEFAULTS[j] end
end
for _, st in pairs(d.states) do
for _, part in ipairs({ "intro", "loop" }) do
local frames = st[part]
for f = 1, #frames do
local ids, flat, k = frames[f], {}, 0
for i = 1, #ids do
local op = ops[ids[i]]
for j = 1, 14 do flat[k + j] = op[j] end
k = k + 14
end
frames[f] = flat
end
end
end
d.ops, d.packed = nil, nil
end
-- id -> clip; loads its texture folder the first time
function M.get(id)
local c = DATA[id]
if c == nil then
local ok, d = pcall(require, "background.t12." .. id)
if ok and type(d) == "table" then
if d.packed then expand(d) end
tex.load_folder(SCR, d.tex)
c = setmetatable({ d = d, id = id, cache = {} }, M)
else
c = false
end
DATA[id] = c
end
return c or nil
end
function M.unload_all()
for _, c in pairs(DATA) do
if c then tex.unload_folder(SCR, c.d.tex) end
end
DATA = {}
end
function M:texture(i)
local t = self.cache[i]
if t == nil then
t = tex.get_texture(string.format("%s/%s/t%02d", SCR, self.d.tex, i)) or false
self.cache[i] = t
end
return t or nil
end
-- the white copy (same alpha) for the colour offset pass; the texture itself if there is none
function M:texture_w(i)
self.cache_w = self.cache_w or {}
local t = self.cache_w[i]
if t == nil then
t = tex.get_texture(string.format("%s/%s/t%02d_w", SCR, self.d.tex, i)) or false
self.cache_w[i] = t
end
return t or self:texture(i)
end
function M:state(name) return self.d.states[name] end
-- frame f (0-based) of a state: the intro once, then the loop; hold = keep the last intro frame
-- instead of looping (one-shots). Returns the op list or nil.
function M:frame(name, f, hold)
local st = self.d.states[name]
if not st then return nil end
f = math.max(0, math.floor(f))
local ni, nl = #st.intro, #st.loop
if f < ni then return st.intro[f + 1] end
if nl == 0 then return st.intro[ni] end
if hold then return st.loop[math.min(f - ni, nl - 1) + 1] end
return st.loop[(f - ni) % nl + 1]
end
-- loop phase from an absolute frame counter (keeps a looping state on the beat grid)
function M:loop_frame(name, fabs)
local st = self.d.states[name]
if not st or #st.loop == 0 then return nil end
return st.loop[math.floor(fabs) % #st.loop + 1]
end
function M:length(name)
local st = self.d.states[name]
return st and (#st.intro + #st.loop) or 0
end
local function set(...) local t = {} for _, i in ipairs({ ... }) do t[i] = true end return t end
local ADDITIVE = {
t12_dbg_dance_bg_02 = set(0, 2), -- light pillars
t12_dbg_dance_bg_03 = set(1), -- harbour bokeh
t12_dbg_dance_bg_04 = set(9, 10), -- will-o'-wisps
t12_dbg_dance_bg_05 = set(1), -- lantern glow
t12_dbg_dance_bg_07 = set(4), -- soft star glow
t12_dbg_dance_bg_08 = set(0, 5, 12),
t12_dbg_dance_bg_09 = set(0, 1, 5),
t12_dbg_dance_bg_11 = set(0, 2, 4, 11), -- candle / kamakura glow
t12_dbg_dance_bg_13 = set(0, 1, 2, 3, 4), -- bokeh, lantern-string glow (black fringe: dark red blotch)
t12_dbg_dance_bg_idol = set(1, 2, 3), -- spotlight beams
t12_fever_fever_01 = set(0), -- glow orb (brown edge: brown halo)
t12_fever_fever_02 = set(0),
t12_chara_m_don = set(0, 1), -- eye glow, lightning
t12_chara_m_katsu = set(0, 1),
t12_geki_m_don_geki = set(0), -- lightning
t12_geki_m_katsu_geki = set(0),
}
-- one params / colour table reused for every op (tex.draw_texture reads it on the spot): the
-- background draws a few hundred ops a frame, and two new tables per op were most of the garbage
local floor = math.floor
local P, COLOR, ADD_COLOR = {}, { 0, 0, 0, 255 }, { 0, 0, 0, 255 }
function M:draw(ops, ox, oy, K, fade, flipx)
if not ops then return end
fade = fade or 1
local size = self.d.size
local add = ADDITIVE[self.d.tex]
local p = P
for n = 1, #ops, 14 do
local t = self:texture(ops[n])
if t then
local sz = size[ops[n] + 1]
local w, h = ops[n + 3] * K, ops[n + 4] * K
local x, y = (ox + ops[n + 1]) * K, (oy + ops[n + 2]) * K
local mirror = (ops[n + 6] == 1) and "vertical" or nil
if flipx then
x = (ox + self.d.w - ops[n + 1]) * K - w
mirror = "horizontal" -- (no combined mirror in the engine)
end
p.x, p.y, p.x2, p.y2 = x, y, w - sz[1], h - sz[2]
p.rotation, p.mirror, p.fade = ops[n + 5], mirror, ops[n + 7] * fade
COLOR[1], COLOR[2], COLOR[3] = floor(ops[n + 8] * 255 + 0.5), floor(ops[n + 9] * 255 + 0.5),
floor(ops[n + 10] * 255 + 0.5)
p.color = COLOR
p.blend = (add and add[ops[n]]) and "additive" or nil
tex.draw_texture(t, p)
local ar, ag, ab = ops[n + 11], ops[n + 12], ops[n + 13]
if ar + ag + ab > 0.01 then
p.blend = "additive"
ADD_COLOR[1], ADD_COLOR[2], ADD_COLOR[3] = floor(ar * 255 + 0.5), floor(ag * 255 + 0.5), floor(ab * 255 + 0.5)
p.color = ADD_COLOR
tex.draw_texture(self:texture_w(ops[n]), p)
end
end
end
end
return M