Files
YataiDONNijiiro/Scripts/song_select/diff_sort.lua
T

202 lines
8.8 KiB
Lua

-- Difficulty sort window, split out of song_select.lua.
local base = require("song_select/file_navigator/box_base")
local draw_centered = base.draw_centered
local draw_title = base.draw_title
local draw_guide_label = base.draw_guide_label
local outline = base.outline
local GUIDE_SELECT_CX, GUIDE_DECIDE_CX = base.GUIDE_SELECT_CX, base.GUIDE_DECIDE_CX
local SW_ROW_Y = { 410, 551, 692 }
local SW_BOARD_W, SW_BOARD_H = 1320, 760
local SW_CURSOR_W, SW_CURSOR_H = 1240, 180
local SW_PILL_CX = 1124 -- 960 + 164
local SW_PILL_W, SW_PILL_H = 792, 104
local SW_MARK_CX = 871 -- 960 - 89
local SW_ARROW_LX = 786 -- 960 - 174
local SW_ARROW_RX = 1462 -- 960 + 502
local SW_HEADER_CX = 560 -- 960 - 400
local SW_TEXT_DY = -13
local SW_GUIDE_Y = 827
local SW_GUIDE_W, SW_GUIDE_H = 372, 96
local SW_NUM_RIGHT = 1379
local SW_NUM_Y = 289
local SW_NUM_PITCH = 34
local SW_NUM_W, SW_NUM_H = 48, 56
local SW_SONGS_X = 1402 -- ink LEFT edge (EditText align = left)
local SW_SONGS_Y = 277
local SW_TITLE_X = 405
local SW_TITLE_Y = 272
local SW_TITLE_OUTLINE = { 119, 26, 45, 255 }
local SW_HEADER_BOX_W = 310
local SW_ITEM_BOX_W = { 620, 430, 430 }
local SW_CURSOR_CYCLE = 51.0
local SW_KEYS = {
header = { "sort_window_diff", "sort_window_star", "sort_window_order" },
course = { "course_name_0", "course_name_1", "course_name_2", "course_name_3", "course_name_3" },
order = { "sort_window_order_0", "sort_window_order_1", "sort_window_order_2", "sort_window_order_3" },
title = "folder_sort_select",
songs = "sort_window_songs",
}
local function sw_words(group)
local k = SW_KEYS[group]
if type(k) == "table" then
local out = {}
for i, key in ipairs(k) do out[i] = tex.skin_text(key) end
return out
end
return tex.skin_text(k)
end
-- OutlinedText cache: key = group .. "\0" .. index.
function SongSelect:_sw_text(key, str, size, thickness, outline)
self._sw_texts = self._sw_texts or {}
local t = self._sw_texts[key]
if t == nil then
t = text.create_raw_text(str, size, { 255, 255, 255, 255 },
outline or { 0, 0, 0, 255 }, false, thickness, 2.0)
self._sw_texts[key] = t or false
end
return t or nil
end
-- Fit-shrink: when a string does not fit its box, re-rasterise it at a smaller (uniformly
-- scaled) point size instead of stretching the bitmap, so glyphs keep their proportions.
local SW_FIT_MIN_SIZE = 22
function SongSelect:_sw_text_fit(key, str, size, thickness, outline, max_w)
self._sw_texts = self._sw_texts or {}
local t = self._sw_texts[key]
if t == nil then
t = text.create_raw_text(str, size, { 255, 255, 255, 255 },
outline or { 0, 0, 0, 255 }, false, thickness, 2.0)
if t and max_w and t.width > max_w then
local fit_size = math.max(SW_FIT_MIN_SIZE, math.floor(size * max_w / t.width))
if fit_size < size then
local fit_thickness = thickness * fit_size / size
local t2 = text.create_raw_text(str, fit_size, { 255, 255, 255, 255 },
outline or { 0, 0, 0, 255 }, false, fit_thickness, 2.0)
if t2 then t = t2 end
end
end
self._sw_texts[key] = t or false
end
return t or nil
end
-- Draw an OutlinedText with its ink LEFT edge on x.
local function draw_text_left(t, x, cy, fade)
if not t then return end
t:draw({ x = math.floor(x + 0.5), y = math.floor(cy - t.height / 2 + 0.5), fade = fade })
end
-- cursor glow alpha over the 51-frame loop, sampled from anim/sort_cursor.
local sort_cursor_ok, sort_cursor_anim = pcall(require, "anim/sampler")
local SW_CURSOR_CLIP = (sort_cursor_ok and sort_cursor_anim.safe("anim/sort_cursor"))
or { get = function() return nil end, label = function() return nil end }
local SW_ON_F = SW_CURSOR_CLIP:label("on") or 9
local function sw_cursor_glow(ms)
local r = (ms * 0.06) % SW_CURSOR_CYCLE
local a = SW_CURSOR_CLIP:get("#3@0", SW_ON_F + r, "a")
if a then return a end
if r <= 20.0 then return 1.0 - r / 20.0 end
return (r - 20.0) / 30.0
end
function SongSelect:_sw_ids()
if self._sw_loaded then return end
tex.load_folder("song_select", "diff_sort")
self._id_sw_board = tex.get_texture("song_select/diff_sort/background") -- 1320x760 window
self._id_sw_pill = tex.get_texture("song_select/diff_sort/level_box") -- 792x104 white item pill
self._id_sw_cur = tex.get_texture("song_select/diff_sort/box_highlight") -- 1240x180 flat yellow bar
self._id_sw_glow = tex.get_texture("song_select/diff_sort/box_outline") -- 1240x180 pulsing glow
self._id_sw_mark = tex.get_texture("song_select/diff_sort/diff") -- 96x96, frame = course
self._id_sw_arrow = tex.get_texture("song_select/diff_sort/arrow") -- 48x56, frame 1 = rotated
self._id_sw_digit = tex.get_texture("song_select/diff_sort/stat_num") -- 48x56, frame = digit
self._id_sw_guide = tex.get_texture("song_select/diff_sort/star_select_text") -- s_menfuchi_kp loop
self._sw_loaded = true
end
function SongSelect:draw_sort_window(w)
-- the classic two-step window is the default sort_window module's
if not w:one_menu() then return false end
self:_sw_ids()
local a = w:alpha()
if a <= 0.0 or not self._id_sw_board then return end
local ms = tex.get_current_ms()
local session = w:session()
local vals = { w:diff(), w:star(), w:order() }
draw_centered(self._id_sw_board, 960, 540, SW_BOARD_W, SW_BOARD_H, { fade = a })
-- the three rows, in the cabinet's own depth order (order, star, diff)
for row = 3, 1, -1 do
local ry = SW_ROW_Y[row]
local idx = row - 1 -- 0-based, matching SortWindow:session()
if idx == session and self._id_sw_cur then
if self._id_sw_glow then
draw_centered(self._id_sw_glow, 960, ry, SW_CURSOR_W, SW_CURSOR_H,
{ fade = a * sw_cursor_glow(ms) })
end
draw_centered(self._id_sw_cur, 960, ry, SW_CURSOR_W, SW_CURSOR_H, { fade = a })
end
if self._id_sw_pill then
draw_centered(self._id_sw_pill, SW_PILL_CX, ry, SW_PILL_W, SW_PILL_H, { fade = a })
end
-- diff_mark is visible on the むずかしさ row only
if row == 1 and self._id_sw_mark then
draw_centered(self._id_sw_mark, SW_MARK_CX, ry, 96, 96,
{ frame = math.max(0, math.min(4, vals[1] - 1)), fade = a })
end
if self._id_sw_arrow then
local dx = w:arrow_offset(idx)
draw_centered(self._id_sw_arrow, SW_ARROW_LX + dx, ry, 48, 56, { frame = 0, fade = a })
draw_centered(self._id_sw_arrow, SW_ARROW_RX - dx, ry, 48, 56, { frame = 1, fade = a })
end
-- item value
local str, key
if row == 1 then
local n = math.max(1, math.min(5, vals[1]))
str, key = sw_words("course")[n], "course" .. n
elseif row == 2 then
str, key = tostring(vals[2]), "star" .. vals[2]
else
local n = math.max(1, math.min(4, vals[3]))
str, key = sw_words("order")[n], "order" .. n
end
draw_title(self:_sw_text_fit(key, str, 42, outline(6.5), nil, SW_ITEM_BOX_W[row]),
SW_PILL_CX, ry + SW_TEXT_DY, a, SW_ITEM_BOX_W[row], 0)
-- header
draw_title(self:_sw_text_fit("hdr" .. row, sw_words("header")[row], 42, outline(6.5), nil, SW_HEADER_BOX_W),
SW_HEADER_CX, ry + SW_TEXT_DY, a, SW_HEADER_BOX_W, 0)
end
-- control guide + its two pill labels
if self._id_sw_guide then
local cell = math.floor((ms * 0.06) % 210)
draw_centered(self._id_sw_guide, 960, SW_GUIDE_Y, SW_GUIDE_W, SW_GUIDE_H,
{ frame = cell, fade = a })
local labels = self:guide_labels()
if labels then
draw_guide_label(labels.select, GUIDE_SELECT_CX, SW_GUIDE_Y + 2, a)
draw_guide_label(labels.decide, GUIDE_DECIDE_CX, SW_GUIDE_Y + 2, a)
end
end
-- song counter: 「N 曲」 (right-aligned digits, then the unit label)
local n = math.max(0, math.min(9999, w:song_num()))
local digits = tostring(n)
for i = 1, #digits do
local d = tonumber(digits:sub(i, i))
local x = SW_NUM_RIGHT - (#digits - i) * SW_NUM_PITCH
draw_centered(self._id_sw_digit, x, SW_NUM_Y, SW_NUM_W, SW_NUM_H, { frame = d, fade = a })
end
draw_text_left(self:_sw_text("songs", sw_words("songs"), 42, outline(6.5)), SW_SONGS_X, SW_SONGS_Y, a)
-- board title, with the cabinet's dark-red outline add
draw_text_left(self:_sw_text("title", sw_words("title"), 54, outline(7.5), SW_TITLE_OUTLINE),
SW_TITLE_X, SW_TITLE_Y, a)
end