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