dan game: soul gauge drawn as on the cabinet #66

Merged
GanYiZhong merged 5 commits from GanYiZhong/YataiDONNijiiro:fix/dan-game-gauge into main 2026-10-14 15:38:18 +02:00
10 changed files with 175 additions and 18 deletions

No files matched your search

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+20
View File
@@ -100,5 +100,25 @@
"tamashii_fire": {
"x": 1698,
"y": 34
},
"track_red": {
"x": 738,
"y": 242
},
"1p_lamp": {
"x": 738,
"y": 242
},
"2p_lamp": {
"x": 738,
"y": 242
},
"lamp_clear": {
"x": 738,
"y": 242
},
"bar_fade_clear": {
"x": 733,
"y": 241
}
}
Binary file not shown.
+10 -6
View File
@@ -155,14 +155,18 @@ function DanBackground:draw_back()
self.chibi:draw()
end
-- dan soul gauge separator overlay (static, drawn at a hardcoded fade).
-- the soul gauge's strip the lane covers (the cabinet draws the gauge over the lane), then
-- its threshold marker, on top of everything
local LANE_TOP, GAUGE_BOTTOM = 276, 288
function DanBackground:draw_fore()
if not self._gauge_overlay then
self._gauge_overlay = tex.get_texture("game/gauge_dan/overlay") or 0
end
if self._gauge_overlay ~= 0 then
tex.draw_texture(self._gauge_overlay, { fade = 0.15 })
local redraw = _G.g_dan_gauge_redraw
if redraw then
tex.begin_scissor(0, LANE_TOP, tex.get_screen_width(), GAUGE_BOTTOM - LANE_TOP)
local ok, err = pcall(redraw)
tex.end_scissor()
if not ok then error(err, 0) end
end
self.dan_info:draw_marker()
end
-- dan condition plate, drawn in the gauge's own layer.
+40 -8
View File
@@ -1,5 +1,7 @@
-- 段位道場 exam-panel extras only Lua can draw: the soul-gauge pass-threshold
-- callout plate + captions, anchored on the gauge's own 50-cell grid.
-- callout plate + captions and the white threshold marker on the threshold cell, anchored
-- on the gauge's own 50-cell grid. It also hands the threshold (in cells) to the gauge script
-- (g_dan_gauge_border), which colours the track by it.
local M = {}
M.__index = M
@@ -11,23 +13,30 @@ local LABEL_SIZE = 30 -- arcade MS 明朝 36; ours reads
local WHITE = { 255, 255, 255, 255 }
local BLACK = { 0, 0, 0, 255 }
local PLATE_W, PLATE_H = 586, 60
local PLATE_W, PLATE_H = 586, 60 -- the plate's grey body; the art adds a 9 px margin
local PLATE_ART_PAD = 9 -- (the pill's top/left edge and the drop shadow)
local PLATE_GAP = 12 -- px between the plate's bottom and the bar top
local PLATE_OVERHANG = 24 -- px the plate extends PAST the threshold
local PILL_CX, PILL_CY = 150, 20 -- 魂ゲージ ink centre, plate-local
local VALUE_RIGHT = 532 -- 100%以上 is RIGHT-aligned to this
local VALUE_CY = 22 -- its ink centre y, plate-local
local VALUE_CY = 35 -- its ink centre y, plate-local
local NAME_SIZE = 26 -- arcade id 142
local VALUE_SIZE = 36 -- arcade id 141
-- the threshold marker (gauge_border_cell): its white inside (x 7, y 7 in the art) lands on
-- the threshold cell's left edge + 1, from y 238, as the cabinet's border_N
local MARKER_INNER_X, MARKER_INNER_Y = 7, 7
local MARKER_Y = 238
local CAP_FILL = { 255, 255, 255, 255 }
local CAP_OUTLINE = { 80, 60, 45, 255 } -- #503C2D
local VALUE_OUTLINE = { 0, 0, 0, 255 }
function M.new()
return setmetatable({ state = nil, label = nil, label_for = nil,
name_label = nil, plate = nil, plate_tried = false }, M)
name_label = nil, plate = nil, plate_tried = false,
marker = nil, marker_tried = false }, M)
end
function M:plate_id()
@@ -38,9 +47,12 @@ function M:plate_id()
return self.plate
end
-- Called from Background:handle_dan.
function M:handle(state)
self.state = state
function M:marker_id()
if not self.marker_tried then
self.marker_tried = true
self.marker = tex.get_texture("game/dan_info/gauge_border_cell")
end
return self.marker
end
local function gauge_exam(state)
@@ -51,6 +63,14 @@ local function gauge_exam(state)
return nil
end
-- Called from Background:handle_dan.
function M:handle(state)
self.state = state
local ex = gauge_exam(state)
_G.g_dan_gauge_border = (ex and ex.red and ex.red > 0)
and math.max(1, math.min(CELL_COUNT, math.floor(ex.red / 2))) or nil
end
-- Called from Background:draw_fore(), i.e. after the whole GAME_DAN HUD.
function M:draw()
local st = self.state
@@ -84,7 +104,7 @@ function M:draw()
local px = math.max(0, math.floor(x - PLATE_W + PLATE_OVERHANG + 0.5))
local py = GAUGE_Y - PLATE_GAP - PLATE_H
if pid then
tex.draw_texture(pid, { x = px, y = py })
tex.draw_texture(pid, { x = px - PLATE_ART_PAD, y = py - PLATE_ART_PAD })
end
-- fail soft: with no plate art the two captions still draw, at the same places
@@ -103,4 +123,16 @@ function M:draw()
end
end
-- The threshold marker, drawn over the lane's frame as on the cabinet (it reaches below the
-- gauge), so from Background:draw_fore() rather than with the plate in the gauge layer.
function M:draw_marker()
local ex = gauge_exam(self.state)
if not ex or not ex.red or ex.red <= 0 then return end
local n = math.max(1, math.min(CELL_COUNT, math.floor(ex.red / 2)))
local mid = self:marker_id()
if not mid then return end
local cell_left = GAUGE_X0 + CELL_PITCH * (n - 1)
tex.draw_texture(mid, { x = cell_left + 1 - MARKER_INNER_X, y = MARKER_Y - MARKER_INNER_Y })
end
return M
+88 -2
View File
@@ -4,6 +4,7 @@
-- length-1 (at length it left a dark gap cell before it)
-- * the clear mark sits gauge_clear_mark_offset cells (skin_config: 1, the cell edge) before
-- the exact clear fraction, not the parent's 87/60
-- The dan gauge is drawn as the cabinet's tamashii_gauge (see draw_dan below).
-- Everything else is the parent's file, loaded from its own copy.
-- the parent skin's copy of a script: the second match on package.path (the first is this file)
@@ -23,10 +24,21 @@ assert(load_parent("game/gauge"), "game/gauge: the parent skin has no gauge scri
local function clamp(v, lo, hi) return math.max(lo, math.min(hi, v)) end
local parent_new = Gauge.new
function Gauge.new(...)
local self = parent_new(...)
function Gauge.new(is_2p, dan, string_diff, opts)
local self = parent_new(is_2p, dan, string_diff, opts)
local mark = tex.get_skin_config("gauge_clear_mark_offset")
self.clear_mark_offset = mark and mark.x
if dan then
local screen = (opts and opts.screen) or "game"
local p = (opts and opts.player_str and (opts.player_str .. "_")) or (is_2p and "2p_" or "1p_")
local function t(name) return tex.get_texture(screen .. "/gauge_dan/" .. name) end
self.cab = {
track_red = t("track_red"),
lamp = t(p .. "lamp"),
lamp_clear = t("lamp_clear"),
fade_clear = t("bar_fade_clear"),
}
end
return self
end
@@ -101,3 +113,77 @@ function Gauge:render(y, mirror, idx, points, previous_points, max_points, clear
self:blit(self.tamashii_dark, nil, nil, y, nil, nil, nil, idx)
end
end
-- The dan gauge as the cabinet's tamashii_gauge draws it: the gold track (border), the red
-- track (track_red, gauge_bg) up to the threshold, the lit cells as one lamp stretched 21 px
-- per cell (lamp_clear, yellow, once the gauge reaches the threshold), the glow on a newly lit
-- cell, the cell separators at the cabinet's 103/255, then 魂: grey, white once cleared, and
-- in flames at full. The threshold comes from the dan exam (background dan_info sets
-- g_dan_gauge_border, in cells); without it, or without the cabinet pieces, the parent draws.
local CAB_CELL = 21
local CAB_TRACK = 20.88 -- gauge_bg: 160 px x 0.1305 per cell
local CAB_SEPARATOR = 103 / 255
local parent_draw_dan = Gauge.draw_dan
-- The cabinet draws the gauge over the lane; the engine draws the lane after it, so its top
-- edge (y 276, 184 px x 1.5) hides the cells' last rows. The gauge leaves a redraw for the
-- dan background's draw_fore (g_dan_gauge_redraw), which repeats it clipped to that strip.
local function draw_cab(self, border, points, previous_points, max_points, is_rainbow,
anim_active, anim_alpha, rainbow_fade, rainbow_frac, fire_frame)
local cab = self.cab
local function layer(t, x, x2, fade, frame, scale, center)
if t then
tex.draw_texture(t, { x = x, x2 = x2, fade = fade, frame = frame, scale = scale, center = center })
end
end
local length, solid, alpha, show_up = self:cells(points, previous_points, max_points, anim_active, anim_alpha)
local clear = length >= border
layer(self.border)
if border > 0 then layer(cab.track_red, nil, CAB_TRACK * border - 160) end
if solid > 0 then layer(clear and cab.lamp_clear or cab.lamp, nil, CAB_CELL * solid - 8) end
if is_rainbow and rainbow_fade >= 0 then
local frame_a = math.floor(rainbow_frac) % 8
local frame_b = (frame_a + 1) % 8
local f = rainbow_frac - math.floor(rainbow_frac)
layer(self.rainbow, nil, nil, rainbow_fade, frame_a)
layer(self.rainbow, nil, nil, rainbow_fade * f, frame_b)
end
if show_up and length > 0 then
layer(clear and cab.fade_clear or self.bar_fade, (length - 1) * CAB_CELL, nil, alpha)
end
layer(self.overlay, nil, nil, CAB_SEPARATOR)
if is_rainbow then
layer(self.fire, nil, nil, nil, fire_frame, 0.75, true)
layer(self.tamashii)
if fire_frame == 0 or fire_frame == 1 or fire_frame == 4 or fire_frame == 5 then
layer(self.tamashii_overlay, nil, nil, 0.5)
end
elseif clear then
layer(self.tamashii)
else
layer(self.tamashii_dark)
end
end
function Gauge:draw_dan(points, previous_points, max_points, is_rainbow,
anim_active, anim_alpha, rainbow_fade, rainbow_frac, fire_frame)
local cab = self.cab
local border = _G.g_dan_gauge_border
if not (cab and cab.track_red and cab.lamp and cab.lamp_clear and border) then
_G.g_dan_gauge_redraw = nil
return parent_draw_dan(self, points, previous_points, max_points, is_rainbow,
anim_active, anim_alpha, rainbow_fade, rainbow_frac, fire_frame)
end
draw_cab(self, border, points, previous_points, max_points, is_rainbow,
anim_active, anim_alpha, rainbow_fade, rainbow_frac, fire_frame)
_G.g_dan_gauge_redraw = function()
draw_cab(self, border, points, previous_points, max_points, is_rainbow,
anim_active, anim_alpha, rainbow_fade, rainbow_frac, fire_frame)
end
end