forked from Yonokid/YataiDONNijiiro
119 lines
6.9 KiB
Lua
119 lines
6.9 KiB
Lua
-- YataiDON-HSS: arcade best_score_mc bar (score/high_score, text-free background) +
|
|
-- live-rendered "New Top Score!" / "ベストスコア更新!" caption + small digit7 counters (pitch 14).
|
|
-- ROUND 39 (r39-topscore-banner-text): the caption used to be baked into score/high_score_en.png
|
|
-- / _ja.png (proof it was baked-per-locale: two whole 448x64 bitmaps differing ONLY in a ~200x40
|
|
-- px text patch). Re-render it live via OutlinedText instead, same as every other localized
|
|
-- caption on this screen (text.create_text + skin_config text{} table), so it no longer needs a
|
|
-- baked image per language and any future locale just needs a skin_config string, not a re-bake.
|
|
-- The background bar art (score/high_score.png) had its baked text patch removed by comparing
|
|
-- the two old bakes pixel-for-pixel: every column outside x 52..253 was byte-identical between
|
|
-- them (so is definitely real ribbon/pill art, not ink), and the interior was reconstructed with
|
|
-- that unaffected art as the only ground truth (scratchpad r39tsb/recon8.py — a coincidence-guarded
|
|
-- Laplace fill: naive diff-based "background = pixels equal in both bakes" is wrong here because
|
|
-- both languages' dark outline strokes cross the same pixel often enough to look "equal", which
|
|
-- silently poisoned every earlier attempt with a dark smear the shape of the old text).
|
|
HighScoreIndicator = {}
|
|
HighScoreIndicator.__index = HighScoreIndicator
|
|
|
|
-- ROUND 16 outline convention: `border` is stage px (a Lumen border field is ALREADY in 1080p
|
|
-- stage px), so create_text wants border / screen_scale, not the literal border value.
|
|
local function ol(border) return border / tex.get_screen_scale() end
|
|
|
|
-- MAPPING ROUND 5 (Graphics/result/MAPPING.md "2. ...ベストスコア更新!..."): ET169 box screen
|
|
-- (64.4,266.6)-(364.4,310.6) = 300x44, size 24, outline 4, align CENTRE; the arcade's own pen
|
|
-- formula (box_left + border + (box_w - text_w)/2) puts the ink CENTRE 4 px right/down of the
|
|
-- plain box centre -- i.e. at (box_left + border + box_w/2, box_top + border + box_h/2) =
|
|
-- (64.4+4+150, 266.6+4+22) = (218.4, 292.6), relative to the bar's own texture.json origin
|
|
-- (64,266) that is +154.4/+26.6. Centring an OutlinedText's (padded) width/height on that same
|
|
-- target point reproduces the same ink position (the padding is symmetric, so it cancels out of
|
|
-- the centring math) without needing the raw ink-only width the bake script used.
|
|
local TEXT_CX_REL = 154.4
|
|
local TEXT_CY_REL = 26.6
|
|
local BAR_BASE_X = { [0] = 64, [1] = 1020 } -- texture.json high_score frame 0 (1P) / 1 (2P)
|
|
local BAR_BASE_Y = 266
|
|
|
|
function HighScoreIndicator.new(score_diff, is_2p)
|
|
local self = setmetatable({}, HighScoreIndicator)
|
|
tex.load_animations("result")
|
|
-- arcade best_score_mc "start" (result/result.nulm MC 202, label start = f5): the bar drops
|
|
-- in, overshoots, bounces and settles at its texture.json position (no permanent upward
|
|
-- offset — Green's anim 18 left it 22 px up, under the gauge). ROUND 8: sampled, see below.
|
|
self.start_ms = nil
|
|
self.y_offset = 24
|
|
self.fade_val = 0
|
|
self.score_diff = score_diff
|
|
self.is_2p = is_2p
|
|
self.t_label = tex.load_texture("result/score/high_score")
|
|
self.t_num = tex.load_texture("result/score/high_score_num")
|
|
self.sc_margin = 14
|
|
-- White fill / dark-brown outline sampled directly off the old bakes' glyph pixels
|
|
-- (RGB ~(70,35,10), the same ink colour in both the EN and JA images).
|
|
self.text = text.create_text("result_topscore_text", { 255, 255, 255, 255 }, { 70, 35, 10, 255 },
|
|
false, ol(4.0), 0)
|
|
return self
|
|
end
|
|
|
|
-- ROUND 8 — `KEYS = {{0,24},{5,-22},{15,2},{25,0}}` and `fade = min(1, f/5)` are gone:
|
|
-- `Scripts/anim/best_score.lua` is exported straight out of `result/result.nulm` MC 202 by
|
|
-- scratchpad/anim_export.py (track `#196@0` = the bar). See ANIM_PIPELINE.md.
|
|
-- What the export changed — the hand-fitted 4-key table was not just imprecise, it was the
|
|
-- wrong SHAPE. The cabinet's bar, in frames after label `start`:
|
|
-- t 0..5 drop in, y +24 -> -21.95, alpha 0 -> 1 (five frames, eased)
|
|
-- t 5..9 rebound, y -21.95 -> 0
|
|
-- t 9..13 OVERSHOOT past the rest line, y 0 -> +7
|
|
-- t 13..18 settle, y +7 -> 0 (and it is there from t 18, not t 25)
|
|
-- Round 6's table lerped straight from -22 to +2 over t 5..15 and so drew NO overshoot at
|
|
-- all; it also faded in over 5 frames linearly where the cabinet's alpha is 0.36/0.64/0.84/
|
|
-- 0.96/1.0. The `sy` squash-stretch that rides the same track (1 -> 1.2 -> 1 -> 0.8 -> 1)
|
|
-- is NOT applied — see LUA_CAPABILITIES.md C-res-4.
|
|
-- 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 _ha_ok, hs_anim = pcall(require, "anim/sampler")
|
|
if not _ha_ok then hs_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 BS = hs_anim.safe("anim/best_score")
|
|
local BS_START = BS:label("start") or 5
|
|
local BS_END = math.max(BS_START, BS:last())
|
|
|
|
function HighScoreIndicator:update(current_ms)
|
|
if not self.start_ms then self.start_ms = current_ms end
|
|
local f = BS_START + (current_ms - self.start_ms) * 0.06
|
|
if f > BS_END then f = BS_END end
|
|
self.y_offset = BS:get("#196@0", f, "ty") or 0
|
|
self.fade_val = BS:get("#196@0", f, "a") or 1
|
|
end
|
|
|
|
function HighScoreIndicator:draw()
|
|
local p_idx = self.is_2p and 1 or 0
|
|
local y = self.y_offset
|
|
local fade = self.fade_val
|
|
if self.t_label then tex.draw_texture(self.t_label, { y = y, fade = fade, index = p_idx }) end
|
|
-- Live caption, replacing the old baked-per-locale score/high_score_en|ja.png text patch.
|
|
-- Centred on the arcade's own ink-centre target (see TEXT_CX_REL/TEXT_CY_REL derivation
|
|
-- above); ROUND 11 whole-pixel snap (ink-centring an odd-width label on an integer centre
|
|
-- lands it on a .5 and the GPU blends every texel 50/50 with its neighbour).
|
|
if self.text then
|
|
local cx = BAR_BASE_X[p_idx] + TEXT_CX_REL
|
|
local cy = BAR_BASE_Y + TEXT_CY_REL + y
|
|
self.text:draw({
|
|
x = math.floor(cx - self.text.width / 2 + 0.5),
|
|
y = math.floor(cy - self.text.height / 2 + 0.5),
|
|
fade = fade,
|
|
})
|
|
end
|
|
if self.t_num and self.score_diff > 0 then
|
|
local str = tostring(self.score_diff)
|
|
for i = 1, #str do
|
|
local d = tonumber(string.sub(str, #str - i + 1, #str - i + 1)) or 0
|
|
tex.draw_texture(self.t_num, { frame = d, x = -((i - 1) * self.sc_margin), y = y, fade = fade, index = p_idx })
|
|
end
|
|
end
|
|
end
|