Files
PyTaikoGreen/Scripts/background/bg_objects/renda.lua
T
2026-10-09 18:30:20 -04:00

120 lines
4.9 KiB
Lua

-- Renda: the drumroll notes. Every hit sends notes flying dx, dy px in a
-- straight line from a random spot, one of the folder's pictures at that picture's speed,
-- optionally spinning `spin` degrees over the flight. The spawn area (x, y + their ranges,
-- on top of the texture.json position) comes from the game rather than the data, so those
-- are picked by eye.
-- spec (optional, over the default for the index): ms (flight per picture, cycled), kinds
-- ({ frame, ms } pairs instead of ms), dx, dy, spin, x, x_range, y, y_range, per_hit, glow (a texture
-- in the renda folder drawn additively under each note, frame for frame)
local DEFAULT = { per_hit = 1, x = 0, x_range = 0, y = 0, y_range = 0, dy = 0 }
local LIMIT = 25
-- the default drumrolls (renda_0..2), by index
local SPECS = {
[0] = { ms = { 2000, 2083, 1950, 2250, 1733, 2000 }, dx = 2060, dy = -1300, x_range = 500, y_range = 20 },
[1] = { ms = { 2000 }, dx = 2060, spin = 720, y_range = 200 },
[2] = { ms = { 2000 }, dx = 1560, dy = -1648, x_range = 500, y_range = 20 },
}
local RendaController = {}
RendaController.__index = RendaController
-- Miku and GUMI: two notes a hit flying straight across from the right edge, each one of
-- the five pictures at one of two speeds
RendaController.ACROSS = {
kinds = {
{ 0, 1333 }, { 0, 2000 }, { 1, 1667 }, { 1, 2000 }, { 2, 2333 },
{ 2, 2000 }, { 3, 1667 }, { 3, 2000 }, { 4, 1333 }, { 4, 2000 },
},
dx = -2060, dy = 0, x = 1280, x_range = 200, y = 384, y_range = 288, per_hit = 2,
}
function RendaController.new(path, index, spec)
local self = setmetatable({}, RendaController)
self.path = path or "background"
index = index or math.random(0, 2)
self.name = "renda_" .. index
local base = SPECS[index] or SPECS[0]
local function get(k)
local v = spec and spec[k]
if v == nil then v = base[k] end
if v == nil then v = DEFAULT[k] end
return v
end
for _, k in ipairs({ "dx", "dy", "spin", "x", "x_range", "y", "y_range", "per_hit" }) do self[k] = get(k) end
tex.load_folder(self.path, "renda")
local info = tex.get_texture_info("renda", self.name) or {}
self.width = info.width or 0
self.height = info.height or 0
self.kinds = spec and spec.kinds
if not self.kinds then
local ms = get("ms")
self.kinds = {}
for f = 0, (info.frame_count or 1) - 1 do
table.insert(self.kinds, { f, ms[f % #ms + 1] })
end
end
-- some collabs have no drumroll picture; asking for a missing one logs a load error
self._id = info.width and tex.get_texture(self.path .. "/renda/" .. self.name) or 0
self._id_glow = spec and spec.glow and tex.get_texture(self.path .. "/renda/" .. spec.glow) or nil
self.notes = {}
return self
end
function RendaController:add_renda()
local scale = tex.get_screen_scale()
local function move(ms, distance)
local m = anim.move(ms, { total_distance = math.floor(distance * scale) })
m:start()
return m
end
if self._id == 0 then return end
for _ = 1, self.per_hit do
if #self.notes >= LIMIT then return end
local kind = self.kinds[math.random(#self.kinds)]
table.insert(self.notes, {
frame = kind[1],
x = math.floor((self.x + math.random(0, self.x_range)) * scale),
y = math.floor((self.y + math.random(0, self.y_range)) * scale),
move = move(kind[2], self.dx),
rise = self.dy ~= 0 and move(kind[2], self.dy) or nil,
spin = self.spin and anim.move(kind[2], { total_distance = self.spin }) or nil,
})
local note = self.notes[#self.notes]
if note.spin then note.spin:start() end
end
end
function RendaController:update(current_time_ms)
for i = #self.notes, 1, -1 do
local note = self.notes[i]
note.move:update(current_time_ms)
if note.rise then note.rise:update(current_time_ms) end
if note.spin then note.spin:update(current_time_ms) end
if note.move.is_finished then table.remove(self.notes, i) end
end
end
function RendaController:draw()
for _, note in ipairs(self.notes) do
local x = note.x + note.move.attribute
local y = note.y + (note.rise and note.rise.attribute or 0)
if note.spin then
-- spins about its centre
local ox, oy = self.width / 2, self.height / 2
tex.draw_texture(self._id, { frame = note.frame, x = x + ox, y = y + oy,
origin = { ox, oy }, rotation = note.spin.attribute })
else
if self._id_glow then
tex.draw_texture(self._id_glow, { frame = note.frame, x = x, y = y, blend = "additive" })
end
tex.draw_texture(self._id, { frame = note.frame, x = x, y = y })
end
end
end
function RendaController:destroy() tex.unload_folder(self.path, "renda") end
return RendaController