Files
PyTaikoGreen/Scripts/background/bg_objects/petal_fever.lua
T
2026-10-10 01:55:40 -04:00

121 lines
4.8 KiB
Lua

local Timing = require("background/bg_objects/timing")
local chain_moves, sum = Timing.chain_moves, Timing.sum
-- Clear background with falling petals (A3, Touken Ranbu): drawn over the normal one, which
-- stays underneath. A haze fades in over `haze_in` beats; a beat in, the petal streams fade in
-- over half a beat. Losing clear fades the haze out over `haze_out` beats and the petals over
-- `petals_out`. Transitions in beats.
-- Each stream is three tiles (texture.json indices `first`.. of its `key`) drifting by
-- `dx`, `dy` over a `flight` (ms); a new pass starts every half flight, so two are on screen.
-- `warm` streams start with both passes showing (Lumen places the second at frame 0);
-- `fade_out` fades a pass over the second half of its flight; `rotation` turns each tile
-- about its centre (`half` px in).
-- spec: { path, haze = { key, blend }, haze_in, haze_out, petals_out, streams }
-- A value in thousandths that eases from wherever it is to a target (fade in/out envelopes)
local function envelope(current, moves, unit)
return { offset = current, moves = chain_moves(moves, unit, 1) }
end
local function env_value(env) return env.offset + sum(env.moves) end
local function env_update(env, current_time_ms)
for _, m in ipairs(env.moves) do m:update(current_time_ms) end
end
local PETALS_IN = { { 1, 0 }, { 0.5, 1000 } }
local TILES = 3
local PetalFever = {}
PetalFever.__index = PetalFever
function PetalFever.new(bpm, spec)
local self = setmetatable({}, PetalFever)
self.spec = spec
self.name = "bg_fever_0"
tex.load_folder(spec.path, "bg_fever/" .. self.name)
local function id(key) return tex.get_texture(spec.path .. "/" .. self.name .. "/" .. key) or 0 end
self._id_haze = id(spec.haze.key)
self._ids = {}
for i, stream in ipairs(spec.streams) do self._ids[i] = id(stream.key) end
self.beat = 60000 / bpm
self.haze = envelope(0, {}, 1)
self.petals = envelope(0, {}, 1)
self.streams = {}
return self
end
local function new_pass(stream, delay)
local half = anim.move(stream.flight / 2, { total_distance = 1000, delay = delay })
half:start()
return half
end
function PetalFever:start()
local haze, petals = env_value(self.haze), env_value(self.petals)
self.haze = envelope(haze, { { self.spec.haze_in, 1000 - haze } }, self.beat)
self.petals = envelope(petals, { PETALS_IN[1], { PETALS_IN[2][1], 1000 - petals } }, self.beat)
-- the streams start over as the petals begin to fade in
for i, stream in ipairs(self.spec.streams) do
self.streams[i] = { half = new_pass(stream, PETALS_IN[1][1] * self.beat), laps = stream.warm and 1 or 0 }
end
end
function PetalFever:stop()
local haze, petals = env_value(self.haze), env_value(self.petals)
self.haze = envelope(haze, { { self.spec.haze_out, -haze } }, self.beat)
self.petals = envelope(petals, { { self.spec.petals_out, -petals } }, self.beat)
end
function PetalFever:update(current_time_ms, bpm)
if bpm then self.beat = 60000 / bpm end
env_update(self.haze, current_time_ms)
env_update(self.petals, current_time_ms)
for i, s in pairs(self.streams) do
s.half:update(current_time_ms)
if s.half.is_finished then
s.half = new_pass(self.spec.streams[i])
s.laps = s.laps + 1
s.half:update(current_time_ms)
end
end
end
function PetalFever:draw_stream(i, opacity, scale)
local stream, s = self.spec.streams[i], self.streams[i]
-- pass 0 is in the first half of its flight, pass 1 (once there is one) in the second
for pass = 0, math.min(s.laps, 1) do
local p = (pass * 1000 + s.half.attribute) / 2000
local fade = opacity * (stream.alpha or 1)
if stream.fade_out and p > 0.5 then fade = fade * (1 - p) * 2 end
local origin = stream.rotation and stream.half * scale or 0
for t = 0, TILES - 1 do
tex.draw_texture(self._ids[i], {
index = (stream.first or 0) + t,
x = math.floor(stream.dx * p * scale) + origin,
y = math.floor(stream.dy * p * scale) + origin,
fade = fade,
blend = stream.blend,
rotation = stream.rotation,
origin = stream.rotation and { origin, origin } or nil,
})
end
end
end
function PetalFever:draw()
local haze = env_value(self.haze) / 1000
if haze > 0 then tex.draw_texture(self._id_haze, { fade = haze, blend = self.spec.haze.blend }) end
local petals = env_value(self.petals) / 1000
if petals <= 0 then return end
local scale = tex.get_screen_scale()
for i in ipairs(self.spec.streams) do
if self.streams[i] then self:draw_stream(i, petals, scale) end
end
end
function PetalFever:destroy()
tex.unload_folder(self.spec.path, "bg_fever/" .. self.name)
end
return PetalFever