Files
YataiDON14/Scripts/game/practice_menu.lua
T
2026-10-15 09:16:53 -04:00

166 lines
6.8 KiB
Lua

-- Practice mode pause menu: the row of bars, and the confirm dialogs (end / another song / restart /
-- auto play). The engine's PracticeMenu keeps the state (open, selected row, dialog, selected button)
-- and the actions; this draws it and says which bar or button a pointer is over.
--
-- Rows, in order: end game, another song, restart, auto play, jump to mark, set mark, close.
PracticeMenu = {}
PracticeMenu.__index = PracticeMenu
local S = tex.get_screen_scale()
local SKIN = {
panel = { y = 188.0001, width = 940 * S, height = 460 * S },
bar = { gap = 36 * S, width = 92 * S, height = 378 * S },
label = { y = 16 * S, width = 350 * S, font_size = 32 },
dialog_font_size = 32,
dialog_title_y = 100 * S,
dialog_title_auto_y = 56 * S,
button = { x = 160 * S, y = 240 * S, width = 212 * S, height = 102 * S },
auto_label = { y = 150 * S, width = 290 * S },
auto_toggle = { gap = 40 * S, y = 280 * S, width = 240 * S },
toggle_y = 6 * S,
}
local WHITE, BLACK = { 255, 255, 255, 255 }, { 0, 0, 0, 255 }
local ROWS = {
"practice_menu_end_game", "practice_menu_another_song", "practice_menu_restart",
"practice_menu_auto_play", "practice_menu_jump_to_mark", "practice_menu_set_mark",
"practice_menu_close",
}
local DIALOGS = {
auto = { title = "practice_menu_confirm_auto", left = "practice_menu_auto_on", right = "practice_menu_auto_off" },
restart = { title = "practice_menu_confirm_restart", left = "practice_menu_yes", right = "practice_menu_no" },
another = { title = "practice_menu_confirm_another", left = "practice_menu_yes", right = "practice_menu_no" },
["end"] = { title = "practice_menu_confirm_end", left = "practice_menu_yes", right = "practice_menu_no" },
}
local function sprite(name)
return tex.get_texture("practice/" .. name)
end
function PracticeMenu.new()
return setmetatable({ lang = nil }, PracticeMenu)
end
-- the texts, rebuilt when the language changes
function PracticeMenu:texts()
local lang = tex.language()
if self.lang == lang then return end
self.lang = lang
self.rows, self.dialogs = {}, {}
local base = SKIN.label.font_size > 0 and SKIN.label.font_size or 1
for i, key in ipairs(ROWS) do
local label = tex.skin_text(key)
local _, chars = label:gsub("[^\128-\191]", "")
local fs, v = base, 1.0
if chars > 1 then
v = (SKIN.label.width / fs - 1.0) / (chars - 1)
if v < 0.7 then
v = 0.7
fs = math.max(1, math.floor(SKIN.label.width / (1.0 + (chars - 1) * 0.7)))
elseif v > 1.0 then
v = 1.0
end
end
self.rows[i] = text.create_raw_text(label, fs, WHITE, BLACK, true, 5.0 * fs / base, 2.0, v)
end
local fs = SKIN.dialog_font_size > 0 and SKIN.dialog_font_size or 1
for name, d in pairs(DIALOGS) do
self.dialogs[name] = {
title = text.create_raw_text(tex.skin_text(d.title), fs, WHITE, BLACK, false),
left = text.create_raw_text(tex.skin_text(d.left), fs, WHITE, BLACK, false),
right = text.create_raw_text(tex.skin_text(d.right), fs, WHITE, BLACK, false),
}
end
end
local function panel_x()
return (tex.get_screen_width() - SKIN.panel.width) / 2.0
end
-- x of the left and right dialog buttons, and their y
local function dialog_buttons(dialog)
local px, b = panel_x(), SKIN.button
if dialog == "auto" then
local t = SKIN.auto_toggle
local total = b.width * 2 + t.width + t.gap * 2
local left = px + (SKIN.panel.width - total) / 2.0
return left, left + b.width + t.gap + t.width + t.gap, SKIN.panel.y + t.y
end
return px + b.x, px + SKIN.panel.width - b.x - b.width, SKIN.panel.y + b.y
end
local function inside(x, y, left, top, w, h)
return x >= left and x <= left + w and y >= top and y <= top + h
end
-- bar under the pointer (0-based), or the dialog button (0 = left, 1 = right); nil if none
function PracticeMenu:hit_test(x, y, dialog)
local b = SKIN.button
if dialog then
local left, right, row_y = dialog_buttons(dialog)
if inside(x, y, left, row_y, b.width, b.height) then return 0 end
if inside(x, y, right, row_y, b.width, b.height) then return 1 end
return nil
end
local n, bar = #ROWS, SKIN.bar
local pad = (SKIN.panel.width - n * bar.width - (n - 1) * bar.gap) / 2.0
local top = SKIN.panel.y + (SKIN.panel.height - bar.height) / 2.0
for i = 0, n - 1 do
if inside(x, y, panel_x() + pad + i * (bar.width + bar.gap), top, bar.width, bar.height) then
return i
end
end
return nil
end
function PracticeMenu:draw(index)
self:texts()
local n, bar, px = #ROWS, SKIN.bar, panel_x()
local pad = (SKIN.panel.width - n * bar.width - (n - 1) * bar.gap) / 2.0
tex.draw_texture(sprite("menu_panel"), { x = px, y = SKIN.panel.y })
for i = 0, n - 1 do
local x = px + pad + i * (bar.width + bar.gap)
local y = SKIN.panel.y + (SKIN.panel.height - bar.height) / 2.0
tex.draw_texture(sprite(i == index and "menu_bar_selected" or "menu_bar"), { x = x, y = y })
local label = self.rows[i + 1]
-- top-aligned like the arcade, not centred
label:draw({ x = x + (bar.width - label.width) / 2.0, y = y + SKIN.label.y })
end
end
-- sel: 0 = left button, 1 = right
function PracticeMenu:draw_dialog(dialog, sel)
self:texts()
local px, pw, b = panel_x(), SKIN.panel.width, SKIN.button
local d = self.dialogs[dialog]
if not d then return end
tex.draw_texture(sprite("menu_panel"), { x = px, y = SKIN.panel.y })
-- the auto dialog stacks a label chip and the toggle row below its title, so the title sits
-- high; the yes/no confirms sit lower
local title_y = (dialog == "auto") and SKIN.dialog_title_auto_y or SKIN.dialog_title_y
d.title:draw({ x = px + (pw - d.title.width) / 2.0, y = SKIN.panel.y + title_y })
local left, right, row_y = dialog_buttons(dialog)
if dialog == "auto" then
tex.draw_texture(sprite("menu_auto_label"),
{ x = px + (pw - SKIN.auto_label.width) / 2.0, y = SKIN.panel.y + SKIN.auto_label.y })
-- the red knob slides toward whichever side is picked
tex.draw_texture(sprite("menu_toggle"), {
mirror = sel == 1 and "horizontal" or nil,
x = left + b.width + SKIN.auto_toggle.gap, y = row_y + SKIN.toggle_y,
})
end
tex.draw_texture(sprite(sel == 0 and "menu_button_selected" or "menu_button"), { x = left, y = row_y })
tex.draw_texture(sprite(sel == 1 and "menu_button_selected" or "menu_button"), { x = right, y = row_y })
d.left:draw({ x = left + (b.width - d.left.width) / 2.0, y = row_y + (b.height - d.left.height) / 2.0 })
d.right:draw({ x = right + (b.width - d.right.width) / 2.0, y = row_y + (b.height - d.right.height) / 2.0 })
end