-- FolderBox rendering (genre kanban), split out of song_select.lua. Shared helpers and -- the wheel/folder animation state come from box_base. local base = require("song_select/file_navigator/box_base") local folder_leg = base.folder_leg local BOARD_W = base.BOARD_W local CAP_H = base.CAP_H local CEN_CLOSED_H, CEN_OPEN_H = base.CEN_CLOSED_H, base.CEN_OPEN_H local FOLDER_CLOSE_MS, STALE_TIMER_MS = base.FOLDER_CLOSE_MS, base.STALE_TIMER_MS local draw_board, draw_glow, glow_pulse = base.draw_board, base.draw_glow, base.glow_pulse local draw_title = base.draw_title local outline, board_outline = base.outline, base.board_outline local box_id = base.box_id local go_get, leg_f, box_fade, fly_offset = base.go_get, base.leg_f, base.box_fade, base.fly_offset local is_decided = base.is_decided local wheel_open_t, wheel_close_step = base.wheel_open_t, base.wheel_close_step local folder_open_timeline, folder_close_timeline = base.folder_open_timeline, base.folder_close_timeline -- Event/search folders are matched by display name (the engine folds DEFAULT/RECOMMENDED/ -- FAVORITE/DAN/DIFFICULTY into genre frame 0). local EVENT_FOLDER_NAMES = { ["ドドンと!新曲祭り!"] = true, ["New Songs"] = true, ["New"] = true, ["おすすめ曲"] = true, ["Recommended"] = true, ["推薦歌曲"] = true } local SORT_FOLDER_NAMES = { ["曲検索"] = true, ["Search Song"] = true, ["搜尋歌曲"] = true, ["むずかしさで探す"] = true, ["Difficulty Sort"] = true, ["難易度排序"] = true, ["むずかしさからえらぶ"] = true, ["Diff Sort"] = true } local FOLDER_FRAME_EVENT, FOLDER_FRAME_SORT, FOLDER_FRAME_DANI = 11, 12, 10 local SORT_COLLECTIONS = { SEARCH = true, DIFFICULTY = true } local EVENT_COLLECTIONS = { NEW = true, RECOMMENDED = true } local function folder_frame(box) local n = box.text_name if n and EVENT_FOLDER_NAMES[n] then return FOLDER_FRAME_EVENT, true end local col = box.collection if col and col ~= "" then if EVENT_COLLECTIONS[col] then return FOLDER_FRAME_EVENT, true end if SORT_COLLECTIONS[col] then return FOLDER_FRAME_SORT, false end end if box.kind and box:kind() == "dan" then return FOLDER_FRAME_DANI, false end if n and SORT_FOLDER_NAMES[n] then return FOLDER_FRAME_SORT, false end return box:genre_frame(), false end -- instance_board_pattern: 960x120 strip centred on the board, y follows the title function SongSelect:draw_pattern(bx, by, p, fade) if not self._id_pattern then return end tex.draw_texture(self._id_pattern, { x = bx - 480, y = by - 94 * p - 60, fade = fade }) end -- The genre board title, re-made at the skin_config folder_box_name size. local FOLDER_TITLE_SIZE = nil function SongSelect:folder_title(box) if FOLDER_TITLE_SIZE == nil then local cfg = tex.get_skin_config("folder_box_name") FOLDER_TITLE_SIZE = (cfg and cfg.font_size and cfg.font_size > 0) and cfg.font_size or false end if not FOLDER_TITLE_SIZE then return box:name() end local key = box.text_name if not key or key == "" then return box:name() end local ff = folder_frame(box) key = ff .. "\0" .. key local t = self._folder_titles[key] if t == nil then t = text.create_raw_text(box.text_name, FOLDER_TITLE_SIZE, { 255, 255, 255, 255 }, board_outline(ff), false, outline(7.5), 2.0) self._folder_titles[key] = t or false end return t or box:name() end function SongSelect:draw_folder_board(box, pb, ch_alpha, ch_off, ta, glow_a) local bx, by = box:box_x(), box:box_y() local fdx, fdy = fly_offset(box); bx, by = bx + fdx, by + fdy -- wheel fly-in local cen_h = CEN_CLOSED_H + (CEN_OPEN_H - CEN_CLOSED_H) * pb local fade = box_fade(box) local frame, has_pattern = folder_frame(box) -- the genre board furls/unfurls horizontally across the folder transition local ltitle = 1.0 local lf = (box:draw_state() == "open" or is_decided(box)) and leg_f() or nil if lf then local sx, ba = go_get("genre_board_sx", lf, 1), go_get("genre_board_sx", lf, 2) if sx and ba then if ba <= 0.0 then return end -- the board is not on screen if sx < 1.0 then -- furl / unfurl: the open slab alone draw_board(self._id_bar_folder, frame, bx, by, CEN_OPEN_H, fade * ba, false, sx) return end ch_alpha = math.min(ch_alpha, go_get("genre_chara_a", lf, 1) or 1.0) ta = math.min(ta, go_get("genre_intro_a", lf, 1) or 1.0) ltitle = go_get("genre_title_a", lf, 1) or 1.0 end end if glow_a > 0 then draw_glow(self._id_glow, bx, by, cen_h, fade * glow_a * glow_pulse()) end -- closed folder = bar_genre, open folder = folder_graphic (title band on top) if pb <= 0.0 then draw_board(self._id_bar_genre, frame, bx, by, cen_h, fade) else draw_board(self._id_bar_folder, frame, bx, by, cen_h, fade, pb >= 1.0) if pb < 1.0 then draw_board(self._id_bar_genre, frame, bx, by, cen_h, fade * (1.0 - pb)) end end if has_pattern then self:draw_pattern(bx, by, pb, fade) end if box:has_box_texture() then -- a library-supplied box.png is fitted (aspect kept, shrink only) into the board rect if ch_alpha > 0 then box:draw_box_texture({ cx = bx, cy = by, max_w = BOARD_W, max_h = CAP_H * 2 + CEN_OPEN_H, fade = fade * ch_alpha }) end -- title drawn after the art, matching the cabinet's depth order draw_title(self:folder_title(box), bx, by - 94 * pb, fade * ltitle) return end -- characters: texture has L at x=510 and R at x=1410 if ch_alpha > 0 then local slide = 450.0 - ch_off local gframe = box:genre_frame() tex.draw_texture(self._id_box_chara, { frame = gframe, x = bx - 960 + slide, y = by - 240, x2 = 960 - 1920, fade = fade * ch_alpha, src = { x = 0, y = 0, width = 960, height = 480 } }) tex.draw_texture(self._id_box_chara, { frame = gframe, x = bx - slide, y = by - 240, x2 = 960 - 1920, fade = fade * ch_alpha, src = { x = 960, y = 0, width = 960, height = 480 } }) end -- title rises to y-94 when open; the song count stands in for the intro text draw_title(self:folder_title(box), bx, by - 94 * pb, fade * ltitle) local n = box.tja_count if ta > 0 and n and n > 0 then local ckey = frame .. "\0" .. n local label = self._count_cache[ckey] if not label then label = text.create_raw_text(tostring(n) .. " songs", 32, { 255, 255, 255, 255 }, board_outline(frame), false, outline(4.0), 2.0) self._count_cache[ckey] = label end if label then draw_title(label, bx, by + 49 * pb, fade * ta) end end end function SongSelect:draw_folder_closed(box) local key = "f\0" .. box_id(box) local e = wheel_close_step(key, FOLDER_CLOSE_MS, STALE_TIMER_MS) if e then local pb, ch_alpha, ch_off, ta = folder_close_timeline(e) self:draw_folder_board(box, pb, ch_alpha, ch_off, ta, pb) return end self:draw_folder_board(box, 0.0, 0.0, 340.0, 0.0, 0.0) end function SongSelect:draw_folder_open(box) -- remember the decided board: the engine removes it from the wheel while its songs -- load, and draw_overlays keeps drawing it until it has furled away folder_leg.last_open_id, folder_leg.last_open_box = box_id(box), box if is_decided(box) then self._dec_frame = self._frame_no self:draw_folder_board(box, 1.0, 1.0, 440.0, 1.0, 1.0) return end local key = "f\0" .. box_id(box) local t = wheel_open_t(key) if t == nil then -- 508 ms pre-growth hold: fully closed self:draw_folder_board(box, 0.0, 0.0, 340.0, 0.0, 0.0) return end local pb, ch_alpha, ch_off, ta = folder_open_timeline(t) self:draw_folder_board(box, pb, ch_alpha, ch_off, ta, pb) end return { folder_frame = folder_frame }