Files
2026-10-13 13:22:39 -04:00

177 lines
6.7 KiB
Lua

ClearAnimation = {}
ClearAnimation.__index = ClearAnimation
-- Lumen action_result/success: the bachio pair fades in, slides out and settles
-- while the five letters pop up one after another; the highlight flashes over
-- the letters, the whole word replaces them and sparkles loop over it.
-- Offsets are from the bachio textures' json top-left (the right bachio mirrors x).
local PIECE_X = { 0, 59, 118, 185, 256 } -- letter lefts from the first letter
local PIECE_START, PIECE_STEP = 333.33, 50 -- ms
local PIECE_RISE, PIECE_FALL, PIECE_POP = 167, 150, 15
-- in pose frame 0, then out frames 0..3 (the last one rests): dx, dy
local OUT_POSES = {
{ -233, 1 }, { -204, -20 }, { -204, -20 }, { -212, 1 }, { -212, 1 },
}
local IN_REST = { -1, 1 }
-- sparkles loop with sprite 52 (95 frames) from 1783 ms, one every 83.33 ms
local SPARKLE_LOOP, SPARKLE_START, SPARKLE_STEP = 1583.33, 1783.33, 83.33
local SPARKLES = {
{ -1.65, -48.65 }, { 120.35, -48.65 }, { -121.65, -48 }, { -79.65, 48 },
{ 156.35, 33.35 }, { -161.65, 30 }, { 68.35, 48 },
}
function ClearAnimation.new(is_2p)
local self = setmetatable({}, ClearAnimation)
self.is_2p = is_2p
self.idx = is_2p and 1 or 0
self.draw_clear_full = false
self.name = "in"
self.frame = 0
self.start_ms = nil
self.dx, self.dy = IN_REST[1], IN_REST[2]
local function game_anim(id) return tex.get_animation(id, "game") end
self.bachio_fade_in = game_anim(46)
self.pose_in = game_anim(94)
self.pose_out = game_anim(95)
self.slide = game_anim(96)
self.overshoot = game_anim(97)
self.settle = game_anim(98)
self.highlight_fade_in = game_anim(99)
self.highlight_fade_out = game_anim(100)
self.anims = {
self.bachio_fade_in, self.pose_in, self.pose_out, self.slide, self.overshoot,
self.settle, self.highlight_fade_in, self.highlight_fade_out,
}
self.piece_fade, self.piece_rise, self.piece_fall = {}, {}, {}
for i = 1, 5 do
local delay = PIECE_START + (i - 1) * PIECE_STEP
self.piece_fade[i] = anim.fade(PIECE_RISE, { initial_opacity = 0.0, final_opacity = 1.0, delay = delay, ease_out = "quadratic" })
self.piece_rise[i] = anim.move(PIECE_RISE, { total_distance = PIECE_POP, delay = delay, ease_out = "quadratic" })
self.piece_fall[i] = anim.move(PIECE_FALL, { total_distance = PIECE_POP, delay = delay + PIECE_RISE, ease_in = "quadratic" })
for _, a in ipairs({ self.piece_fade[i], self.piece_rise[i], self.piece_fall[i] }) do
self.anims[#self.anims + 1] = a
end
end
for _, a in ipairs(self.anims) do a:start() end
audio.play_sound("clear", "sound")
self.t_clear = tex.get_texture("game/ending_anim/clear")
self.t_clear_separated = tex.get_texture("game/ending_anim/clear_separated")
self.t_clear_sep_hl = tex.get_texture("game/ending_anim/clear_separated_highlight")
self.t_clear_highlight = tex.get_texture("game/ending_anim/clear_highlight")
self.t_bachio_l_in = tex.get_texture("game/ending_anim/bachio_l_in")
self.t_bachio_l_out = tex.get_texture("game/ending_anim/bachio_l_out")
self.t_bachio_r_in = tex.get_texture("game/ending_anim/bachio_r_in")
self.t_bachio_r_out = tex.get_texture("game/ending_anim/bachio_r_out")
self.t_sparkle = tex.get_texture("game/ending_anim/sparkle")
return self
end
function ClearAnimation:update(current_ms)
self.start_ms = self.start_ms or current_ms
self.now = current_ms - self.start_ms
for _, a in ipairs(self.anims) do a:update(current_ms) end
-- the highlight has covered the letters: the whole word takes over
if self.highlight_fade_in.is_finished then
self.draw_clear_full = true
end
if self.pose_in.is_finished then
self.name = "out"
local step = math.floor(self.pose_out.attribute) + 1
self.frame = step
self.dx, self.dy = OUT_POSES[step][1], OUT_POSES[step][2]
else
self.name = "in"
self.frame = math.floor(self.pose_in.attribute)
if self.frame == 0 then
self.dx, self.dy = IN_REST[1], IN_REST[2]
else
self.dx = self.slide.attribute + self.overshoot.attribute - self.settle.attribute
self.dy = IN_REST[2]
end
end
end
function ClearAnimation:draw_bachio(side)
-- out step 1 is the in pose's first frame, steps 2..5 are out frames 0..3
local t, frame
if self.name == "in" or self.frame == 1 then
t = side < 0 and self.t_bachio_l_in or self.t_bachio_r_in
frame = self.name == "in" and self.frame or 0
else
t = side < 0 and self.t_bachio_l_out or self.t_bachio_r_out
frame = self.frame - 2
end
tex.draw_texture(t, {
frame = frame,
x = -side * self.dx,
y = self.dy,
fade = self.bachio_fade_in.attribute,
index = self.idx,
})
end
function ClearAnimation:draw_sparkles()
if not self.now then return end
for k, s in ipairs(SPARKLES) do
local t = self.now - SPARKLE_START - (k - 1) * SPARKLE_STEP
if t >= 0 then
t = t % SPARKLE_LOOP
if t < 333.33 then
local grow = math.min(t / 166.67, 1)
tex.draw_texture(self.t_sparkle, {
x = s[1],
y = s[2],
scale = 0.2 + 0.8 * grow,
center = true,
fade = t < 183.33 and 1.0 or 0.89 * (1 - (t - 183.33) / 150),
index = self.idx,
})
end
end
end
end
function ClearAnimation:draw()
if self.draw_clear_full then
tex.draw_texture(self.t_clear, { index = self.idx })
tex.draw_texture(self.t_clear_highlight, {
fade = self.highlight_fade_out.attribute,
index = self.idx,
})
self:draw_sparkles()
else
for i = 5, 1, -1 do
local j = i - 1
local pop = self.piece_rise[i].attribute - self.piece_fall[i].attribute
local params = {
frame = j,
x = PIECE_X[i],
y = -pop,
y2 = pop,
fade = self.piece_fade[i].attribute,
index = self.idx,
}
tex.draw_texture(self.t_clear_separated, params)
tex.draw_texture(self.t_clear_sep_hl, {
frame = j,
x = PIECE_X[i],
y = -pop,
y2 = pop,
fade = math.min(self.highlight_fade_in.attribute, self.piece_fade[i].attribute),
index = self.idx,
})
end
end
self:draw_bachio(-1)
self:draw_bachio(1)
end