Files
YataiDONNijiiro/Scripts/result/high_score_indicator.lua
2026-09-02 14:25:04 -04:00

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