Files
YataiDONNijiiro/Scripts/song_select/selector.lua
T
2026-10-14 16:14:11 -04:00

93 lines
4.2 KiB
Lua

-- SongSelectPlayer selector cursor (bubble + course frame / button glow), split out of
-- song_select.lua. Drawn in two passes (under/over the wheel).
local base = require("song_select/file_navigator/box_base")
local DIFF_EASY, DIFF_NEIRO, DIFF_ONI = base.DIFF_EASY, base.DIFF_NEIRO, base.DIFF_ONI
local DS_SLOT_X = base.DS_SLOT_X
local DS_BOARD_Y, DS_CURSOR_Y, DS_NEIRO_Y, DS_BTN_Y = base.DS_BOARD_Y, base.DS_CURSOR_Y, base.DS_NEIRO_Y, base.DS_BTN_Y
local SEL_BASE_BALLOON = base.SEL_BASE_BALLOON
local MOD_SLIDE, MOD_SLIDE_MS = base.MOD_SLIDE, base.MOD_SLIDE_MS
local draw_centered, draw_centered_b = base.draw_centered, base.draw_centered_b
-- main_diff sprite 40 (balloon): `in` f10-24 drops from y -50 with a fade (ease-out), `move` f4-9 bobs
-- 3.35 -> 10 -> 0 (linear). Returns y offset and alpha for the frame `i` of a state.
local FRAME_MS = 1000 / 60
local function balloon_motion(st, i)
if st == "in" then
local u = 1 - (1 - math.min(1, i / 14)) ^ 2
return -50 * (1 - u), u
end
if i < 3 then return 3.35 + 6.65 * i / 2, 1 end
if i < 6 then return 6.65 * (1 - (i - 3) / 2), 1 end
return 0, 1
end
function SongSelect:draw_selector(player, is_half, fade_in, pass)
local under = (pass == nil) or (pass == 0)
local over = (pass == nil) or (pass == 1)
local diff = player:selected_difficulty()
local p = player:player_num()
if under then
self._mark_diff[p] = diff
local decided = diff >= DIFF_EASY and player.difficulty_decided and player:difficulty_decided()
if decided then
if not self._mark_decided[p] then
self._mark_decided[p] = { t0 = tex.get_current_ms(), course = diff }
end
else
self._mark_decided[p] = nil
end
end
self._bal = self._bal or {}
if fade_in < 1.0 then self._bal[p] = nil return end
local bal = self._bal[p]
if not bal then
bal = { diff = diff, t0 = tex.get_current_ms(), st = "in" }
self._bal[p] = bal
elseif bal.diff ~= diff then
bal.diff, bal.t0, bal.st = diff, tex.get_current_ms(), "move"
end
local bal_dy, bal_a = balloon_motion(bal.st, (tex.get_current_ms() - bal.t0) / FRAME_MS)
local mod_on = player:modifier_active()
if player.modifier_offset then
self._mod_off[p] = player:modifier_offset()
elseif mod_on then
if not self._mod_seen[p] then
local now = tex.get_current_ms()
local prev = self._last_sel_ms or now
self._mod_t0[p] = (now - prev < 250) and math.min(now, prev + 16.7) or now
end
local u = math.max(0.0, math.min(1.0, (tex.get_current_ms() - self._mod_t0[p]) / MOD_SLIDE_MS))
self._mod_off[p] = -MOD_SLIDE * u * (2 - u)
else
self._mod_off[p] = nil
end
self._last_sel_ms = tex.get_current_ms()
self._mod_seen[p] = mod_on
local fade = (player:neiro_active() or mod_on) and 0.5 or fade_in
-- the cursor offset is +-40 only while both players sit on the same slot
local cursor_dx = is_half and ((p == 2) and 40 or -40) or 0
local sx = DS_SLOT_X[(diff >= DIFF_EASY) and math.min(DIFF_ONI, diff) or diff]
if not sx then return end
if diff >= DIFF_EASY then
local col = math.min(DIFF_ONI, diff)
if col == DIFF_ONI and self._ura_anim then return end -- cursor/frame hidden during the change
sx = DS_SLOT_X[col]
local frame_id = (p == 1) and self._id_course_frame or self._id_course_frame2
if under then draw_centered(frame_id, sx, DS_BOARD_Y, 248, 408, { fade = fade }) end
if over then
draw_centered_b((p == 1) and self._id_bubble_1p or self._id_bubble_2p, SEL_BASE_BALLOON,
sx + cursor_dx, DS_CURSOR_Y + bal_dy, 124, 124, { fade = fade * bal_a })
end
else
local by = (diff == DIFF_NEIRO) and DS_NEIRO_Y or DS_BTN_Y
if under then draw_centered(self._id_btn_glow, sx, by, 168, 168, { fade = fade }) end
if over then
draw_centered_b((p == 1) and self._id_bubble_1p or self._id_bubble_2p, SEL_BASE_BALLOON,
sx + cursor_dx, DS_CURSOR_Y + bal_dy, 124, 124, { fade = fade * bal_a })
end
end
end