Collab songs (#SCENEPRESET) now get their own cabinet backdrop, replayed by
the same objects as the default backdrop instead of the parent's collab art:
miku, toho, gumi, ia, lovelive, i7 (id7 / tri / rev / natsu), imas
(765 / cg / ml / sidem), yo-kai (base / matsuri / hatsukoi / tokoroten /
yougota), mario, a3
Each preset is a pack in bg_data/collab_packs.lua naming its parts: donbg
band (1P and 2P), normal and clear backdrops, troupe, stand under the
troupe, drumroll effect and soul-max crowd. Shared parts (the i7, imas
and yo-kai families) are shared the way the cabinet shares them. A pack is
used only when every part it names is present, otherwise the preset falls
back to the collab script as before. Packs also apply in 2P (donbg bands).
The objects take a part name where they took a random index:
DonBG.create_named, BGNormal.create_named, BGFever.create_named,
ArcadeDancerGroup.new("<name>"), Fever.Anim.new("<name>"). New:
renda_clip.lua (each hit replays one of the effect's parts along its own
path), and a beat-synced BGAnim for the whole-troupe dancer movies (i7,
a3). Collab troupes stand in fixed positions (lead in the centre) and
grow from 1 to the full troupe with the gauge.
Exported placements may now mirror: bg_anim, donbg and dancer data carry a
mirror flag (drawn with mirror = horizontal and the origin moved to the
other side of the png). Existing data has no flag and draws as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
105 lines
3.4 KiB
Lua
105 lines
3.4 KiB
Lua
-- Replays an exported cabinet backdrop movie (lower half of the screen).
|
|
-- Data: background/bg_data/bg_anim/<name>.lua; textures: bga_<name>/s<c>.png.
|
|
local BGAnim = {}
|
|
BGAnim.__index = BGAnim
|
|
|
|
local FRAME_MS = 1000.0 / 60.0
|
|
local STAGE_Y = 540
|
|
local ONE = string.byte("1")
|
|
|
|
function BGAnim.available(name)
|
|
local ok, data = pcall(require, "background/bg_data/bg_anim/" .. name)
|
|
return ok and type(data) == "table" and data.normal ~= nil
|
|
end
|
|
|
|
function BGAnim.new(name, path)
|
|
local ok, data = pcall(require, "background/bg_data/bg_anim/" .. name)
|
|
if not ok or type(data) ~= "table" or not data.normal then return nil end
|
|
local self = setmetatable({}, BGAnim)
|
|
self.name = name
|
|
self.subset = "bga_" .. name
|
|
self.path = path or "background"
|
|
tex.load_folder(self.path, self.subset)
|
|
self.data = data
|
|
self.ids = {}
|
|
self.elapsed = 0
|
|
self.last_ms = nil
|
|
return self
|
|
end
|
|
|
|
function BGAnim:id_of(shape)
|
|
local id = self.ids[shape]
|
|
if id == nil then
|
|
id = tex.get_texture(self.path .. "/" .. self.subset .. "/s" .. shape) or false
|
|
self.ids[shape] = id
|
|
end
|
|
return id or nil
|
|
end
|
|
|
|
-- beat_frames: replay on the song's beat instead of the wall clock, one beat per
|
|
-- beat_frames movie frames (the collab dancer movies are authored at 40 frames a beat)
|
|
function BGAnim:set_beat(beat_frames)
|
|
self.beat_frames = beat_frames
|
|
end
|
|
|
|
function BGAnim:update(current_ms, bpm)
|
|
if self.last_ms then
|
|
local dt = current_ms - self.last_ms
|
|
if dt > 0 and dt < 500 then
|
|
if self.beat_frames and bpm and bpm > 0 then
|
|
dt = dt * FRAME_MS / ((60000 / bpm) / self.beat_frames)
|
|
end
|
|
self.elapsed = self.elapsed + dt
|
|
end
|
|
end
|
|
self.last_ms = current_ms
|
|
end
|
|
|
|
local function ch(v, i)
|
|
if type(v) == "number" then return v end
|
|
return v[i]
|
|
end
|
|
|
|
function BGAnim:draw(fade)
|
|
fade = fade or 1.0
|
|
if fade <= 0.002 then return end
|
|
local frame = math.floor(self.elapsed / FRAME_MS)
|
|
local list = self.data.normal
|
|
local shapes = self.data.shapes
|
|
for k = 1, #list do
|
|
local e = list[k]
|
|
local i = (frame % e.period) + 1
|
|
local a = ch(e.a, i)
|
|
if a > 0.002 and e.on then
|
|
if e.on:byte((frame % e.on_period) + 1) ~= ONE then a = 0 end
|
|
end
|
|
if a > 0.002 then
|
|
local s = shapes[e.shape]
|
|
local id = s and self:id_of(e.shape)
|
|
if id then
|
|
local sx, sy = ch(e.sx, i), ch(e.sy, i)
|
|
-- mir: a mirroring placement; the texture flips inside its rect, so the
|
|
-- origin moves to the other side of the png
|
|
local mir = e.mir and ch(e.mir, i) == 1
|
|
tex.draw_texture(id, {
|
|
x = ch(e.tx, i),
|
|
y = STAGE_Y + ch(e.ty, i),
|
|
x2 = s[1] * (sx - 1),
|
|
y2 = s[2] * (sy - 1),
|
|
origin = { (mir and (s[1] - s[3]) or s[3]) * sx, s[4] * sy },
|
|
rotation = ch(e.rot, i),
|
|
fade = a * fade,
|
|
mirror = mir and "horizontal" or nil,
|
|
blend = (e.blend == 8) and "additive" or nil, -- cabinet add-blended glows
|
|
})
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function BGAnim:destroy()
|
|
tex.unload_folder(self.path, self.subset)
|
|
end
|
|
|
|
return BGAnim
|