Files
PyTaikoGreen/Scripts/entry/campaign_menu.lua
T
2026-11-02 15:45:40 -05:00

123 lines
4.0 KiB
Lua

require("entry/mac_popup")
CampaignMenu = {}
CampaignMenu.__index = CampaignMenu
local S = tex.get_screen_scale()
local FADE_MS = 250
local WAIT_MS = 1500
local SELECT_MS = 30000
local SLIDE_MS = 150
local OUT_MS, IN_MS = 167, 150
local ARROW_MS, ARROW_DX, ARROW_GAP = 1000, 30 * S, 600 * S
-- small panel left edge by slot offset; the centre slot sits under the big panel
local SLOT_X = { [-2] = -338, [-1] = 17, [0] = 482, [1] = 947, [2] = 1302 }
local SLOT_STEP, SLOT_Y = 355, 262 * S
local LEFT, RIGHT, DECIDE = 1, 2, 3
local function clamp01(v) return math.max(0.0, math.min(1.0, v)) end
local function slot_x(offset)
local lo = math.floor(offset)
local function at(k)
if k < -2 then return SLOT_X[-2] + (k + 2) * SLOT_STEP end
if k > 2 then return SLOT_X[2] + (k - 2) * SLOT_STEP end
return SLOT_X[k]
end
return (at(lo) + (at(lo + 1) - at(lo)) * (offset - lo)) * S
end
function CampaignMenu.new(ids)
for _, id in ipairs(ids) do
if id == "mac" then return MacPopup.new() end
end
local self = setmetatable({}, CampaignMenu)
self.ids = { "skip" }
for _, id in ipairs(ids) do self.ids[#self.ids + 1] = id end
self.small, self.main = {}, {}
for _, id in ipairs(self.ids) do
self.small[id] = tex.get_texture("entry/campaign/" .. id .. "_small")
self.main[id] = tex.get_texture("entry/campaign/" .. id .. "_main")
end
self.arrow = tex.get_texture("entry/campaign/arrow")
self.current = 1
self.t0 = tex.get_current_ms()
self.now = self.t0
self.turn = nil
self.decided = nil
if audio.has_sound("campaign_entry") then audio.play_sound("campaign_entry", "sound") end
return self
end
function CampaignMenu:update(current_ms)
self.now = current_ms
if not self.decided and current_ms - self.t0 >= WAIT_MS + SELECT_MS then
self:decide()
end
if self.turn and current_ms - self.turn.t0 >= OUT_MS + IN_MS then
self.turn = nil
end
end
function CampaignMenu:decide()
self.decided = self.now
end
function CampaignMenu:input(action)
if self.decided or self.turn or self.now - self.t0 < WAIT_MS then
return false
end
if action == DECIDE then
self:decide()
return true
end
local dir = action == RIGHT and 1 or -1
self.turn = { dir = dir, t0 = self.now, from = self.current }
self.current = (self.current - 1 + dir) % #self.ids + 1
return true
end
function CampaignMenu:is_done()
return self.decided ~= nil and self.now - self.decided >= FADE_MS
end
function CampaignMenu:get_choice()
return self.ids[self.current]
end
function CampaignMenu:draw()
local fade = clamp01((self.now - self.t0) / FADE_MS)
if self.decided then fade = 1 - clamp01((self.now - self.decided) / FADE_MS) end
local n = #self.ids
-- mid-turn every slot is still `dir` slots short of where it lands
local shift = 0
if self.turn then shift = self.turn.dir * (1 - clamp01((self.now - self.turn.t0) / SLIDE_MS)) end
for k = -3, 3 do
local offset = k + shift
if offset > -3 and offset < 3 then
local id = self.ids[(self.current - 1 + k) % n + 1]
tex.draw_texture(self.small[id], { x = slot_x(offset), y = SLOT_Y,
fade = fade * clamp01(math.abs(offset)) })
end
end
local id, main_fade = self.ids[self.current], 1
if self.turn then
local el = self.now - self.turn.t0
if el < OUT_MS then
id, main_fade = self.ids[self.turn.from], 1 - el / OUT_MS
else
main_fade = clamp01((el - OUT_MS) / IN_MS)
end
end
tex.draw_texture(self.main[id], { fade = fade * main_fade })
local el = self.now - self.t0 - WAIT_MS
if el >= 0 and not self.decided then
local u = (el % ARROW_MS) / ARROW_MS
tex.draw_texture(self.arrow, { x = -ARROW_DX * u, fade = fade * (1 - u) })
tex.draw_texture(self.arrow, { x = ARROW_GAP + ARROW_DX * u, fade = fade * (1 - u), mirror = "horizontal" })
end
end