Files
YataiDON14/Scripts/game/note_arc.lua
T
2026-10-15 09:16:53 -04:00

105 lines
3.9 KiB
Lua

local SKIN_S = tex.get_screen_scale()
local SKIN = {
note_arc_start_x_offset = { x = 150 },
note_arc_curve_height = { height = 244.5 },
note_arc_balloon_p2_y = { y = 435 * SKIN_S },
}
NoteArc = {}
NoteArc.__index = NoteArc
local ARC_POINTS = 100
local FRAME_MS = 16.67
local function trunc(v) return v < 0 and math.ceil(v) or math.floor(v) end
function NoteArc.new(note_tex_type, big, is_balloon, player_num, start_x, start_y)
local self = setmetatable({}, NoteArc)
self.big = big
self.is_balloon = is_balloon
self.p2 = player_num == 2
local curve_height = SKIN.note_arc_curve_height.height
local sx = start_x + SKIN.note_arc_start_x_offset.x
local sy = start_y + tex.get_skin_config("game_notes").y
local hit = tex.get_skin_config("game_gauge_hit_effect_note")
local ex, ey = hit.x, hit.y
if self.p2 then ey = ey + hit.height end
self.sx, self.sy, self.ex, self.ey = sx, sy, ex, ey
self.cx = (sx + ex) / 2
if self.p2 then
self.cy = math.max(sy, ey) + curve_height -- arc downward
else
self.cy = math.min(sy, ey) - curve_height -- arc upward
end
self.t_note = tex.get_texture("game/notes/" .. note_tex_type)
self.note_width = tex.get_texture_info("notes", tostring(note_tex_type)).width
self.t_firework = big and tex.get_texture("game/hit_effect/firework") or nil
self.firework_frames = self.t_firework and tex.get_texture_info("hit_effect", "firework").frame_count or 0
self.t_rainbow = tex.get_texture("game/balloon/rainbow")
if self.t_rainbow then
self.rainbow_info = tex.get_texture_info("balloon", "rainbow")
end
return self
end
-- bezier sampled at the same 100 steps the C++ cache used, truncated to whole pixels
function NoteArc:point(progress)
local t = math.min(math.floor(progress * ARC_POINTS), ARC_POINTS) / ARC_POINTS
local ti = 1 - t
return trunc(ti * ti * self.sx + 2 * ti * t * self.cx + t * t * self.ex),
trunc(ti * ti * self.sy + 2 * ti * t * self.cy + t * t * self.ey)
end
function NoteArc:draw_rainbow(y, raw_progress)
local w, h = self.rainbow_info.width, self.rainbow_info.height
local trail_start = math.max(0, raw_progress - 0.5)
local trail_end = math.min(raw_progress, 1)
if trail_end <= trail_start then return end
local crop_start = math.floor(trail_start * w + 0.5)
local crop_w = math.floor(trail_end * w + 0.5) - crop_start
if crop_w <= 0 then return end
local y_pos = self.p2 and SKIN.note_arc_balloon_p2_y.y or 0
tex.draw_texture(self.t_rainbow, {
x = crop_start, y = y + y_pos, x2 = -w + crop_w,
src = { x = crop_start, y = 0, width = crop_w, height = self.p2 and -h or h },
})
end
function NoteArc:draw_fireworks(y, elapsed_ms, duration_ms)
local half = self.note_width * 0.6 / 2
for i = 0, 8 do
local spawn_ms = (3 + 3 * i) * FRAME_MS
local age_ms = elapsed_ms - spawn_ms
local frame = math.floor(age_ms / FRAME_MS)
if age_ms >= 0 and frame < self.firework_frames then
local fx, fy = self:point(math.min(spawn_ms / duration_ms, 1))
tex.draw_texture(self.t_firework, { frame = frame, scale = 0.8, x = fx - half, y = y + fy - half, blend = "additive" })
end
end
end
function NoteArc:draw(y, elapsed_ms, duration_ms)
local progress = math.min(1, elapsed_ms / duration_ms)
local x_i, y_i = self:point(progress)
if self.is_balloon then
if not self.t_rainbow then
if progress < 1 then tex.draw_texture(self.t_note, { x = x_i, y = y + y_i }) end
return
end
self:draw_rainbow(y, elapsed_ms / duration_ms)
elseif self.big and self.t_firework and self.firework_frames > 0 then
self:draw_fireworks(y, elapsed_ms, duration_ms)
end
if progress < 1 then
tex.draw_texture(self.t_note, { x = x_i, y = y + y_i })
end
end
return NoteArc