local Timing = require("background/bg_objects/timing") local chain_moves, sum = Timing.chain_moves, Timing.sum local BaseFever = {} BaseFever.__index = BaseFever function BaseFever.new(index, bpm, path) local self = setmetatable({}, BaseFever) self.name = "fever_" .. index self.path = path or "background" tex.load_folder(self.path, "fever/" .. self.name) local half_beat = (60000 / bpm) / 2 self.bounce_up = anim.move(half_beat, {total_distance = 50, ease_out = "quadratic"}) self.bounce_down = anim.move(half_beat, {total_distance = 50, ease_in = "quadratic", delay = half_beat}) return self end -- Only look up the keys a variant draws: a missing key makes tex.get_texture log a load error function BaseFever:load_ids(keys) for _, key in ipairs(keys) do self["_id_" .. key] = tex.get_texture(self.path .. "/" .. self.name .. "/" .. key) end end function BaseFever:start() self.bounce_up:start() self.bounce_down:start() end function BaseFever:update(current_time_ms, bpm) self.bounce_up:update(current_time_ms) self.bounce_down:update(current_time_ms) if self.bounce_down.is_finished then local half_beat = (60000 / bpm) / 2 self.bounce_up = anim.move(half_beat, {total_distance = 50, ease_out = "quadratic"}) self.bounce_down = anim.move(half_beat, {total_distance = 50, ease_in = "quadratic", delay = half_beat}) self.bounce_up:start() self.bounce_down:start() end end function BaseFever:destroy() tex.unload_folder(self.path, "fever/" .. self.name) end local Fever0 = setmetatable({}, {__index = BaseFever}) Fever0.__index = Fever0 function Fever0.new(index, bpm, path) local self = setmetatable({}, Fever0) local base = BaseFever.new(index, bpm, path) for k, v in pairs(base) do self[k] = v end self:load_ids({ "overlay_l", "overlay_r" }) return self end function Fever0:draw() local y = self.bounce_down.attribute - self.bounce_up.attribute if self._id_overlay_l then tex.draw_texture(self._id_overlay_l, {y = y}) end if self._id_overlay_r then tex.draw_texture(self._id_overlay_r, {y = y}) end end -- Fever1 and Fever2 share the same draw as Fever0 local Fever1 = setmetatable({}, {__index = Fever0}) Fever1.__index = Fever1 function Fever1.new(index, bpm, path) local self = setmetatable({}, Fever1) local base = BaseFever.new(index, bpm, path) for k, v in pairs(base) do self[k] = v end self:load_ids({ "overlay_l", "overlay_r" }) return self end function Fever1:draw() Fever0.draw(self) end local Fever2 = setmetatable({}, {__index = Fever0}) Fever2.__index = Fever2 function Fever2.new(index, bpm, path) local self = setmetatable({}, Fever2) local base = BaseFever.new(index, bpm, path) for k, v in pairs(base) do self[k] = v end self:load_ids({ "overlay_l", "overlay_r" }) return self end function Fever2:draw() Fever0.draw(self) end local Fever3 = setmetatable({}, {__index = BaseFever}) Fever3.__index = Fever3 function Fever3.new(index, bpm, path) local self = setmetatable({}, Fever3) local base = BaseFever.new(index, bpm, path) for k, v in pairs(base) do self[k] = v end self:load_ids({ "overlay" }) return self end function Fever3:draw() local y = self.bounce_down.attribute - self.bounce_up.attribute if self._id_overlay then tex.draw_texture(self._id_overlay, {y = y}) end end -- LumenFever: the Lumen collabs' fever overlay (Miku, GUMI). The line-up jumps up from -- below the screen, bounces once a beat switching between its two poses (overlay_l/overlay_r -- frames 0 and 1), and hops off again when the rainbow gauge is lost. Timed in beats. local FEVER_IN = { { 0.475, -280, "out" }, { 0.5, 70, "in" } } local FEVER_BOUNCE = { { 0.5, -70, "out" }, { 0.5, 70, "in" } } local FEVER_OUT = { { 0.25, -70, "out" }, { 0.75, 400, "in" } } local FEVER_IN_OFFSET = 210 local LumenFever = {} LumenFever.__index = LumenFever function LumenFever.new(bpm, path) local self = setmetatable({}, LumenFever) self.name = "fever_0" self.path = path tex.load_folder(self.path, "fever/" .. self.name) self._id_l = tex.get_texture(self.path .. "/" .. self.name .. "/" .. "overlay_l") or 0 self._id_r = tex.get_texture(self.path .. "/" .. self.name .. "/" .. "overlay_r") or 0 self.beat = 60000 / bpm self.state = nil -- nil (hidden), "in", "bounce", "out" return self end function LumenFever:play(state, moves, offset) self.state = state self.offset = offset self.moves = chain_moves(moves, self.beat, tex.get_screen_scale()) end function LumenFever:y() return self.offset + sum(self.moves) end function LumenFever:start() self.pose = 0 self:play("in", FEVER_IN, math.floor(FEVER_IN_OFFSET * tex.get_screen_scale())) end -- leaves from wherever the bounce currently is function LumenFever:stop() if self.state then self:play("out", FEVER_OUT, self:y()) end end function LumenFever: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 self.pose = 1 - self.pose self:play("bounce", FEVER_BOUNCE, 0) end end function LumenFever:draw() if not self.state then return end local y = self:y() tex.draw_texture(self._id_l, { frame = self.pose, y = y }) tex.draw_texture(self._id_r, { frame = self.pose, y = y }) end function LumenFever:destroy() tex.unload_folder(self.path, "fever/" .. self.name) end local Fever = {} Fever.Base = BaseFever Fever.Fever0 = Fever0 Fever.Fever1 = Fever1 Fever.Fever2 = Fever2 Fever.Fever3 = Fever3 Fever.Lumen = LumenFever function Fever.create(index, bpm, path) local map = {Fever0, Fever1, Fever2, Fever3} return map[index + 1].new(index, bpm, path) end return Fever