90 lines
4.1 KiB
Lua
90 lines
4.1 KiB
Lua
-- YataiDON-HSS — the GAME→RESULT transition, ROUND 13.
|
||
--
|
||
-- The cabinet does NOT close a shutter here. `000_default/loading/loading_kuro_result.nulm`
|
||
-- (root sprite 4, 166 frames, labels `init 0 / in 6 / stop 115 / out 145 / end 165`) is a
|
||
-- SINGLE full-stage black shape whose **alpha** ramps linearly 0 → 1 over frames 6…115 —
|
||
-- 109 frames = 1816.7 ms — then holds at `stop` until the result screen takes over.
|
||
--
|
||
-- The engine's built-in rig (`src/objects/game/result_transition.cpp`) is a `move`
|
||
-- animation with no alpha at all, so this could not be expressed as skin data; round 13
|
||
-- added the scripted hook (see ENGINE_BINDINGS.md) and this file is the consumer.
|
||
-- The engine calls `start` / `update` / `draw` / `is_finished`; defining `draw` is what
|
||
-- takes the shutter out of the picture, and defining `is_finished` is what stops the scene
|
||
-- cutting to RESULT at 983 ms, when the screen would still be only ~54 % black.
|
||
--
|
||
-- The ramp itself is NOT hand-fitted: it is the exported arcade timeline,
|
||
-- `Scripts/anim/kuro_result.lua` (ANIM_PIPELINE.md). `anim_export.py` compressed the 318
|
||
-- dense rows to 4 breakpoints, which is itself the proof the ramp is linear between them,
|
||
-- and `anim_check.py kuro_result` samples the shipped table through the shipped sampler at
|
||
-- every arcade frame: `a max|d| = 0.001938`, inside the 0.004 (1/256) export tolerance.
|
||
--
|
||
-- The fade-out half of the record (`stop 115` → `end 165`) is deliberately NOT played
|
||
-- here: the result screen's own entry already fades its backdrop out over the board
|
||
-- (`result` animation 15), so playing both would double the dip.
|
||
|
||
local anim = require("anim/sampler")
|
||
|
||
ResultTransition = {}
|
||
ResultTransition.__index = ResultTransition
|
||
|
||
local F_IN, F_STOP = 6, 115 -- loading_kuro_result frame labels
|
||
local MS_PER_F = 1000.0 / 60.0
|
||
local TRACK = "#3@0" -- the black plate's own placement
|
||
|
||
function ResultTransition.new(player_num)
|
||
local self = setmetatable({}, ResultTransition)
|
||
self.player_num = player_num
|
||
self.t0 = nil
|
||
self.alpha = 0.0
|
||
-- Belt and braces: the engine already refuses to forward `update` / `is_finished`
|
||
-- before `start()` (it drives this object from the first frame of the song, and
|
||
-- `is_finished` is what ends the GAME screen), but a script that can run its clock
|
||
-- unstarted would cut the chart short, so refuse here too.
|
||
self.started = false
|
||
-- FAIL SOFT: `anim.safe` never raises — a missing or malformed generated table
|
||
-- yields the NULL clip, whose readers return nil, and `update` falls back to the
|
||
-- arcade's own documented linear ramp below.
|
||
self.clip = anim.safe("anim/kuro_result")
|
||
self.t_black = tex.get_texture("global/result_transition/fade")
|
||
return self
|
||
end
|
||
|
||
function ResultTransition:start()
|
||
self.t0 = nil
|
||
self.alpha = 0.0
|
||
self.started = true
|
||
end
|
||
|
||
function ResultTransition:update(current_ms)
|
||
if not self.started then return end
|
||
if not self.t0 then self.t0 = current_ms end
|
||
local f = F_IN + (current_ms - self.t0) / MS_PER_F
|
||
if f > F_STOP then f = F_STOP end
|
||
|
||
local a = self.clip:at(TRACK, f)
|
||
if not a then
|
||
-- the generated table is gone: the record is linear f6…f115, 0 → 1
|
||
a = (f - F_IN) / (F_STOP - F_IN)
|
||
end
|
||
if a < 0 then a = 0 elseif a > 1 then a = 1 end
|
||
self.alpha = a
|
||
end
|
||
|
||
-- The engine hands the screen over once this is true AND the result voice has stopped.
|
||
function ResultTransition:is_finished()
|
||
return self.started and self.alpha >= 1.0
|
||
end
|
||
|
||
function ResultTransition:draw()
|
||
if not self.t_black then return end
|
||
if self.alpha <= 0.004 then return end -- below the engine's own 1/256 quantisation
|
||
local w = tex.get_screen_width()
|
||
local h = tex.get_screen_height()
|
||
-- `fade.png` is an 8×8 black plate; texture.json carries its own 8×8 size as the base
|
||
-- destination rect and `x2`/`y2` are ADDED to it (texture.cpp:655-680), so this
|
||
-- stretches it to exactly the screen at any resolution.
|
||
tex.draw_texture(self.t_black, { x = 0, y = 0, x2 = w - 8, y2 = h - 8, fade = self.alpha })
|
||
end
|
||
|
||
return ResultTransition
|