Files
PyTaikoGreen/Scripts/song_select/ai_support_menu.lua
T
2026-11-06 00:21:39 -05:00

185 lines
6.1 KiB
Lua

AISupportMenu = {}
AISupportMenu.__index = AISupportMenu
local FRAME_MS = 1000 / 60
local S_SCALE = tex.get_screen_scale()
local DIR = "song_select/ai_battle/"
local P2_OFFSET = 836
local BOARD_OFFSETS = { [1] = { 0 }, [4] = { -117, -39, 38, 116 } }
local COURSES = {
[0] = { board = "subdiff_easy", count = 1 },
[1] = { board = "subdiff_normal", count = 4 },
[2] = { board = "subdiff_hard", count = 4 },
[3] = { board = "subdiff_oni", count = 4 },
}
local SLIDE_IN = { { 0, 0, 0.2 }, { 5, -124, 1 }, { 15, -373, 1 }, { 21, -356, 1 } }
local SLIDE_OUT = { { 0, -356, 1 }, { 15, -89, 1 }, { 20, 0, 0 } }
local GRAY_FRAMES, GRAY_FADE = 30, 0.4
local GLOW_PERIOD, GLOW_LOW = 120, 0.7
local BRANCH_PERIOD = 120
local function slide_at(keys, frame)
for i = 2, #keys do
local a, b = keys[i - 1], keys[i]
if frame <= b[1] then
local t = math.max(0, (frame - a[1]) / (b[1] - a[1]))
return a[2] + (b[2] - a[2]) * t, a[3] + (b[3] - a[3]) * t
end
end
return keys[#keys][2], keys[#keys][3]
end
local function wave(frame, period, from, to)
local t = 1 - math.abs(2 * (frame % period) / period - 1)
return from + (to - from) * t
end
function AISupportMenu.new()
local self = setmetatable({}, AISupportMenu)
self.ids = {}
self.shown = false
self.slide_ms = -math.huge
self.course = 0
self.choose_ms = nil
return self
end
function AISupportMenu:id(key)
self.ids[key] = self.ids[key] or tex.get_texture(DIR .. key)
return self.ids[key]
end
function AISupportMenu:set_status(status)
self.status = status
self.rank_ids = {}
end
local RANK_DIR = "song_select/ai_rank/"
local RANK_MAX, ASSIGNED_RANK = 11, 10
local TODO_X, TODO_Y = 396, 590
local TODO_BACK = { [0] = 0, 0, 0, 1, 1, 2, 2, 3, 3, 3 }
function AISupportMenu:rank_tex(key, x, y, frame, fade, scale)
self.rank_ids[key] = self.rank_ids[key] or tex.get_texture(RANK_DIR .. key)
local o = { x = x * S_SCALE, y = y * S_SCALE, frame = frame or 0, fade = fade or 1 }
if scale then
local info = tex.get_texture_info("ai_rank", key)
o.x2 = info.width * S_SCALE * (scale - 1)
o.y2 = info.height * S_SCALE * (scale - 1)
end
tex.draw_texture(self.rank_ids[key], o)
end
function AISupportMenu:rank_number(key, value, right_x, y, step, scale)
value = math.max(0, math.floor(value))
local x = right_x
repeat
self:rank_tex(key, x, y, value % 10, 1, scale)
value = value // 10
x = x - step
until value == 0
end
function AISupportMenu:draw_rank_panels(ms)
local s = self.status
if not s then return end
if s.rank >= RANK_MAX then
self:rank_tex("rank_panel_max", 17, 146)
else
self:rank_tex("rank_panel", 17, 139)
if s.rank >= 10 then
self:rank_tex("digits_rank", 112.5, 117, 1)
self:rank_tex("digits_rank", 149.5, 117, s.rank - 10)
else
self:rank_tex("digits_rank", 131, 117, s.rank)
end
self:rank_number("digits_small", s.next_rank_tokens - s.tokens, 158, 189.2, 16.8, 0.8)
end
if s.next_reward_wins >= 0 then
self:rank_tex("wins_panel", 16, 225)
self:rank_number("digits_small", s.next_reward_wins - s.wins, 173, 277.2, 16.8, 0.8)
else
self:rank_tex("wins_panel_max", 44, 219)
end
self:rank_number("digits_wins", s.wins, 198.2, 243.2, 18, 0.8)
end
function AISupportMenu:draw_rank_todo(ms)
local s = self.status
if not s then return end
local pulse = 0.5 + 0.5 * wave(ms / FRAME_MS, 80, 0, 1)
if s.rank >= RANK_MAX then
self:rank_tex("todo_glow", TODO_X, TODO_Y, 0, pulse)
self:rank_tex("todo_strongest", TODO_X, TODO_Y)
self:rank_tex("todo_sparkle", TODO_X + 438, TODO_Y + 44, 0, wave(ms / FRAME_MS, 60, 0, 1))
elseif s.rank >= ASSIGNED_RANK then
self:rank_tex("todo_glow", TODO_X, TODO_Y, 0, pulse)
self:rank_tex("todo_strip", TODO_X, TODO_Y, 10)
else
self:rank_tex("todo_back", TODO_X, TODO_Y, TODO_BACK[s.rank])
self:rank_tex("todo_strip", TODO_X, TODO_Y, s.rank)
end
end
function AISupportMenu:draw(ms, course, cursor, choosing, branching, player, tension)
tex.draw_texture(self:id("tension"), { frame = tension })
self:draw_rank_panels(ms)
if course < 0 then self:draw_rank_todo(ms) end
local keys = self.shown and SLIDE_IN or SLIDE_OUT
local frame = (ms - self.slide_ms) / FRAME_MS
if (course >= 0) ~= self.shown and frame >= keys[#keys][1] then
self.shown = course >= 0
self.slide_ms = ms
keys = self.shown and SLIDE_IN or SLIDE_OUT
frame = 0
end
if not self.shown and frame >= keys[#keys][1] then
return
end
if self.shown then
self.course = course
end
local y, fade = slide_at(keys, frame)
local x = player == 2 and P2_OFFSET or 0
local function draw(key, params)
params = params or {}
params.x, params.y = (params.x or 0) + x, (params.y or 0) + y
params.fade = (params.fade or 1) * fade
tex.draw_texture(self:id(key), params)
end
if choosing and not self.choose_ms then
self.choose_ms = ms
end
if choosing then
local gray = math.min(1, (ms - self.choose_ms) / FRAME_MS / GRAY_FRAMES)
tex.draw_texture(self:id("gray_filter"), { x2 = 1264, y2 = 704, fade = GRAY_FADE * gray })
draw("box_bg", { fade = wave(ms / FRAME_MS, GLOW_PERIOD, 1, GLOW_LOW) })
draw("box_bg_blur")
draw("box_2")
draw("subdiff_select_text")
draw(player == 2 and "2p" or "1p")
else
draw("box_1")
end
local info = COURSES[self.course]
local offsets = BOARD_OFFSETS[info.count]
local row = info.count == 1 and 1 or 0
for i = 0, info.count - 1 do
local board_x = offsets[i + 1]
if choosing and i == cursor then
draw("subdiff_selector", { x = board_x, y = row })
end
draw(info.board, { frame = i, x = board_x, y = row })
if branching then
draw("branch_indicator", { x = board_x, y = row, fade = wave(ms / FRAME_MS, BRANCH_PERIOD, 0, 1) })
end
end
draw("bottom_text")
end