Files
PyTaikoGreen/Scripts/background/bg_objects/fever.lua
T
2026-10-09 11:56:30 -04:00

107 lines
3.6 KiB
Lua

local Timing = require("background/bg_objects/timing")
local chain_moves, sum = Timing.chain_moves, Timing.sum
-- Fever: the rainbow-gauge line-up. It jumps up from `rise` px below its
-- rest position, overshooting `peak` px above it, bounces `bounce` px once a beat, and when
-- the rainbow is lost hops to the top of the bounce and drops `drop` px off the screen.
-- Every texture in the folder is drawn together; with `poses`, they switch between frames
-- 0 and 1 on every bounce. Timed in beats. The texture.json position is the rest position.
-- spec (optional): name (folder, default "fever_0"), keys (draw order, default sorted), rise,
-- peak, bounce, drop, poses, additive (set of keys drawn with additive blend, e.g. glows)
local DEFAULT = { rise = 210, peak = 72, bounce = 72, drop = 400 } -- fever_0..2
-- the default line-ups (folders fever_0..3), by Fever.create index
local SPECS = {
[0] = {},
[1] = {},
[2] = {},
[3] = { rise = 262, drop = 334 },
}
local Fever = {}
Fever.__index = Fever
function Fever.new(bpm, path, spec)
local self = setmetatable({}, Fever)
spec = spec or {}
self.name = spec.name or "fever_0"
self.path = path
self.rise = spec.rise or DEFAULT.rise
self.bounce = spec.bounce or DEFAULT.bounce
self.peak = spec.peak or self.bounce
self.drop = spec.drop or DEFAULT.drop
self.poses = spec.poses
tex.load_folder(self.path, "fever/" .. self.name)
local keys = spec.keys
if not keys then
keys = tex.get_texture_keys(self.name) or {}
table.sort(keys)
end
local additive = spec.additive or {}
self._ids = {}
for _, key in ipairs(keys) do
table.insert(self._ids, {
tex.get_texture(self.path .. "/" .. self.name .. "/" .. key) or 0,
additive[key] and "additive" or nil,
})
end
self.beat = 60000 / bpm
self.pose = 0
self.state = nil -- nil (hidden), "in", "bounce", "out"
return self
end
function Fever:play(state, moves, offset)
self.state = state
self.offset = offset
self.moves = chain_moves(moves, self.beat, tex.get_screen_scale())
end
function Fever:y()
return self.offset + sum(self.moves)
end
function Fever:start()
local scale = tex.get_screen_scale()
self.pose = 0
self:play("in", { { 0.5, -(self.rise + self.peak), "out" }, { 0.5, self.peak, "in" } },
math.floor(self.rise * scale))
end
-- leaves from wherever the bounce currently is
function Fever:stop()
if not self.state then return end
local y = self:y()
self:play("out", { { 0.25, -self.bounce - y / tex.get_screen_scale(), "out" }, { 0.75, self.drop, "in" } }, y)
end
function Fever:update(current_time_ms, bpm)
if bpm then self.beat = 60000 / bpm end
if not self.state then return end
for _, m in ipairs(self.moves) do m:update(current_time_ms) end
if not self.moves[#self.moves].is_finished then return end
if self.state == "out" then
self.state = nil
else
if self.poses then self.pose = 1 - self.pose end
self:play("bounce", { { 0.5, -self.bounce, "out" }, { 0.5, self.bounce, "in" } }, 0)
end
end
function Fever:draw()
if not self.state then return end
local y = self:y()
for _, t in ipairs(self._ids) do tex.draw_texture(t[1], { frame = self.pose, y = y, blend = t[2] }) end
end
function Fever:destroy() tex.unload_folder(self.path, "fever/" .. self.name) end
-- a default line-up (index 0-3) from the folder fever_<index> under path
function Fever.create(index, bpm, path)
local spec = setmetatable({ name = "fever_" .. index }, { __index = SPECS[index] })
return Fever.new(bpm, path or "background", spec)
end
return Fever