The exported clips and backdrop movies were drawn source-over everywhere, but the cabinet places a number of glow layers with its add blend mode: the lantern glows of the lower backdrops (bg_nomal_a_01..05), the crown's radial burst, the score rank plate's effect copy, and the full-combo / donderful letter glows. Drawn source-over, those textures show up as dark or grey patches. - movie_export / clip_export record the placement's blend mode (`blend = 8` on a backdrop leaf, an 8th row value on a clip leaf; the root placement's mode is folded into its group), and bg_anim / clip_player / ending draw such leaves with blend = "additive". Data re-exported; only the blend field changed. - result_crown: the burst copies draw additively; the crown_loop shine sweep is a plain white polygon on the cabinet, so gleam frames 0..2 are white now instead of the atlas colour they were exported with. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
211 lines
11 KiB
Lua
211 lines
11 KiB
Lua
-- YataiDON-HSS override of PyTaikoGreen/result/result_crown.lua
|
|
-- ROUND 6 (LUA_CAPABILITIES item 33) — the arcade crown fly-in + crown_loop sparkles,
|
|
-- keyed frame-for-frame off lumen `result/result` MC 73 `crown_mc`
|
|
-- (125 frames, labels init 0 / start 5 / end 120). `t` below is FRAMES since label "start",
|
|
-- i.e. since this object is constructed (result_player.lua builds it at the crown beat).
|
|
--
|
|
-- d3 `crown` MC 45 -> result/crown/crown_clear|fc|dfc (crown_effect__shape39/41/43)
|
|
-- f 5..18 scale 3.0 -> 0.8, alpha 0 -> 1. Both channels are driven by u = (t/13)^2
|
|
-- (verified: scale = 0.8 + 2.2*(1-u), alpha = u, max error < 1/256 on all 14 frames)
|
|
-- f18..22 scale 0.8 -> 1.0 (+0.05/frame) — the settle overshoot, then held at 1.0
|
|
-- d2 `crown_effect` the same crown as a white flash -> result/crown/crown_fade
|
|
-- f18..22 scale 0.8 -> 1.0 with the crown, alpha 1
|
|
-- f22..47 scale 1.0 -> 2.0 (+0.04/frame), alpha 1 -> 0 (25 frames)
|
|
-- d0/d1 MC 38 -> crown_mc__shape36, a 288x288 radial burst, two copies
|
|
-- f18..22 alpha 161/256 -> 1; scale d0 0.050 -> 0.154, d1 0.100 -> 0.299
|
|
-- f22..54 alpha 1 -> 0; scale d0 0.154 -> 0.749, d1 0.299 -> 1.423 (32 frames)
|
|
-- d4 MC 50 -> a 5-star cluster (score_rank_kiwami_mc__shape46)
|
|
-- f22..59 scale 0.5 -> 1.0, offset (30.2,-2.2) -> (63.2,-17.0)
|
|
-- f59..100 scale 1.0 -> 1.2, offset -> (76.5,-23.0)
|
|
-- f101..119 alpha 1 -> 0
|
|
-- d5 `crown_loop` MC 71/72 — from f120 a 180-frame loop whose only moving part is a
|
|
-- 3-step shine sweep: shape65 @ (-28,-30) frames 0..2, shape67 @ (6,8)
|
|
-- frames 3..5, shape69 @ (28,42) frames 6..8, then 171 idle frames.
|
|
--
|
|
-- The loop shine is a solid white polygon on the cabinet (a plain fill, no texture),
|
|
-- clipped to the crown; gleam frames 0..2 carry it as white with the clipped alpha.
|
|
-- d0/d1, d4 and the loop shine all live in `result/crown/gleam` — ROUND 6 re-baked it from
|
|
-- 3 transparent 152x152 frames into 5 x 320x320 frames, each centred on the crown's own
|
|
-- registration point (see scratchpad r6_bake_gleam.py and Graphics/result/MAPPING.md):
|
|
-- 0/1/2 = the loop shine sweep, 3 = the radial burst, 4 = the star cluster.
|
|
ResultCrown = {}
|
|
ResultCrown.__index = ResultCrown
|
|
|
|
-- ROUND 7 — the per-channel constants above are no longer typed in by hand: every
|
|
-- curve below is SAMPLED out of `Scripts/anim/crown_mc.lua`, exported straight from
|
|
-- the cabinet's own `result/result.nulm` sprite 73 by scratchpad/anim_export.py
|
|
-- (see ANIM_PIPELINE.md). Track names are the arcade display-list paths; an unnamed
|
|
-- placement is `#<character>@<depth>`, which is why the two radial-burst copies read as
|
|
-- `#38@0` / `#38@1` and the star cluster's container as `#50@4`.
|
|
--
|
|
-- Two things the export corrected in the round 6 hand-port:
|
|
-- * the radial burst is TWO EQUAL copies (both 0.100 -> 0.307 -> 1.498). Round 6 read
|
|
-- the first one as half the size of the second (0.050 -> 0.154 -> 0.749), so our
|
|
-- smaller burst was drawing at half the cabinet's diameter for its whole life.
|
|
-- * the star cluster's fade-out runs f100..f119 of the MC, i.e. t = 95..114 frames
|
|
-- after `start`. Round 6 keyed it on t = 100..119, so it faded 83 ms late.
|
|
local F = 1000 / 60 -- one arcade frame in ms
|
|
|
|
-- ROUND 8: fail soft. `pcall` the require and use `anim.safe`, which hands back a NULL
|
|
-- clip when a generated table is missing or malformed, so every reader below falls
|
|
-- through to its `or <constant>` default instead of taking the screen down.
|
|
local _cr_ok, crown_anim = pcall(require, "anim/sampler")
|
|
if not _cr_ok then crown_anim = { safe = function()
|
|
-- sampler.lua itself is missing: hand back the same NULL clip shape it would
|
|
local n = {}
|
|
n.at = function() return nil end; n.get = function() return nil end
|
|
n.label = function() return nil end; n.has = function() return false end
|
|
n.first = function() return 0 end; n.last = function() return 0 end
|
|
return n
|
|
end } end
|
|
local CROWN = crown_anim.safe("anim/crown_mc")
|
|
local CROWN_START_F = CROWN:label("start") or 5 -- t = 0 is this arcade frame
|
|
local CROWN_END_F = CROWN:label("end") or 120
|
|
|
|
local GL_SHINE = { 0, 1, 2 } -- gleam frames of the crown_loop sweep
|
|
local GL_BURST, GL_STARS = 3, 4
|
|
|
|
-- ROUND 53 (r53-anim-lumen-sweep): the crown_loop shine sweep, sampled. `anim/crown_loop`
|
|
-- is the nested MC 71 (`crown_loop`, 180 f) that sprite 73 parks on from the `end` label:
|
|
-- the gleam shape steps through three placements — (-28,-30) f0..2, (6,8) f3..5,
|
|
-- (28,42) f6..8 — then 171 idle frames. Those offsets are baked into our gleam frames
|
|
-- 0/1/2, so the leaf windows drive WHICH frame shows and WHEN; the R7 hand ladder
|
|
-- (k<9, floor(k/3)) is kept below as the fail-soft fallback.
|
|
local CLOOP = crown_anim.safe("anim/crown_loop")
|
|
local CLOOP_LEN = CLOOP:last() - CLOOP:first() + 1 -- 180
|
|
local CLOOP_LEAVES = { { "#65@1", 1 }, { "#67@1", 2 }, { "#69@1", 3 } } -- -> GL_SHINE idx
|
|
-- GL_SHINE index visible at loop-frame k, or nil during the idle stretch / missing table
|
|
local function crown_loop_shine(k)
|
|
if not CLOOP.tracks then return nil, false end
|
|
local ki = math.floor(k)
|
|
for i = 1, #CLOOP_LEAVES do
|
|
local rows = CLOOP.tracks[CLOOP_LEAVES[i][1]]
|
|
if rows and #rows > 0 and ki >= rows[1][1] and ki <= rows[#rows][1] then
|
|
return CLOOP_LEAVES[i][2], true
|
|
end
|
|
end
|
|
return nil, true -- table present, idle frame
|
|
end
|
|
|
|
local function clamp01(v)
|
|
if v <= 0 then return 0 end
|
|
if v >= 1 then return 1 end
|
|
return v
|
|
end
|
|
|
|
local function lerp(a, b, p) return a + (b - a) * p end
|
|
|
|
function ResultCrown.new(crown_type, is_2p, has_2p)
|
|
local self = setmetatable({}, ResultCrown)
|
|
|
|
self.is_2p = is_2p
|
|
self.p_idx = is_2p and 1 or 0
|
|
self.t0 = tex.get_current_ms()
|
|
self.now = self.t0
|
|
self.sound_played = false
|
|
|
|
local crown_names = { [0] = "crown_clear", [1] = "crown_fc", [2] = "crown_dfc" }
|
|
self.t_crown = tex.load_texture("result/crown/" .. (crown_names[crown_type] or "crown_clear"))
|
|
self.t_crown_fade = tex.load_texture("result/crown/crown_fade")
|
|
self.t_gleam = tex.load_texture("result/crown/gleam")
|
|
|
|
-- ROUND 52 (r52-lua-divergence-fixes): the arcade crown SE is
|
|
-- colour x seat, not one generic sample. ScoreBase.lua:322-342 builds
|
|
-- "crown_" .. silver|gold|rainbow (g_crown 1/2/3 = clear/FC/DFC) ..
|
|
-- _l/_r when g_entryType == 2, else _c. YataiDON crown_type is 0/1/2 =
|
|
-- clear/fc/dfc, i.e. arcade colour index + 1. Samples shipped ROUND 52
|
|
-- (se_result_v12a crown_{silver,gold,rainbow}_{c,l,r}); the old generic
|
|
-- "crown" (= crown_gold_c) stays on disk as the fallback for a stale
|
|
-- sound folder.
|
|
local colours = { [0] = "silver", [1] = "gold", [2] = "rainbow" }
|
|
local seat = has_2p and (is_2p and "_r" or "_l") or "_c"
|
|
self.sound_name = "crown_" .. (colours[crown_type] or "silver") .. seat
|
|
|
|
return self
|
|
end
|
|
|
|
function ResultCrown:update(current_ms)
|
|
self.now = current_ms
|
|
-- ROUND 15: the cue is the FLY-IN, not the settle. ScoreBase.lua StartCrownDisplay
|
|
-- does `crownMc_:GotoAndPlay("start")` and `TSound:Play("se_result_v12a", crown_<colour>_c)`
|
|
-- in the same statement block, so the sound lands on t = 0. Round 6 put it at t = 17
|
|
-- (the settle) because that was also the beat that popped the message bubble; the
|
|
-- bubble is now on its own arcade beat (MoveResultAction), so the two are independent.
|
|
if not self.sound_played and (current_ms - self.t0) / F >= 0 then
|
|
-- ROUND 52: colour x seat variant (see new()).
|
|
audio.play_sound(self.sound_name or "crown", "sound")
|
|
self.sound_played = true
|
|
end
|
|
end
|
|
|
|
-- result_player.lua gates the 「クリア」 bubble on this: the crown has landed at scale 1.0.
|
|
function ResultCrown:is_settled()
|
|
return (self.now - self.t0) / F >= 17
|
|
end
|
|
|
|
function ResultCrown:draw()
|
|
local t = (self.now - self.t0) / F
|
|
if t < 0 then return end
|
|
local idx = self.p_idx
|
|
|
|
local f = CROWN_START_F + t -- absolute arcade frame on sprite 73
|
|
|
|
-- ---- d3 crown -------------------------------------------------------------
|
|
local c_scale = CROWN:get("crown", f, "sx") or 1.0
|
|
local c_alpha = CROWN:get("crown", f, "a") or 1.0
|
|
if self.t_crown then
|
|
tex.draw_texture(self.t_crown, { scale = c_scale, center = true, index = idx, fade = c_alpha })
|
|
end
|
|
|
|
-- ---- d2 crown_effect (white flash ghost) -----------------------------------
|
|
if self.t_crown_fade and t >= 13 and t <= 42 then
|
|
local s = CROWN:get("crown_effect", f, "sx") or 1.0
|
|
local a = CROWN:get("crown_effect", f, "a") or 0.0
|
|
if a > 0.001 then
|
|
tex.draw_texture(self.t_crown_fade, { scale = s, center = true, index = idx, fade = a })
|
|
end
|
|
end
|
|
|
|
if not self.t_gleam then return end
|
|
|
|
-- ---- d0 / d1 radial burst ---------------------------------------------------
|
|
-- two EQUAL copies, differing only in a sub-pixel jitter the cabinet's tween leaves
|
|
-- behind; drawn twice because the arcade draws it twice and the doubled coverage is
|
|
-- what makes the flash read as bright rather than as one thin ring.
|
|
if t >= 13 and t <= 49 then
|
|
local a = CROWN:get("#38@1", f, "a") or 0.0
|
|
local s1 = CROWN:get("#38@1", f, "sx") or 0.1
|
|
local s0 = CROWN:get("#38@0", f, "sx") or 0.1
|
|
if a > 0.001 then
|
|
-- the cabinet places both burst copies with the add blend mode
|
|
tex.draw_texture(self.t_gleam, { frame = GL_BURST, scale = s1, center = true, index = idx, fade = a, blend = "additive" })
|
|
tex.draw_texture(self.t_gleam, { frame = GL_BURST, scale = s0, center = true, index = idx, fade = a, blend = "additive" })
|
|
end
|
|
end
|
|
|
|
-- ---- d4 drifting star cluster ----------------------------------------------
|
|
if t >= 17 and t <= 119 then
|
|
local s = CROWN:get("#50@4", f, "sx") or 0.5
|
|
local ox = CROWN:get("#50@4", f, "tx") or 30.2
|
|
local oy = CROWN:get("#50@4", f, "ty") or -2.2
|
|
local a = CROWN:get("#50@4", f, "a") or 1.0
|
|
tex.draw_texture(self.t_gleam,
|
|
{ frame = GL_STARS, scale = s, center = true, index = idx, x = ox, y = oy, fade = clamp01(a) })
|
|
end
|
|
|
|
-- ---- d5 crown_loop shine sweep (180-frame loop from "end") -------------------
|
|
-- ROUND 53: which gleam frame shows, and when, now comes from anim/crown_loop's own
|
|
-- leaf windows; the R7 hand ladder is the fail-soft fallback.
|
|
if f >= CROWN_END_F - 5 then
|
|
local k = (f - (CROWN_END_F - 5)) % (CLOOP_LEN > 1 and CLOOP_LEN or 180)
|
|
local shine, have_table = crown_loop_shine(k)
|
|
if shine then
|
|
tex.draw_texture(self.t_gleam,
|
|
{ frame = GL_SHINE[shine], center = true, index = idx })
|
|
elseif not have_table and k < 9 then
|
|
tex.draw_texture(self.t_gleam,
|
|
{ frame = GL_SHINE[math.floor(k / 3) + 1], center = true, index = idx })
|
|
end
|
|
end
|
|
end
|