diff --git a/Graphics/game/hit_effect/firework.png b/Graphics/game/hit_effect/firework.png new file mode 100644 index 0000000..f6ac29f --- /dev/null +++ b/Graphics/game/hit_effect/firework.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e16ba21d00a5cded6fafc1d1933de58577f492906d41dc807f5a28734e26f6f6 +size 429400 diff --git a/Graphics/game/hit_effect/texture.json b/Graphics/game/hit_effect/texture.json index a9937d4..41bfe34 100644 --- a/Graphics/game/hit_effect/texture.json +++ b/Graphics/game/hit_effect/texture.json @@ -146,5 +146,87 @@ 336 ] ] + }, + "firework": { + "crop": [ + [ + 0, + 0, + 256, + 256 + ], + [ + 256, + 0, + 256, + 256 + ], + [ + 512, + 0, + 256, + 256 + ], + [ + 768, + 0, + 256, + 256 + ], + [ + 0, + 256, + 256, + 256 + ], + [ + 256, + 256, + 256, + 256 + ], + [ + 512, + 256, + 256, + 256 + ], + [ + 768, + 256, + 256, + 256 + ], + [ + 0, + 512, + 256, + 256 + ], + [ + 256, + 512, + 256, + 256 + ], + [ + 512, + 512, + 256, + 256 + ], + [ + 768, + 512, + 256, + 256 + ], + [ + 0, + 768, + 256, + 256 + ] + ] } } diff --git a/Scripts/game/note_arc.lua b/Scripts/game/note_arc.lua new file mode 100644 index 0000000..18193a8 --- /dev/null +++ b/Scripts/game/note_arc.lua @@ -0,0 +1,94 @@ +-- This skin's note arc: the parent's script with the flight and the big-note sparks redone as +-- the cabinet's (onp_jump): +-- * the note flies from the judge circle to 魂 in 30 frames on the cabinet's path, here a +-- cubic Bezier fitted to it (within 6 px), P1 arcing up and P2 down, each with its own fit; +-- it then waits at 魂 until the engine's flight (31 frames) ends +-- * a big note leaves 9 sparks (hit_effect/firework, the cabinet's 13-frame spark on black), +-- the first at frame 5 and then every 3 frames, each centred where the note was about 2 +-- frames before, at full size, added onto the scene +-- The balloon keeps the parent's flight and rainbow (the cabinet's balloon has its own path). +-- Everything else is the parent's file, loaded from its own copy. + +-- the parent skin's copy of a script: the second match on package.path (the first is this file) +local function load_parent(name) + local seen = false + for tmpl in package.path:gmatch("[^;]+") do + local chunk = loadfile((tmpl:gsub("%?", name))) + if chunk then + if seen then return chunk end + seen = true + end + end +end + +assert(load_parent("game/note_arc"), "game/note_arc: the parent skin has no note_arc script")() + +local FRAME_MS = 16.67 +local FLIGHT_MS = 30 * FRAME_MS +-- control points as offsets from the start (c1) and from the end (c2) +local CONTROL = { + [1] = { c1x = 196.8, c1y = -474.8, c2x = -300.4, c2y = -419.3 }, + [2] = { c1x = 178.5, c1y = 486.4, c2x = -314.9, c2y = 414.3 }, +} +-- sparks: spawn frames, and where each sits on the 30-frame flight (frames) +local SPARK_COUNT = 9 +local SPARK_SPAWN, SPARK_SPAWN_STEP = 5, 3 +local SPARK_AT, SPARK_AT_STEP = 2.58, 3.085 + +local parent_new = NoteArc.new +function NoteArc.new(note_tex_type, big, is_balloon, player_num, start_x, start_y) + local self = parent_new(note_tex_type, big, is_balloon, player_num, start_x, start_y) + local c = CONTROL[self.p2 and 2 or 1] + self.c1x, self.c1y = self.sx + c.c1x, self.sy + c.c1y + self.c2x, self.c2y = self.ex + c.c2x, self.ey + c.c2y + + -- a spark is centred on the note: both textures' own offsets and sizes + local note = tex.get_texture_info("notes", tostring(note_tex_type)) + local spark = self.t_firework and tex.get_texture_info("hit_effect", "firework") + if note and spark then + self.spark_dx = note.x[1] - spark.x[1] + (note.x2[1] - spark.x2[1]) / 2 + self.spark_dy = note.y[1] - spark.y[1] + (note.y2[1] - spark.y2[1]) / 2 + end + return self +end + +local parent_point = NoteArc.point +local parent_draw = NoteArc.draw + +-- the cabinet's flight, progress 0..1 over its 30 frames +function NoteArc:point(progress) + if self.is_balloon then return parent_point(self, progress) end + local t = math.max(0, math.min(1, progress)) + local u = 1 - t + local b0, b1, b2, b3 = u * u * u, 3 * u * u * t, 3 * u * t * t, t * t * t + return b0 * self.sx + b1 * self.c1x + b2 * self.c2x + b3 * self.ex, + b0 * self.sy + b1 * self.c1y + b2 * self.c2y + b3 * self.ey +end + +function NoteArc:draw_fireworks(y, elapsed_ms) + if not self.spark_dx then return end + for i = 0, SPARK_COUNT - 1 do + local age_ms = elapsed_ms - (SPARK_SPAWN + SPARK_SPAWN_STEP * i) * FRAME_MS + local frame = math.floor(age_ms / FRAME_MS) + if age_ms >= 0 and frame < self.firework_frames then + local fx, fy = self:point((SPARK_AT + SPARK_AT_STEP * i) * FRAME_MS / FLIGHT_MS) + tex.draw_texture(self.t_firework, { + frame = frame, center = true, blend = "additive", + x = fx + self.spark_dx, y = y + fy + self.spark_dy, + }) + end + end +end + +function NoteArc:draw(y, elapsed_ms, duration_ms) + if self.is_balloon then return parent_draw(self, y, elapsed_ms, duration_ms) end + if self.big and self.t_firework and self.firework_frames > 0 then + self:draw_fireworks(y, elapsed_ms) + end + if elapsed_ms < duration_ms then + local x_i, y_i = self:point(elapsed_ms / FLIGHT_MS) + tex.draw_texture(self.t_note, { x = x_i, y = y + y_i }) + end +end + +return NoteArc