154 lines
6.3 KiB
Lua
154 lines
6.3 KiB
Lua
-- Animated 1P result backdrop: sky halves, Mt Fuji and drifting cloud bars, plus
|
|
-- a white header band, all driven by the exported result_bg timeline.
|
|
|
|
local anim = require("anim/sampler")
|
|
|
|
local ResultBgLayers = {}
|
|
ResultBgLayers.__index = ResultBgLayers
|
|
|
|
local LOOP = 720 -- frames, sprite 83 / 87
|
|
local FUJI_LOOP = 90 -- frames, huji_1p_l_mc (sprite 86), clear state only
|
|
local MS_TO_F = 0.06 -- 60 fps
|
|
|
|
-- depth, cloud texture index, y, scale (|sx| == sy for every one of them), mirror, source width
|
|
-- (only a mirrored layer needs it -- and for both of those the base and clear bakes are the same
|
|
-- width), arcade track name, frame-0 tx, frame-0 alpha
|
|
local LAYERS = {
|
|
{ 3, 0, 162.60, 1.0, false, 592, "#67@3", 1705.70, 0 },
|
|
{ 4, 1, 905.25, 1.0, false, 960, "#69@4", 1240.00, 0 },
|
|
{ 5, 1, 934.00, 1.2, true, 960, "#69@5", 1106.30, 0 },
|
|
{ 6, 2, 622.00, 1.0, false, 840, "#71@6", 1382.90, 1 },
|
|
{ 7, 3, 873.25, 1.0, false, 824, "#73@7", -272.10, 1 },
|
|
{ 8, 4, 873.25, 1.0, false, 800, "#75@8", 668.65, 0 },
|
|
{ 9, 5, 595.25, 1.0, false, 848, "#77@9", 468.00, 0 },
|
|
{ 10, 6, -17.70, 1.0, false, 616, "#79@10", 1645.00, 1 },
|
|
{ 11, 2, 75.10, 0.6, true, 840, "#71@11", 346.40, 0 },
|
|
{ 12, 6, 214.30, 1.0, false, 616, "#79@12", 767.80, 1 },
|
|
{ 13, 7, 400.75, 1.0, false, 600, "#81@13", -190.35, 0 },
|
|
}
|
|
|
|
local FUJI_X = 583.00 -- shape 65, base
|
|
local FUJI_Y = 280.00
|
|
local FUJI_CLEAR_X = 584.65 -- shape 84 inside huji_1p_l_mc, clear
|
|
local FUJI_H = 800
|
|
local FUJI_TRACK = "huji_1p_l_mc/#84@0"
|
|
|
|
function ResultBgLayers.new(player_num)
|
|
local self = setmetatable({}, ResultBgLayers)
|
|
self.player_num = player_num
|
|
self.two_player = (player_num == 3)
|
|
self.t0 = nil
|
|
self.fuji_t0 = nil
|
|
|
|
if self.two_player then
|
|
-- 2P: single baked plate (no clouds or Fuji) + header
|
|
self.t_bg = tex.get_texture("result/background/background_2p")
|
|
self.t_header = tex.get_texture("result/background/bg_header")
|
|
return self
|
|
end
|
|
|
|
self.t_sky = tex.get_texture("result/background/background_1p")
|
|
self.t_sky_clear = tex.get_texture("result/background/bg_sky_clear")
|
|
self.t_fuji = tex.get_texture("result/background/bg_fuji")
|
|
self.t_fuji_clear = tex.get_texture("result/background/bg_fuji_clear")
|
|
self.t_header = tex.get_texture("result/background/bg_header")
|
|
self.t_cloud, self.t_cloud_clear = {}, {}
|
|
for i = 0, 7 do
|
|
self.t_cloud[i] = tex.get_texture("result/background/bg_cloud_" .. i)
|
|
self.t_cloud_clear[i] = tex.get_texture("result/background/bg_cloud_clear_" .. i)
|
|
end
|
|
|
|
-- FAIL SOFT: `anim.safe` returns a NULL clip whose readers return nil, so a missing or
|
|
-- malformed table degrades to the LAYERS constants instead of raising.
|
|
self.clip = anim.safe("anim/result_bg")
|
|
self.fuji = anim.safe("anim/result_bg_fuji")
|
|
return self
|
|
end
|
|
|
|
-- arcade frame of the 720-frame loop, from the engine clock
|
|
function ResultBgLayers:frame()
|
|
local ms = tex.get_current_ms()
|
|
if not self.t0 then self.t0 = ms end
|
|
return math.fmod((ms - self.t0) * MS_TO_F, LOOP)
|
|
end
|
|
|
|
-- Mt Fuji runs on its own clock, zeroed when the clear state fires.
|
|
function ResultBgLayers:fuji_frame(clear)
|
|
if clear <= 0 then
|
|
self.fuji_t0 = nil -- re-arm: the next clear starts from rest again
|
|
return 0
|
|
end
|
|
local ms = tex.get_current_ms()
|
|
if not self.fuji_t0 then self.fuji_t0 = ms end
|
|
-- monotonic; the sampler parks it on frame 89 (FUJI_LOOP - 1) forever after.
|
|
return (ms - self.fuji_t0) * MS_TO_F
|
|
end
|
|
|
|
local function draw_cloud(id, w, x, y, scale, mirror, a)
|
|
if not id or a <= 0.004 then return end
|
|
local left = mirror and (x - w * scale) or x
|
|
tex.draw_texture(id, {
|
|
x = left, y = y, scale = scale, fade = a,
|
|
mirror = mirror and "horizontal" or nil,
|
|
})
|
|
end
|
|
|
|
-- `fade` multiplies everything (the screen-entry wipe); `clear` is 0..1, the success cross-fade.
|
|
function ResultBgLayers:draw(fade, clear)
|
|
fade = fade or 1.0
|
|
clear = clear or 0.0
|
|
if fade <= 0 then return end
|
|
|
|
if self.two_player then
|
|
if self.t_bg then tex.draw_texture(self.t_bg, { fade = fade }) end
|
|
if self.t_header then tex.draw_texture(self.t_header, { fade = fade }) end
|
|
return
|
|
end
|
|
|
|
local f = self:frame()
|
|
|
|
-- 1. sky. Base: shape 3 at x=0 and mirrored at x=960. Clear: shape 5 (gold, a different
|
|
-- atlas) the other way round -- x=960 upright, x=0 mirrored.
|
|
if self.t_sky then
|
|
tex.draw_texture(self.t_sky, { index = 0, fade = fade })
|
|
tex.draw_texture(self.t_sky, { index = 1, fade = fade, mirror = "horizontal" })
|
|
end
|
|
if clear > 0 and self.t_sky_clear then
|
|
tex.draw_texture(self.t_sky_clear, { index = 0, fade = fade * clear })
|
|
tex.draw_texture(self.t_sky_clear, { index = 1, fade = fade * clear, mirror = "horizontal" })
|
|
end
|
|
|
|
-- 2. Mt Fuji, pulsing only in the clear state (a 90-frame one-shot, held at the end).
|
|
if self.t_fuji and clear < 1.0 then
|
|
tex.draw_texture(self.t_fuji, { x = FUJI_X, y = FUJI_Y, fade = fade * (1.0 - clear) })
|
|
end
|
|
if clear > 0 and self.t_fuji_clear then
|
|
local ty, sy = self.fuji:at(FUJI_TRACK, self:fuji_frame(clear))
|
|
ty = ty or FUJI_Y
|
|
sy = sy or 1.0
|
|
tex.draw_texture(self.t_fuji_clear, {
|
|
x = FUJI_CLEAR_X, y = ty, y2 = FUJI_H * sy - FUJI_H, fade = fade * clear,
|
|
})
|
|
end
|
|
|
|
-- 3. the eleven cloud bars, driven off the one exported table for both states.
|
|
for i = 1, #LAYERS do
|
|
local L = LAYERS[i]
|
|
local idx, y, scale, mirror, w, track = L[2], L[3], L[4], L[5], L[6], L[7]
|
|
local tx, a = self.clip:at(track, f)
|
|
tx = tx or L[8]
|
|
a = a or L[9]
|
|
if clear < 1.0 then
|
|
draw_cloud(self.t_cloud[idx], w, tx, y, scale, mirror, fade * a * (1.0 - clear))
|
|
end
|
|
if clear > 0 then
|
|
draw_cloud(self.t_cloud_clear[idx], w, tx, y, scale, mirror, fade * a * clear)
|
|
end
|
|
end
|
|
|
|
-- 4. header band, drawn last (above the clouds).
|
|
if self.t_header then tex.draw_texture(self.t_header, { fade = fade }) end
|
|
end
|
|
|
|
return ResultBgLayers
|