209 lines
8.0 KiB
Lua
209 lines
8.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 F = 1000 / 60
|
|
local HINT_K, HINT_Y, HINT_FONT = 0.85, 620, 28
|
|
local DRUM_R_DX, ARROW_R_DX = 329.8, 122.4
|
|
local WHITE, BLACK = { 255, 255, 255, 255 }, { 0, 0, 0, 255 }
|
|
|
|
local WORDS = {
|
|
{ tex = "rim", f = 0, d = 1.28, lift = 6.75 },
|
|
{ text = "campaign_hint_by", x = 545.65, f = 0, d = 1.091, lift = 2.15 },
|
|
{ text = "campaign_hint_select", x = 616.2, f = 66, d = 1.214, lift = 5.1 },
|
|
{ tex = "face", f = 181, d = 1.28, lift = 6.75 },
|
|
{ text = "campaign_hint_by", x = 834.65, f = 181, d = 1.091, lift = 2.15 },
|
|
{ text = "campaign_hint_decide", x = 883.1, f = 246, d = 1.214, lift = 5.1 },
|
|
}
|
|
|
|
local RIM_GLOW_L = { { 0, 0 }, { 20, 1 }, { 40, 0 }, { 65, 0 }, { 65, 1 }, { 180, 1 }, { 185, 0 },
|
|
{ 361, 0 }, { 390, 1 }, { 420, 0 } }
|
|
local RIM_GLOW_R = { { 0, 0 }, { 20, 1 }, { 40, 0 }, { 361, 0 }, { 390, 1 }, { 420, 0 },
|
|
{ 425, 0 }, { 425, 1 }, { 540, 1 }, { 545, 0 } }
|
|
local RIM_STICK_L = { { 65, 0 }, { 65, 1 }, { 180, 1 }, { 185, 0 } }
|
|
local RIM_STICK_R = { { 425, 0 }, { 425, 1 }, { 540, 1 }, { 545, 0 } }
|
|
local ARROW_L = { { 65, 0 }, { 65, 1 }, { 125, 0 }, { 125, 1 }, { 180, 1 }, { 185, 0 } }
|
|
local ARROW_R = { { 425, 0 }, { 425, 1 }, { 485, 0 }, { 485, 1 }, { 540, 1 }, { 545, 0 } }
|
|
local FACE_DOT = { { 181, 0 }, { 210, 1 }, { 240, 0 }, { 245, 0 }, { 245, 1 }, { 355, 1 }, { 360, 0 } }
|
|
local FACE_STICK = { { 245, 0 }, { 245, 1 }, { 355, 1 }, { 360, 0 } }
|
|
|
|
local function env(f, keys)
|
|
if f < keys[1][1] then return keys[1][2] end
|
|
for i = 2, #keys do
|
|
local a, b = keys[i - 1], keys[i]
|
|
if f < b[1] then return a[2] + (b[2] - a[2]) * (f - a[1]) / (b[1] - a[1]) end
|
|
end
|
|
return keys[#keys][2]
|
|
end
|
|
|
|
-- 19 frames up, 20 down: {y stretch, lift}
|
|
local function bounce(f, w)
|
|
local u = f - w.f
|
|
if u < 0 or u >= 40 then return 1, 0 end
|
|
local p = u < 20 and u / 19 or (40 - u) / 20
|
|
return 1 + (w.d - 1) * p, w.lift * HINT_K * p
|
|
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.hint = {}
|
|
for _, k in ipairs({ "bar", "rim", "face", "drum", "rim_glow", "rim_stick", "arrow", "face_dot", "face_stick" }) do
|
|
self.hint[k] = tex.get_texture("entry/campaign/hint_" .. k)
|
|
end
|
|
self.hint_text = {}
|
|
for _, w in ipairs(WORDS) do
|
|
if w.text and not self.hint_text[w.text] then
|
|
local str = tex.skin_text(w.text)
|
|
self.hint_text[w.text] = str ~= "" and
|
|
text.create_raw_text(str, math.floor(HINT_FONT * S), WHITE, BLACK, false, 5, 2.0) or false
|
|
end
|
|
end
|
|
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_hint(fade)
|
|
local h = self.hint
|
|
local t = self.now - self.t0
|
|
local wf, df = (t / F) % 360, (t / F) % 720
|
|
tex.draw_texture(h.bar, { fade = fade })
|
|
for _, w in ipairs(WORDS) do
|
|
local st, lift = bounce(wf, w)
|
|
if w.tex then
|
|
local hgt = 48 * HINT_K * S
|
|
tex.draw_texture(h[w.tex], { y = -hgt * (st - 1) / 2 - lift * S, y2 = hgt * (st - 1), fade = fade })
|
|
elseif self.hint_text[w.text] then
|
|
local tx = self.hint_text[w.text]
|
|
local sc = math.min(1, 122.4 * S / tx.width)
|
|
local wd, hgt = tx.width * sc, tx.height * sc * st
|
|
tx:draw({ x = w.x * S - wd / 2, y = HINT_Y * S - hgt / 2 - lift * S,
|
|
x2 = wd - tx.width, y2 = hgt - tx.height, fade = fade })
|
|
end
|
|
end
|
|
local dr, ar = DRUM_R_DX * S, ARROW_R_DX * S
|
|
tex.draw_texture(h.drum, { fade = fade })
|
|
tex.draw_texture(h.drum, { x = dr, fade = fade })
|
|
tex.draw_texture(h.rim_glow, { fade = fade * env(df, RIM_GLOW_L) })
|
|
tex.draw_texture(h.rim_glow, { mirror = "horizontal", fade = fade * env(df, RIM_GLOW_R) })
|
|
tex.draw_texture(h.rim_stick, { fade = fade * env(df, RIM_STICK_L) })
|
|
tex.draw_texture(h.rim_stick, { mirror = "horizontal", fade = fade * env(df, RIM_STICK_R) })
|
|
tex.draw_texture(h.arrow, { fade = fade * env(df, ARROW_L) })
|
|
tex.draw_texture(h.arrow, { x = ar, mirror = "horizontal", fade = fade * env(df, ARROW_R) })
|
|
tex.draw_texture(h.face_dot, { fade = fade * env(wf, FACE_DOT) })
|
|
tex.draw_texture(h.face_stick, { fade = fade * env(wf, FACE_STICK) })
|
|
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
|
|
self:draw_hint(fade)
|
|
end
|