dan select: 自己ベスト rows and all six clear stamps #65

Merged
GanYiZhong merged 1 commits from GanYiZhong/YataiDONNijiiro:feat/dan-select-best-stamps into main 2026-10-14 15:38:04 +02:00
14 changed files with 146 additions and 13 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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2 -1
View File
@@ -208,5 +208,6 @@
"difficulty_level": { "difficulty_level": {
"x": 538, "x": 538,
"y": 287 "y": 287
} },
"exam_best_pill": {}
} }
+12
View File
@@ -835,6 +835,18 @@
"height": 88, "height": 88,
"font_size": 42 "font_size": 42
}, },
"dan_exam_best_label": {
"font_size": 17,
"text": {
"en": "My Best",
"ja": "自己ベスト",
"zh": "个人最佳",
"ko": "개인 최고"
}
},
"dan_exam_best_value": {
"font_size": 26
},
"dan_back_label": { "dan_back_label": {
"x": 0, "x": 0,
"y": 89, "y": 89,
+101 -8
View File
@@ -201,6 +201,20 @@ local function course_view(box)
}) })
end end
-- The clear stamps are the cabinet's six (box/clear_mark and yellow_box/clear_mark, frames
-- 0..5 = red pass, full combo, all good, gold pass, full combo, all good), picked by the
-- saved record's rank (2..7). An engine without box.record_rank only gives clear_mark
-- (1 passed, 2 rank 7), which maps to frames 0 and 5.
local function stamp_frame(box)
local ok, rank = pcall(function() return box.record_rank end)
if ok and type(rank) == "number" then
return rank >= 2 and math.min(rank, 7) - 2 or nil
end
if box.clear_mark == 1 then return 0 end
if box.clear_mark == 2 then return 5 end
return nil
end
-- The course's clear stamp (yellow_box/clear_mark) sits over the dan plate and Don, as the -- The course's clear stamp (yellow_box/clear_mark) sits over the dan plate and Don, as the
-- cabinet's stamp_detail does (it is above don_1p). The parent draws it with the box, under -- cabinet's stamp_detail does (it is above don_1p). The parent draws it with the box, under
-- both, so it is held back there and drawn with the title, which the engine draws after -- both, so it is held back there and drawn with the title, which the engine draws after
@@ -245,9 +259,8 @@ if parent_open then
draw_level(box:song_level(i), i * (off.x or 0), i * (off.y or 0), f) draw_level(box:song_level(i), i * (off.x or 0), i * (off.y or 0), f)
end end
end end
if stamp and box.clear_mark > 0 then local sf = stamp and stamp_frame(box)
held_stamp = { id = stamp, frame = box.clear_mark - 1, fade = f } if sf then held_stamp = { id = stamp, frame = sf, fade = f } end
end
end end
function DanSelect:draw_title() function DanSelect:draw_title()
@@ -334,8 +347,9 @@ function DanSelect:draw_dan_closed(box)
local bx, fade, y = box:box_x(), box.fade.attribute, chip_drop(box) local bx, fade, y = box:box_x(), box.fade.attribute, chip_drop(box)
local id = tex.get_texture("dan_select/box/folder") local id = tex.get_texture("dan_select/box/folder")
if id then tex.draw_texture(id, { frame = box.dan_color, x = bx, y = y, fade = fade }) end if id then tex.draw_texture(id, { frame = box.dan_color, x = bx, y = y, fade = fade }) end
local mark = box.clear_mark > 0 and tex.get_texture("dan_select/box/clear_mark") local sf = stamp_frame(box)
if mark then tex.draw_texture(mark, { frame = box.clear_mark - 1, x = bx, y = y, fade = fade }) end local mark = sf and tex.get_texture("dan_select/box/clear_mark")
if mark then tex.draw_texture(mark, { frame = sf, x = bx, y = y, fade = fade }) end
draw_chip_name(box, bx, y, fade) draw_chip_name(box, bx, y, fade)
end end
@@ -349,10 +363,11 @@ end
-- The cursor's glow goes over the chip, its emblem and name (they grey under it, as on the -- The cursor's glow goes over the chip, its emblem and name (they grey under it, as on the
-- cabinet) but not over the clear stamp, which is drawn again on top of it. -- cabinet) but not over the clear stamp, which is drawn again on top of it.
local function draw_chip_front(box) local function draw_chip_front(box)
if box.is_back or box.clear_mark <= 0 then return end if box.is_back then return end
local mark = tex.get_texture("dan_select/box/clear_mark") local sf = stamp_frame(box)
local mark = sf and tex.get_texture("dan_select/box/clear_mark")
if mark then if mark then
tex.draw_texture(mark, { frame = box.clear_mark - 1, x = box:box_x(), y = chip_drop(box), tex.draw_texture(mark, { frame = sf, x = box:box_x(), y = chip_drop(box),
fade = box.fade.attribute }) fade = box.fade.attribute })
end end
end end
@@ -393,3 +408,81 @@ function DanSelect:draw_cursor(pos, now_ms, moved_ms)
if selected then draw_chip_front(selected) end if selected then draw_chip_front(selected) end
return true return true
end end
-- Each exam's 自己ベスト row, as on the cabinet: a white pill under the value with the label
-- on its left and the saved best on its right (box:exam_best / exam_song_best; an engine
-- without them, or a course never played, shows the label alone). The gauge box's pill is
-- part of exam_gauge_box; the right-hand boxes get yellow_box/exam_best_pill, one per song
-- segment. The boxes are walked as the parent's draw_exam_grid walks them.
local BEST_GAUGE = { x = 10, y = 116, w = 301 } -- the pill in exam_gauge_box, from the box
local BEST_RIGHT = { x = 11, y = 86, w = 317 } -- exam_best_pill, from the box (+ seg dx)
local BEST_H = 31
local BEST_LABEL_X, BEST_VALUE_R = 8, 14 -- label from the pill's left, value from its right
local best_texts = {}
local function best_text(key, str)
local cfg = tex.get_skin_config(key)
if not cfg or str == nil or str == "" then return nil end
local k = key .. "\0" .. str
if best_texts[k] == nil then
best_texts[k] = raw_create(str, cfg.font_size, { 20, 20, 20, 255 }, { 0, 0, 0, 255 },
false, 0, 1) or false
end
return best_texts[k] or nil
end
local function call(box, name, ...)
local ok, fn = pcall(function() return box[name] end)
if not ok or type(fn) ~= "function" then return nil end
local ok2, v = pcall(fn, box, ...)
return ok2 and v or nil
end
local function draw_best_row(box, value, etype, px, py, pw, f)
local label = best_text("dan_exam_best_label", tex.skin_text("dan_exam_best_label"))
if label then
label:draw({ x = px + BEST_LABEL_X, y = py + (BEST_H - label.height) / 2, fade = f })
end
if value then
local str = tostring(value) .. (etype == "gauge" and " %" or "")
local t = best_text("dan_exam_best_value", str)
if t then
t:draw({ x = px + pw - BEST_VALUE_R - t.width, y = py + (BEST_H - t.height) / 2, fade = f })
end
end
end
local parent_grid = DanSelect.draw_exam_grid
if parent_grid then
function DanSelect:draw_exam_grid(box, f)
parent_grid(self, box, f)
local pill = tex.get_texture("dan_select/yellow_box/exam_best_pill")
local gb = tex.get_skin_config("dan_exam_gauge_box") or { x = 436, y = 610 }
local sb = tex.get_skin_config("dan_exam_score_box") or { x = 769, y = 610, height = 132 }
local sg = tex.get_skin_config("dan_exam_seg") or { width = 327 }
local pitch = (sb.height and sb.height > 0) and sb.height or 132
local seg_dx = (sg.width and sg.width > 0) and sg.width or 327
local right_row, gauge_used = 0, false
for i = 0, box:exam_count() - 1 do
local etype = box:exam_type(i)
local left = (etype == "gauge" and not gauge_used)
if left then gauge_used = true end
if left then
draw_best_row(box, call(box, "exam_best", i), etype,
gb.x + BEST_GAUGE.x, gb.y + BEST_GAUGE.y, BEST_GAUGE.w, f)
elseif right_row < 3 then
local by = sb.y + pitch * right_row
local gothrough = box:exam_gothrough(i)
local segs = gothrough and 1 or math.min(box:song_count(), 3)
for k = 0, segs - 1 do
local px = sb.x + BEST_RIGHT.x + seg_dx * k
local py = by + BEST_RIGHT.y
if pill then tex.draw_texture(pill, { x = px, y = py, fade = f }) end
local v = gothrough and call(box, "exam_best", i) or call(box, "exam_song_best", i, k)
draw_best_row(box, v, etype, px, py, BEST_RIGHT.w, f)
end
right_row = right_row + 1
end
end
end
end