forked from Yonokid/YataiDONNijiiro
A leaf placed with a negative-determinant matrix is exported as a negative sx with the angle of the mirrored axis; drawing it with a negative width put the piece on the wrong side, upside down (the control guide's second drum stick, and mirrored leaves in the full-combo / donderful banners). Such a leaf is now drawn as the |sx| copy mirrored horizontally, with the pivot moved to w - ox inside the flipped image and the angle brought back by 180 degrees. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
101 lines
3.7 KiB
Lua
101 lines
3.7 KiB
Lua
-- Ending banner (clear / full combo / donderful / fail), replayed from the cabinet
|
|
-- movie data exported by scratchpad/lyric/clip_export.py:
|
|
-- Scripts/game/ending_anim/ending_<kind>.lua -- frames[f+1] = {{shape, tx, ty, sx, sy, rot, a[, 8]}, ...}
|
|
-- (an 8th value 8 marks a leaf the cabinet draws with the add blend mode)
|
|
-- Graphics/game/ending_<kind>_anim/s<c>.png -- one texture per shape
|
|
-- The clip plays once from frame 0 and then loops from `loop_at`, at 60 fps, on the
|
|
-- 1920x264 stage the original places at the lane (screen y 276 for 1P; the 2P
|
|
-- lane adds the skin's game_2p_offset). The engine keeps its own ending
|
|
-- animation running for the sounds and hands the drawing to this object through
|
|
-- Background:handle_ending / draw_ending.
|
|
local Ending = {}
|
|
Ending.__index = Ending
|
|
|
|
local FRAME_MS = 1000.0 / 60.0
|
|
local STAGE_Y = 276
|
|
-- the movie root places every ending clip at (1209, 109.5) on its 1920x264 stage;
|
|
-- the exported frames are in clip-local coordinates, so add that placement
|
|
local CLIP_X, CLIP_Y = 1209, 109.5
|
|
local CLIPS = { donderful = "ending_donderful", full_combo = "ending_fullcombo",
|
|
clear = "ending_clear", fail = "ending_fail" }
|
|
|
|
function Ending.available(kind)
|
|
local name = CLIPS[kind]
|
|
if not name then return false end
|
|
local ok, data = pcall(require, "game/ending_anim/" .. name)
|
|
return ok and type(data) == "table" and data.frames ~= nil
|
|
end
|
|
|
|
function Ending.new(kind, player_num, path)
|
|
local name = CLIPS[kind]
|
|
local ok, data = pcall(require, "game/ending_anim/" .. name)
|
|
if not ok or type(data) ~= "table" or not data.frames then return nil end
|
|
local self = setmetatable({}, Ending)
|
|
self.kind = kind
|
|
self.subset = name .. "_anim"
|
|
self.path = path or "game"
|
|
tex.load_folder(self.path, self.subset)
|
|
self.data = data
|
|
self.ids = {}
|
|
self.t0 = nil
|
|
self.y = STAGE_Y
|
|
if player_num == 2 then
|
|
local off = tex.get_skin_config("game_2p_offset")
|
|
self.y = STAGE_Y + ((off and off.y) or 0)
|
|
end
|
|
return self
|
|
end
|
|
|
|
function Ending:id_of(shape)
|
|
local id = self.ids[shape]
|
|
if id == nil then
|
|
id = tex.get_id(self.subset, "s" .. shape) or false
|
|
self.ids[shape] = id
|
|
end
|
|
return id or nil
|
|
end
|
|
|
|
function Ending:frame(current_ms)
|
|
if not self.t0 then self.t0 = current_ms end
|
|
local f = math.floor((current_ms - self.t0) / FRAME_MS)
|
|
local n, loop_at = self.data.nframes, self.data.loop_at
|
|
if f >= n then
|
|
local span = n - loop_at
|
|
f = (span > 0) and (loop_at + (f - loop_at) % span) or (n - 1)
|
|
end
|
|
return f
|
|
end
|
|
|
|
function Ending:draw(current_ms)
|
|
local rows = self.data.frames[self:frame(current_ms) + 1]
|
|
if not rows then return end
|
|
local shapes = self.data.shapes
|
|
for i = 1, #rows do
|
|
local r = rows[i]
|
|
local s = shapes[r[1]]
|
|
local id = s and self:id_of(r[1])
|
|
if id then
|
|
local sx, sy, rot, ox, mirror = r[4], r[5], r[6], s[3], nil
|
|
-- mirrored leaf: see game/clip_player.lua
|
|
if sx < 0 then sx, rot, ox, mirror = -sx, rot - 180, s[1] - s[3], "horizontal" end
|
|
tex.draw_texture(id, {
|
|
x = CLIP_X + r[2],
|
|
y = self.y + CLIP_Y + r[3],
|
|
x2 = s[1] * (sx - 1),
|
|
y2 = s[2] * (sy - 1),
|
|
origin = { ox * sx, s[4] * sy },
|
|
rotation = rot,
|
|
mirror = mirror,
|
|
fade = r[7],
|
|
blend = (r[8] == 8) and "additive" or nil, -- cabinet add-blended leaf
|
|
})
|
|
end
|
|
end
|
|
end
|
|
|
|
function Ending:destroy()
|
|
tex.unload_folder(self.path, self.subset)
|
|
end
|
|
|
|
return Ending
|