|
|
|
@@ -0,0 +1,618 @@
|
|
|
|
|
-- Nijiiro's dan select base: PyTaikoGreen's dan_select.lua as of 4d325135, before upstream
|
|
|
|
|
-- (2026-10-15) dropped the exam grid, value centring, rank plate and total-notes options and
|
|
|
|
|
-- moved their numbers into its own script. Nijiiro still uses all of them, so it carries this
|
|
|
|
|
-- copy (loaded by dan_select.lua in place of the parent's). The options are fixed to Nijiiro's
|
|
|
|
|
-- values; keys upstream dropped have Nijiiro's own values from before the migration.
|
|
|
|
|
-- Generated by make_nj_dan_select_base.py; edit that, not this.
|
|
|
|
|
local LEGACY = {
|
|
|
|
|
dan_chip_name = { ["x"] = 0, ["y"] = 89, ["height"] = 88, ["font_size"] = 42 },
|
|
|
|
|
dan_exam_gauge_box = { ["x"] = 436, ["y"] = 610 },
|
|
|
|
|
dan_exam_info = { ["x"] = 40, ["y"] = 140, ["width"] = 984 },
|
|
|
|
|
dan_exam_label_pill = { ["x"] = 28, ["y"] = -1 },
|
|
|
|
|
dan_exam_score_box = { ["x"] = 769, ["y"] = 610, ["height"] = 132 },
|
|
|
|
|
dan_exam_seg = { ["x"] = 11, ["y"] = 47, ["width"] = 327 },
|
|
|
|
|
dan_exam_value = { ["x"] = 185, ["y"] = 40 },
|
|
|
|
|
dan_exam_value_center = { ["x"] = 633.0, ["width"] = -6.0 }, -- PyTaikoGreen's x 1.5
|
|
|
|
|
dan_hori_name = { ["x"] = 257, ["y"] = 380, ["width"] = 240 },
|
|
|
|
|
dan_level_counter_margin = { ["x"] = 34 },
|
|
|
|
|
dan_select_exam_border_text = { ["x"] = 309, ["y"] = 55, ["font_size"] = 25, ["outline"] = 2.6 },
|
|
|
|
|
dan_select_gauge_border_text = { ["x"] = 291, ["y"] = 67, ["font_size"] = 32, ["outline"] = 3.1 },
|
|
|
|
|
dan_select_total_notes = { ["x"] = 0 },
|
|
|
|
|
dan_subtitle = { ["x"] = 632, ["y"] = 262, ["height"] = 40, ["font_size"] = 22 },
|
|
|
|
|
dan_title = { ["x"] = 632, ["y"] = 262, ["height"] = 60, ["font_size"] = 36, ["outline"] = 2.6 },
|
|
|
|
|
dan_yellow_box_offset = { ["x"] = 0, ["y"] = 110 },
|
|
|
|
|
exam_counter_margin = { ["x"] = 44 },
|
|
|
|
|
exam_gauge_offset = { ["x"] = 0 },
|
|
|
|
|
song_box_name = { ["x"] = 0, ["y"] = 0, ["height"] = 120, ["font_size"] = 42 },
|
|
|
|
|
total_notes_counter_margin = { ["x"] = 44 },
|
|
|
|
|
yellow_box_bottom = { ["y"] = 974 },
|
|
|
|
|
}
|
|
|
|
|
local RENAMED = { dan_title = "dan_select_title", dan_subtitle = "dan_select_subtitle", dan_chip_name = "dan_select_chip_name", song_box_name = "song_select_box_name", dan_exam_info = "dan_select_exam_info" }
|
|
|
|
|
local OPTS = { dan_confirm_yes_left = false, dan_exam_grid = true, dan_exam_value_center = true, dan_select_rank_plate = true }
|
|
|
|
|
DanSelect = {}
|
|
|
|
|
DanSelect.__index = DanSelect
|
|
|
|
|
|
|
|
|
|
local _tex = {}
|
|
|
|
|
local function T(subset, name)
|
|
|
|
|
local key = subset .. "/" .. name
|
|
|
|
|
local id = _tex[key]
|
|
|
|
|
if id == nil then
|
|
|
|
|
id = tex.get_texture("dan_select/" .. subset .. "/" .. name)
|
|
|
|
|
_tex[key] = id
|
|
|
|
|
end
|
|
|
|
|
return id
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function cfg(name)
|
|
|
|
|
return (RENAMED[name] and tex.get_skin_config(RENAMED[name])) or tex.get_skin_config(name) or LEGACY[name]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function opt(name)
|
|
|
|
|
return OPTS[name]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- Texture draw-offset at index 0 (TextureObject::x[0] / y[0] / x2[0]).
|
|
|
|
|
local function tex_x0(subset, name)
|
|
|
|
|
local info = tex.get_texture_info(subset, name)
|
|
|
|
|
if info and info.x and info.x[1] then return info.x[1] end
|
|
|
|
|
return 0
|
|
|
|
|
end
|
|
|
|
|
local function tex_y0(subset, name)
|
|
|
|
|
local info = tex.get_texture_info(subset, name)
|
|
|
|
|
if info and info.y and info.y[1] then return info.y[1] end
|
|
|
|
|
return 0
|
|
|
|
|
end
|
|
|
|
|
local function tex_x2(subset, name)
|
|
|
|
|
local info = tex.get_texture_info(subset, name)
|
|
|
|
|
if info and info.x2 and info.x2[1] then return info.x2[1] end
|
|
|
|
|
return 0
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- exam.type -> icon texture name (exam_icon_id in box_dan.cpp). "renda" resolves to
|
|
|
|
|
-- exam_roll, falling back to exam_drumroll in older skins. Returns the resolved name.
|
|
|
|
|
local EXAM_ICON = {
|
|
|
|
|
gauge = "exam_gauge", combo = "exam_combo",
|
|
|
|
|
hit = "exam_hit", judgebad = "exam_judgebad",
|
|
|
|
|
judgegood = "exam_judgegood", judgeperfect = "exam_judgeperfect",
|
|
|
|
|
score = "exam_score", renda = "exam_roll",
|
|
|
|
|
}
|
|
|
|
|
local function exam_icon_name(type)
|
|
|
|
|
local name = EXAM_ICON[type]
|
|
|
|
|
if not name then return nil end
|
|
|
|
|
if T("yellow_box", name) then return name end
|
|
|
|
|
if T("yellow_box", "exam_drumroll") then return "exam_drumroll" end
|
|
|
|
|
return nil
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local yb_box = { path = nil, state = "closed" }
|
|
|
|
|
|
|
|
|
|
function DanSelect.new()
|
|
|
|
|
local self = setmetatable({ now = 0 }, DanSelect)
|
|
|
|
|
yb_box.path, yb_box.state = nil, "closed"
|
|
|
|
|
return self
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local YB_DIFF_ANIMS = { 13, 14, 15, 16, 17, 18 }
|
|
|
|
|
|
|
|
|
|
local function draw_dan_yellow_box(is_diff, fade)
|
|
|
|
|
local right_x = tex.get_animation(is_diff and 14 or 10).attribute
|
|
|
|
|
local left_x = tex.get_animation(is_diff and 13 or 9).attribute
|
|
|
|
|
local center_width = tex.get_animation(is_diff and 15 or 11).attribute
|
|
|
|
|
local top_y = tex.get_animation(16).attribute
|
|
|
|
|
local center_height = tex.get_animation(17).attribute
|
|
|
|
|
local bottom_y = (cfg("yellow_box_bottom") or { y = 0 }).y
|
|
|
|
|
local info = tex.get_texture_info("yellow_box", "yellow_box_bottom_right")
|
|
|
|
|
local eh = (info and info.height) or 0
|
|
|
|
|
|
|
|
|
|
tex.draw_texture(T("yellow_box", "yellow_box_bottom_right"), { x = right_x, y = bottom_y, fade = fade })
|
|
|
|
|
tex.draw_texture(T("yellow_box", "yellow_box_bottom_left"), { x = left_x, y = bottom_y, fade = fade })
|
|
|
|
|
tex.draw_texture(T("yellow_box", "yellow_box_top_right"), { x = right_x, y = top_y, fade = fade })
|
|
|
|
|
tex.draw_texture(T("yellow_box", "yellow_box_top_left"), { x = left_x, y = top_y, fade = fade })
|
|
|
|
|
tex.draw_texture(T("yellow_box", "yellow_box_bottom"), { x = left_x + eh, y = bottom_y, x2 = center_width, fade = fade })
|
|
|
|
|
tex.draw_texture(T("yellow_box", "yellow_box_right"), { x = right_x, y = top_y + eh, y2 = center_height, fade = fade })
|
|
|
|
|
tex.draw_texture(T("yellow_box", "yellow_box_left"), { x = left_x, y = top_y + eh, y2 = center_height, fade = fade })
|
|
|
|
|
tex.draw_texture(T("yellow_box", "yellow_box_top"), { x = left_x + eh, y = top_y, x2 = center_width, fade = fade })
|
|
|
|
|
tex.draw_texture(T("yellow_box", "yellow_box_center"), { x = left_x + eh, y = top_y + eh, x2 = center_width, y2 = center_height, fade = fade })
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- Track the open box and start the diff-select expansion on the closed -> diff_select
|
|
|
|
|
-- transition (the dan box never passes through the "open" draw_state).
|
|
|
|
|
local function track_dan_yellow_box(box)
|
|
|
|
|
local state = box:draw_state()
|
|
|
|
|
local path = box:path()
|
|
|
|
|
if state == "closed" then
|
|
|
|
|
if yb_box.path == path then
|
|
|
|
|
yb_box.path, yb_box.state = nil, "closed"
|
|
|
|
|
end
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
if yb_box.path ~= path then
|
|
|
|
|
yb_box.path, yb_box.state = path, state
|
|
|
|
|
for _, id in ipairs(YB_DIFF_ANIMS) do tex.get_animation(id):reset() end
|
|
|
|
|
for _, id in ipairs(YB_DIFF_ANIMS) do tex.get_animation(id):start() end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function update_dan_yellow_box(ms)
|
|
|
|
|
for _, id in ipairs(YB_DIFF_ANIMS) do tex.get_animation(id):update(ms) end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function DanSelect:update(ms)
|
|
|
|
|
self.now = ms
|
|
|
|
|
update_dan_yellow_box(ms)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function draw_tex(subset, name, params)
|
|
|
|
|
local id = T(subset, name)
|
|
|
|
|
if id then tex.draw_texture(id, params) end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function DanSelect:draw_back()
|
|
|
|
|
draw_tex("global", "bg", {})
|
|
|
|
|
draw_tex("global", "bg_header", {})
|
|
|
|
|
draw_tex("global", "bg_footer", {})
|
|
|
|
|
draw_tex("global", "footer", {})
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function DanSelect:draw_cursor(pos, now, since_move)
|
|
|
|
|
if since_move < 0 or since_move >= 967 then return true end
|
|
|
|
|
local p = since_move / 967
|
|
|
|
|
local drift = 30 * p
|
|
|
|
|
local fade = 0.984 * (1 - p)
|
|
|
|
|
draw_tex("global", "arrow", { index = 0, x = -drift, fade = fade })
|
|
|
|
|
draw_tex("global", "arrow", { index = 1, x = drift, fade = fade, mirror = "horizontal" })
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function DanSelect:draw_title()
|
|
|
|
|
draw_tex("global", "dan_select", {})
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local CONFIRM_OPTION, CONFIRM_YES = 0, 1
|
|
|
|
|
|
|
|
|
|
-- fade: confirm_fade attribute; index: 0 option / 1 yes / 2 no
|
|
|
|
|
function DanSelect:draw_confirm(f, index)
|
|
|
|
|
if f <= 0 then return end
|
|
|
|
|
tex.draw_rect(0, 0, tex.get_screen_width(), tex.get_screen_height(), 0, 0, 0, math.min(0.5, f) * 255)
|
|
|
|
|
draw_tex("confirm_box", "bg", { fade = f })
|
|
|
|
|
draw_tex("confirm_box", "confirmation_text", { fade = f })
|
|
|
|
|
for i = 0, 1 do
|
|
|
|
|
draw_tex("confirm_box", "selection_box", { fade = f, index = i })
|
|
|
|
|
end
|
|
|
|
|
if index ~= CONFIRM_OPTION then
|
|
|
|
|
-- selection_box slots are screen-left / screen-right; yes sits right of no unless swapped
|
|
|
|
|
local yes_info = tex.get_texture_info("confirm_box", "yes")
|
|
|
|
|
local no_info = tex.get_texture_info("confirm_box", "no")
|
|
|
|
|
local yes_right = yes_info and no_info and yes_info.x[1] > no_info.x[1]
|
|
|
|
|
local side = ((index == CONFIRM_YES) == (yes_right and true or false)) and 1 or 0
|
|
|
|
|
draw_tex("confirm_box", "selection_box_highlight", { fade = f, index = side })
|
|
|
|
|
draw_tex("confirm_box", "selection_box_outline", { fade = f, index = side })
|
|
|
|
|
end
|
|
|
|
|
local swap_dx = 0
|
|
|
|
|
if opt("dan_confirm_yes_left") then
|
|
|
|
|
local info = tex.get_texture_info("confirm_box", "selection_box")
|
|
|
|
|
if info and info.x and #info.x >= 2 then swap_dx = info.x[2] - info.x[1] end
|
|
|
|
|
end
|
|
|
|
|
draw_tex("confirm_box", "yes", { x = -swap_dx, fade = f })
|
|
|
|
|
draw_tex("confirm_box", "no", { x = swap_dx, fade = f })
|
|
|
|
|
if T("confirm_box", "option") then
|
|
|
|
|
if index == CONFIRM_OPTION then draw_tex("confirm_box", "option_highlight", { fade = f }) end
|
|
|
|
|
draw_tex("confirm_box", "option", { fade = f })
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- Draw a texture at an absolute position, ignoring its own x[0]/y[0] anchor (draw_abs).
|
|
|
|
|
local function draw_abs(subset, name, X, Y, fade, frame)
|
|
|
|
|
local id = T(subset, name)
|
|
|
|
|
if not id then return end
|
|
|
|
|
tex.draw_texture(id, {
|
|
|
|
|
frame = frame or 0,
|
|
|
|
|
x = X - tex_x0(subset, name),
|
|
|
|
|
y = Y - tex_y0(subset, name),
|
|
|
|
|
fade = fade,
|
|
|
|
|
})
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local BACK_CX, BACK_BOTTOM, BACK_HALF0, BACK_HALF1 = 642, 539, 44.4, 191
|
|
|
|
|
local BACK_TOP0, BACK_TOP1, BACK_GROW_MS = 338 - 228, 338 - 231, 150
|
|
|
|
|
|
|
|
|
|
function DanSelect:draw_back_box(box)
|
|
|
|
|
local bx = box:box_x()
|
|
|
|
|
local fade = box.fade.attribute
|
|
|
|
|
tex.draw_texture(T("back", "folder"), { x = bx, fade = fade })
|
|
|
|
|
if box:draw_state() == "closed" then
|
|
|
|
|
self.back_open_at = nil
|
|
|
|
|
tex.draw_texture(T("back", "chip"), { x = bx, fade = fade })
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
self.back_open_at = self.back_open_at or self.now
|
|
|
|
|
local ms = self.now - self.back_open_at
|
|
|
|
|
local u = math.min(math.max(ms / BACK_GROW_MS, 0), 1)
|
|
|
|
|
local half = BACK_HALF0 + (BACK_HALF1 - BACK_HALF0) * u
|
|
|
|
|
local top = BACK_TOP0 + (BACK_TOP1 - BACK_TOP0) * u
|
|
|
|
|
local l, r = BACK_CX - half, BACK_CX + half - 32
|
|
|
|
|
local w, h = r - l - 32, BACK_BOTTOM - top - 32
|
|
|
|
|
local function piece(name, x, y, x2, y2)
|
|
|
|
|
tex.draw_texture(T("back", name), { x = x, y = y, x2 = x2 and x2 - 32 or 0, y2 = y2 and y2 - 32 or 0 })
|
|
|
|
|
end
|
|
|
|
|
piece("board_tl", l, top); piece("board_t", l + 32, top, w)
|
|
|
|
|
piece("board_tr", r, top); piece("board_r", r, top + 32, nil, h)
|
|
|
|
|
piece("board_br", r, BACK_BOTTOM); piece("board_b", l + 32, BACK_BOTTOM, w)
|
|
|
|
|
piece("board_bl", l, BACK_BOTTOM); piece("board_l", l, top + 32, nil, h)
|
|
|
|
|
piece("board_c", l + 32, top + 32, w, h)
|
|
|
|
|
local f = math.min(math.max((ms - 33) / 117, 0), 1)
|
|
|
|
|
for _, name in ipairs({ "graphic", "text", "pill" }) do tex.draw_texture(T("back", name), { fade = f }) end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function DanSelect:draw_box(box)
|
|
|
|
|
if box.is_back then return self:draw_back_box(box) end
|
|
|
|
|
track_dan_yellow_box(box)
|
|
|
|
|
local state = box:draw_state()
|
|
|
|
|
if state == "closed" then
|
|
|
|
|
self:draw_dan_closed(box)
|
|
|
|
|
else
|
|
|
|
|
self:draw_dan_open(box)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
|
-- draw_chip (closed): folder board + vertical chip name.
|
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
|
function DanSelect:draw_dan_closed(box)
|
|
|
|
|
local bx = box:box_x()
|
|
|
|
|
local fade = box.fade.attribute
|
|
|
|
|
|
|
|
|
|
tex.draw_texture(T("box", "folder"), { frame = box.dan_color, x = bx, fade = fade })
|
|
|
|
|
|
|
|
|
|
if box.clear_mark > 0 and T("box", "clear_mark") then
|
|
|
|
|
tex.draw_texture(T("box", "clear_mark"), { frame = box.clear_mark - 1, x = bx, fade = fade })
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local name = box:chip_name()
|
|
|
|
|
if name then
|
|
|
|
|
local nb = cfg("dan_chip_name") or cfg("song_box_name") or { x = 0, y = 0, height = 0 }
|
|
|
|
|
name:draw({
|
|
|
|
|
x = bx + nb.x - name.width / 2,
|
|
|
|
|
y = nb.y,
|
|
|
|
|
y2 = math.min(name.height, nb.height or name.height) - name.height,
|
|
|
|
|
fade = fade,
|
|
|
|
|
})
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
|
-- draw_open: chip + song ribbon + total notes + rank + exam box.
|
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
|
function DanSelect:draw_dan_open(box)
|
|
|
|
|
self:draw_dan_closed(box)
|
|
|
|
|
local f = box.open_fade.attribute
|
|
|
|
|
|
|
|
|
|
-- The yellow box board the course details sit on (opaque; its growth is driven by
|
|
|
|
|
-- the diff-select animations, matching the song-select wheel).
|
|
|
|
|
draw_dan_yellow_box(true, 1.0)
|
|
|
|
|
|
|
|
|
|
local off = cfg("dan_yellow_box_offset") or { x = 0, y = 0 }
|
|
|
|
|
local offset_x = off.x or 0
|
|
|
|
|
local offset_y = off.y or 0
|
|
|
|
|
|
|
|
|
|
local n = box:song_count()
|
|
|
|
|
for i = 0, n - 1 do
|
|
|
|
|
local x = i * offset_x
|
|
|
|
|
local y = i * offset_y
|
|
|
|
|
|
|
|
|
|
tex.draw_texture(T("yellow_box", "genre_banner"), { frame = box:song_genre(i), x = x, y = y, fade = f })
|
|
|
|
|
|
|
|
|
|
local song_label = T("yellow_box", "song_label")
|
|
|
|
|
if song_label then
|
|
|
|
|
tex.draw_texture(song_label, { frame = math.min(i, 2), x = x, y = y, fade = f })
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
tex.draw_texture(T("yellow_box", "difficulty"), { frame = box:song_difficulty(i), x = x, y = y, fade = f })
|
|
|
|
|
if box:song_difficulty(i) == 4 and T("yellow_box", "difficulty_ura") then
|
|
|
|
|
tex.draw_texture(T("yellow_box", "difficulty_ura"), { x = x, y = y, fade = f })
|
|
|
|
|
end
|
|
|
|
|
tex.draw_texture(T("yellow_box", "difficulty_x"), { x = x, y = y, fade = f })
|
|
|
|
|
tex.draw_texture(T("yellow_box", "difficulty_star"), { x = x, y = y, fade = f })
|
|
|
|
|
|
|
|
|
|
-- Level counter
|
|
|
|
|
local lvl = tostring(box:song_level(i))
|
|
|
|
|
local margin = (cfg("dan_level_counter_margin") or { x = 0 }).x
|
|
|
|
|
local total_w = #lvl * margin
|
|
|
|
|
for j = 1, #lvl do
|
|
|
|
|
local digit = string.byte(lvl, j) - 48
|
|
|
|
|
tex.draw_texture(T("yellow_box", "difficulty_num"), {
|
|
|
|
|
frame = digit, x = x - total_w / 2 + (j - 1) * margin, y = y, fade = f })
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- Song title + subtitle
|
|
|
|
|
local title_text = box:song_title_text(i)
|
|
|
|
|
local sub_text = box:song_subtitle_text(i)
|
|
|
|
|
local td = cfg("dan_title") or { x = 0, y = 0, height = 0 }
|
|
|
|
|
local sd = cfg("dan_subtitle") or { x = 0, y = 0, height = 0 }
|
|
|
|
|
if box:song_masked(i) and T("yellow_box", "hidden_title") then
|
|
|
|
|
tex.draw_texture(T("yellow_box", "hidden_title"), { x = x, y = y, fade = f })
|
|
|
|
|
elseif title_text then
|
|
|
|
|
title_text:draw({
|
|
|
|
|
x = td.x + x, y = td.y + y,
|
|
|
|
|
y2 = math.min(title_text.height, td.height or title_text.height) - title_text.height,
|
|
|
|
|
fade = f })
|
|
|
|
|
end
|
|
|
|
|
if sub_text then
|
|
|
|
|
sub_text:draw({
|
|
|
|
|
x = sd.x + x,
|
|
|
|
|
y = sd.y + y - math.min(sub_text.height, sd.height or sub_text.height),
|
|
|
|
|
y2 = math.min(sub_text.height, sd.height or sub_text.height) - sub_text.height,
|
|
|
|
|
fade = f })
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if box.clear_mark > 0 and T("yellow_box", "clear_mark") then
|
|
|
|
|
tex.draw_texture(T("yellow_box", "clear_mark"), { frame = box.clear_mark - 1, fade = f })
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- Total notes
|
|
|
|
|
tex.draw_texture(T("yellow_box", "total_notes_bg"), { fade = f })
|
|
|
|
|
local tn_slot = cfg("dan_select_total_notes")
|
|
|
|
|
if not tn_slot or tn_slot.x >= 1.0 then
|
|
|
|
|
tex.draw_texture(T("yellow_box", "total_notes"), { fade = f })
|
|
|
|
|
local tn = tostring(box.total_notes)
|
|
|
|
|
local tn_margin = (cfg("total_notes_counter_margin") or { x = 0 }).x
|
|
|
|
|
for i = 1, #tn do
|
|
|
|
|
local digit = string.byte(tn, i) - 48
|
|
|
|
|
-- Lumen centres the counter on its 4-digit width
|
|
|
|
|
tex.draw_texture(T("yellow_box", "total_notes_counter"), { frame = digit, x = (i - 1 + (4 - #tn) / 2) * tn_margin, fade = f })
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- Rank plate / frame
|
|
|
|
|
if box.dan_rank >= 0 and opt("dan_select_rank_plate") then
|
|
|
|
|
tex.draw_texture(T("yellow_box", "rank_plate"), { frame = box.dan_rank, fade = f })
|
|
|
|
|
else
|
|
|
|
|
tex.draw_texture(T("yellow_box", "frame"), { frame = box.dan_color, fade = f })
|
|
|
|
|
local hori = box:hori_name()
|
|
|
|
|
if hori then
|
|
|
|
|
local hn = cfg("dan_hori_name") or { x = 0, y = 0, width = 0 }
|
|
|
|
|
hori:draw({
|
|
|
|
|
x = hn.x - hori.width / 2,
|
|
|
|
|
y = hn.y,
|
|
|
|
|
x2 = math.min(hori.width, hn.width or hori.width) - hori.width,
|
|
|
|
|
fade = f })
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
self:draw_exam_box(box, f)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
|
-- Exam board (the default "condition rows" layout).
|
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
|
function DanSelect:draw_exam_box(box, f)
|
|
|
|
|
if opt("dan_exam_grid") then
|
|
|
|
|
self:draw_exam_grid(box, f)
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local offset_y = (cfg("dan_exam_info") or { y = 0 }).y
|
|
|
|
|
local margin = (cfg("exam_counter_margin") or { x = 0 }).x
|
|
|
|
|
|
|
|
|
|
-- Four or more conditions do not fit the board: drop the soul-gauge row.
|
|
|
|
|
local exam_count = box:exam_count()
|
|
|
|
|
local shown = {}
|
|
|
|
|
for i = 0, exam_count - 1 do
|
|
|
|
|
if not (exam_count >= 4 and box:exam_type(i) == "gauge") then
|
|
|
|
|
shown[#shown + 1] = i
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
if #shown > 3 then
|
|
|
|
|
while #shown > 3 do shown[#shown] = nil end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local L = ({ { dt = 38, row0 = 90, pitch = 0 }, { dt = 18, row0 = 37, pitch = 106 } })[#shown]
|
|
|
|
|
or { dt = 0, row0 = 0, pitch = offset_y }
|
|
|
|
|
local dt = L.dt
|
|
|
|
|
local function part(name, p)
|
|
|
|
|
p.fade = f
|
|
|
|
|
tex.draw_texture(T("yellow_box", name), p)
|
|
|
|
|
end
|
|
|
|
|
part("exam_box_bottom_right", { y = -dt })
|
|
|
|
|
part("exam_box_bottom_left", { y = -dt })
|
|
|
|
|
part("exam_box_top_right", { y = dt })
|
|
|
|
|
part("exam_box_top_left", { y = dt })
|
|
|
|
|
part("exam_box_bottom", { y = -dt })
|
|
|
|
|
part("exam_box_right", { y = dt, y2 = -2 * dt })
|
|
|
|
|
part("exam_box_left", { y = dt, y2 = -2 * dt })
|
|
|
|
|
part("exam_box_top", { y = dt })
|
|
|
|
|
part("exam_box_center", { y = dt, y2 = -2 * dt })
|
|
|
|
|
part("exam_header", { y = dt })
|
|
|
|
|
|
|
|
|
|
for idx = 1, #shown do
|
|
|
|
|
local i = shown[idx]
|
|
|
|
|
local y = L.row0 + (idx - 1) * L.pitch
|
|
|
|
|
local etype = box:exam_type(i)
|
|
|
|
|
local range = box:exam_range(i)
|
|
|
|
|
|
|
|
|
|
tex.draw_texture(T("yellow_box", "judge_box"), { y = y, fade = f })
|
|
|
|
|
|
|
|
|
|
local icon_name = exam_icon_name(etype)
|
|
|
|
|
if icon_name then tex.draw_texture(T("yellow_box", icon_name), { y = y, fade = f }) end
|
|
|
|
|
|
|
|
|
|
if opt("dan_exam_value_center") then
|
|
|
|
|
-- Centre the whole value (digits, %, suffix) on dan_exam_value_center.x.
|
|
|
|
|
local vc = cfg("dan_exam_value_center") or { x = 0, width = 0 }
|
|
|
|
|
local digits = tostring(box:exam_red(i))
|
|
|
|
|
local pct = (etype == "gauge") and T("yellow_box", "exam_percent") or nil
|
|
|
|
|
local suf = (range == "more") and T("yellow_box", "exam_more")
|
|
|
|
|
or (range == "less") and T("yellow_box", "exam_less") or nil
|
|
|
|
|
|
|
|
|
|
local digits_w = (#digits - 1) * margin + tex_x2("yellow_box", "judge_num")
|
|
|
|
|
local pct_w = pct and tex_x2("yellow_box", "exam_percent") or 0
|
|
|
|
|
local suf_w = suf and tex_x2("yellow_box", (range == "more") and "exam_more" or "exam_less") or 0
|
|
|
|
|
local gap = vc.width or 0
|
|
|
|
|
local parts = 1 + (pct and 1 or 0) + (suf and 1 or 0)
|
|
|
|
|
local total = digits_w + pct_w + suf_w + gap * (parts - 1)
|
|
|
|
|
local cx = (vc.x or 0) - total / 2
|
|
|
|
|
|
|
|
|
|
for j = 1, #digits do
|
|
|
|
|
local digit = string.byte(digits, j) - 48
|
|
|
|
|
tex.draw_texture(T("yellow_box", "judge_num"),
|
|
|
|
|
{ frame = digit, x = cx + (j - 1) * margin - tex_x0("yellow_box", "judge_num"), y = y, fade = f })
|
|
|
|
|
end
|
|
|
|
|
cx = cx + digits_w + gap
|
|
|
|
|
if pct then
|
|
|
|
|
tex.draw_texture(pct, { x = cx - tex_x0("yellow_box", "exam_percent"), y = y, fade = f })
|
|
|
|
|
cx = cx + pct_w + gap
|
|
|
|
|
end
|
|
|
|
|
if suf then
|
|
|
|
|
local suf_name = (range == "more") and "exam_more" or "exam_less"
|
|
|
|
|
tex.draw_texture(suf, { x = cx - tex_x0("yellow_box", suf_name), y = y, fade = f })
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
local x_offset = 0
|
|
|
|
|
if etype == "gauge" then
|
|
|
|
|
tex.draw_texture(T("yellow_box", "exam_percent"), { y = y, fade = f })
|
|
|
|
|
x_offset = (cfg("exam_gauge_offset") or { x = 0 }).x
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local counter = tostring(box:exam_red(i))
|
|
|
|
|
for j = 1, #counter do
|
|
|
|
|
local digit = string.byte(counter, j) - 48
|
|
|
|
|
local x = x_offset - (#counter - j + 1) * margin
|
|
|
|
|
tex.draw_texture(T("yellow_box", "judge_num"), { frame = digit, x = x, y = y, fade = f })
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if range == "more" then
|
|
|
|
|
tex.draw_texture(T("yellow_box", "exam_more"), { x = -x_offset * 1.7, y = y, fade = f })
|
|
|
|
|
elseif range == "less" then
|
|
|
|
|
tex.draw_texture(T("yellow_box", "exam_less"), { x = -x_offset * 1.7, y = y, fade = f })
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
|
-- Exam board grid mode (dan_exam_grid option): a two-column gauge/score layout.
|
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
|
local function caption(self, str, font_size, outline)
|
|
|
|
|
self._captions = self._captions or {}
|
|
|
|
|
local key = str .. "\0" .. tostring(font_size) .. "\0" .. tostring(outline)
|
|
|
|
|
local cap = self._captions[key]
|
|
|
|
|
if cap == nil then
|
|
|
|
|
cap = text.create_raw_text(str, font_size, { 255, 255, 255, 255 }, { 0, 0, 0, 255 }, false, outline)
|
|
|
|
|
self._captions[key] = cap
|
|
|
|
|
end
|
|
|
|
|
return cap
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function draw_value_text(self, cap_str, bt, right_x, top_y, f)
|
|
|
|
|
if not bt then return false end
|
|
|
|
|
local ol = (bt.outline and bt.outline >= 0) and bt.outline or (3.0 / tex.get_screen_scale())
|
|
|
|
|
local size = (bt.font_size and bt.font_size > 0) and bt.font_size or 28
|
|
|
|
|
local cap = caption(self, cap_str, size, ol)
|
|
|
|
|
if not cap then return false end
|
|
|
|
|
local pad = math.floor(ol * tex.get_screen_scale()) + 2
|
|
|
|
|
cap:draw({ x = right_x - cap.width + pad, y = top_y - pad, fade = f })
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- draw_value (grid mode): centred digits + optional percent/suffix, absolute anchors.
|
|
|
|
|
local function draw_value(self, red, etype, range, centre_x, top_y, f)
|
|
|
|
|
local digits = tostring(red)
|
|
|
|
|
local margin = (cfg("exam_counter_margin") or { x = 0 }).x
|
|
|
|
|
|
|
|
|
|
local pct_w = 0
|
|
|
|
|
if etype == "gauge" and T("yellow_box", "exam_percent") then
|
|
|
|
|
pct_w = tex_x2("yellow_box", "exam_percent")
|
|
|
|
|
end
|
|
|
|
|
local suffix = (range == "less") and "exam_less" or (range == "more") and "exam_more" or nil
|
|
|
|
|
local suf_w = 0
|
|
|
|
|
if suffix and T("yellow_box", suffix) then
|
|
|
|
|
suf_w = tex_x2("yellow_box", suffix)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local total = #digits * margin + pct_w + suf_w
|
|
|
|
|
local cx = centre_x - total * 0.5
|
|
|
|
|
for j = 1, #digits do
|
|
|
|
|
local digit = string.byte(digits, j) - 48
|
|
|
|
|
draw_abs("yellow_box", "judge_num", cx, top_y, f, digit)
|
|
|
|
|
cx = cx + margin
|
|
|
|
|
end
|
|
|
|
|
if pct_w > 0 then
|
|
|
|
|
draw_abs("yellow_box", "exam_percent", cx, top_y + 8, f)
|
|
|
|
|
cx = cx + pct_w
|
|
|
|
|
end
|
|
|
|
|
if suf_w > 0 then
|
|
|
|
|
draw_abs("yellow_box", suffix, cx, top_y + 6, f)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function DanSelect:draw_exam_grid(box, f)
|
|
|
|
|
local exam_frame = T("yellow_box", "exam_frame")
|
|
|
|
|
if exam_frame then tex.draw_texture(exam_frame, { fade = f }) end
|
|
|
|
|
tex.draw_texture(T("yellow_box", "exam_header"), { fade = f })
|
|
|
|
|
|
|
|
|
|
local gb = cfg("dan_exam_gauge_box")
|
|
|
|
|
local sb = cfg("dan_exam_score_box")
|
|
|
|
|
local gx = (gb and gb.x) or 436.0
|
|
|
|
|
local gy = (gb and gb.y) or 615.0
|
|
|
|
|
local sx = (sb and sb.x) or 769.0
|
|
|
|
|
local sy = (sb and sb.y) or 615.0
|
|
|
|
|
local pitch = (sb and sb.height and sb.height > 0) and sb.height or 132.0
|
|
|
|
|
|
|
|
|
|
local sg = cfg("dan_exam_seg")
|
|
|
|
|
local seg_x = (sg and sg.x) or 14.0
|
|
|
|
|
local seg_y = (sg and sg.y) or 48.0
|
|
|
|
|
local seg_dx = (sg and sg.width and sg.width > 0) and sg.width or 327.0
|
|
|
|
|
local vp = cfg("dan_exam_value")
|
|
|
|
|
local val_x = (vp and vp.x) or 185.0
|
|
|
|
|
local val_y = (vp and vp.y) or 40.0
|
|
|
|
|
local pp = cfg("dan_exam_label_pill")
|
|
|
|
|
local pill_x = (pp and pp.x) or 26.0
|
|
|
|
|
local pill_y = (pp and pp.y) or 2.0
|
|
|
|
|
|
|
|
|
|
local seg_bt = cfg("dan_select_exam_border_text")
|
|
|
|
|
local gauge_bt = cfg("dan_select_gauge_border_text")
|
|
|
|
|
|
|
|
|
|
local right_row = 0
|
|
|
|
|
local gauge_used = false
|
|
|
|
|
local exam_count = box:exam_count()
|
|
|
|
|
for i = 0, exam_count - 1 do
|
|
|
|
|
local etype = box:exam_type(i)
|
|
|
|
|
local range = box:exam_range(i)
|
|
|
|
|
local left = (etype == "gauge" and not gauge_used)
|
|
|
|
|
if left then gauge_used = true end
|
|
|
|
|
-- The cabinet has three right-side slots; skip (don't stop) further right rows.
|
|
|
|
|
if left or right_row < 3 then
|
|
|
|
|
local BX = left and gx or sx
|
|
|
|
|
local BY = left and gy or (sy + pitch * right_row)
|
|
|
|
|
|
|
|
|
|
draw_abs("yellow_box", left and "exam_gauge_box" or "judge_box",
|
|
|
|
|
BX - (left and 0 or 3), BY - (left and 0 or 2), f)
|
|
|
|
|
|
|
|
|
|
local icon_name = exam_icon_name(etype)
|
|
|
|
|
if icon_name then draw_abs("yellow_box", icon_name, BX + pill_x, BY + pill_y, f) end
|
|
|
|
|
|
|
|
|
|
if left then
|
|
|
|
|
if not draw_value_text(self, box:exam_caption(i), gauge_bt,
|
|
|
|
|
BX + ((gauge_bt and gauge_bt.x) or 0),
|
|
|
|
|
BY + ((gauge_bt and gauge_bt.y) or 0), f) then
|
|
|
|
|
draw_value(self, box:exam_red(i), etype, range, BX + 181.0, BY + 45.0, f)
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
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 frame = gothrough and 0 or (k + 1)
|
|
|
|
|
draw_abs("yellow_box", "exam_seg_label", BX + seg_x + seg_dx * k, BY + seg_y, f, frame)
|
|
|
|
|
|
|
|
|
|
local red = gothrough and box:exam_red(i) or box:exam_song_red(i, k)
|
|
|
|
|
if not draw_value_text(self, box:exam_song_caption(i, k), seg_bt,
|
|
|
|
|
BX + ((seg_bt and seg_bt.x) or 0) + seg_dx * k,
|
|
|
|
|
BY + ((seg_bt and seg_bt.y) or 0), f) then
|
|
|
|
|
draw_value(self, red, etype, range, BX + val_x + seg_dx * k, BY + val_y, f)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
if not left then right_row = right_row + 1 end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|