253 lines
10 KiB
Lua
253 lines
10 KiB
Lua
BanaCoinPopup = {}
|
|
BanaCoinPopup.__index = BanaCoinPopup
|
|
|
|
local S = tex.get_screen_scale()
|
|
local F = 1000 / 60
|
|
local FADE_MS = 10 * F
|
|
local NOTICE_MS = 210 * F
|
|
local TOUCH_MS = 210 * F + 15000
|
|
local CONFIRM_S = 20
|
|
local LEFT, RIGHT, DECIDE = 1, 2, 3
|
|
local USE, NOT_USE = 1, 2
|
|
|
|
local WHITE, BLACK = { 255, 255, 255, 255 }, { 0, 0, 0, 255 }
|
|
local NOTICE_FONT, NOTICE_PITCH, NOTICE_MAX_W = 48, 52.5, 620
|
|
|
|
-- warning icon offset from its rest (the shortage spot), text left x and line centres
|
|
local NOTICES = {
|
|
shortage = { se = "bncoin_notenough", warn = { 0, 0 }, key = "bncoin_shortage", x = 429, y = 249.85,
|
|
balance = { dy = 0, cx = 722, cy = 380 } },
|
|
remain = { se = "bncoin_popup", balance = { dy = -30, cx = 722, cy = 350 }, note = true },
|
|
onlyplay = { se = "bncoin_onlysingle", warn = { -47, 107.65 }, key = "bncoin_onlyplay", x = 379, y = 360 },
|
|
cantuse = { se = "bncoin_offline", warn = { 23, 107.65 }, key = "bncoin_cantuse", x = 449, y = 357.25 },
|
|
cantplay = { se = "bncoin_na", warn = { 23, 107.15 }, key = "bncoin_cantplay", x = 449, y = 359 },
|
|
firstplay = { se = "bncoin_na", warn = { -56, 107.15 }, key = "bncoin_firstplay", x = 369, y = 357.25 },
|
|
cantshop = { se = "bncoin_na", ad = true },
|
|
touch = {},
|
|
}
|
|
|
|
-- digit slot centres from the number's right edge; slot 4 is the comma
|
|
local SLOTS = { 0, -42, -84, -114, -144.5, -186.5, -228.5 }
|
|
local COMMA = 10
|
|
|
|
local CONFIRM_CX, BUTTON_DX, BUTTON_Y = 640, 127.5, 440
|
|
local BORDER_MS, BORDER_ON_MS = 1000, 683
|
|
local TIMER_DX, TIMER_DY, TIMER_CX = -66.8, -112.6, 1006
|
|
local DIGIT_PITCH, DIGIT_SCALE = 25 * S, 0.64
|
|
|
|
local function clamp01(v) return math.max(0.0, math.min(1.0, v)) end
|
|
|
|
local function lines(key, size, outline)
|
|
local out = {}
|
|
for line in tex.skin_text(key):gmatch("[^\n]+") do
|
|
out[#out + 1] = text.create_raw_text(line, math.floor(size * S), WHITE, BLACK, false, outline or 5, 2.0)
|
|
end
|
|
return out
|
|
end
|
|
|
|
-- align: "left" from x, "right" to x, else centred on x
|
|
local function draw_lines(list, x, cy, pitch, max_w, fade, align)
|
|
for i, t in ipairs(list) do
|
|
local scale = math.min(1, max_w * S / t.width)
|
|
local w = t.width * scale
|
|
local left = align == "left" and x * S or (align == "right" and x * S - w or x * S - w / 2)
|
|
local y = (cy + (i - (#list + 1) / 2) * pitch) * S
|
|
t:draw({ x = left, y = y - t.height * scale / 2, scale = scale, fade = fade })
|
|
end
|
|
end
|
|
|
|
function BanaCoinPopup.new(kind, balance, cost)
|
|
local self = setmetatable({}, BanaCoinPopup)
|
|
self.tex = {}
|
|
for _, key in ipairs({ "notice_box", "warn", "balance_label", "remain_note", "touch_card", "cost_line",
|
|
"cancel_bar", "hint_drum", "hint_rim", "hint_arrow", "digits", "banapass_ad",
|
|
"confirm_box" }) do
|
|
self.tex[key] = tex.get_texture("entry/bncoin/" .. key)
|
|
end
|
|
for _, key in ipairs({ "button_red", "button_yellow", "button_outline", "arrow" }) do
|
|
self.tex[key] = tex.get_texture("entry/name_entry/" .. key)
|
|
end
|
|
for _, key in ipairs({ "timer_glow", "timer", "digits" }) do
|
|
self.tex["timer_" .. key] = tex.get_texture("entry/campaign/mac_" .. key)
|
|
end
|
|
self.text = {
|
|
unit = lines("bncoin_unit", 33), consume = lines("bncoin_consume", 33), cancel = lines("bncoin_cancel", 29, 0),
|
|
question = lines("bncoin_confirm_question", 32),
|
|
use = lines("bncoin_confirm_use", 31), not_use = lines("bncoin_confirm_not_use", 31),
|
|
}
|
|
for name, n in pairs(NOTICES) do
|
|
if n.key then self.text[name] = lines(n.key, NOTICE_FONT) end
|
|
end
|
|
self.balance, self.cost = balance, cost
|
|
self.result = "cancel"
|
|
self.now = tex.get_current_ms()
|
|
self:enter(kind == "practice" and "touch" or kind)
|
|
return self
|
|
end
|
|
|
|
function BanaCoinPopup:enter(name)
|
|
self.stage = { name = name, t0 = self.now, sel = USE }
|
|
local se = NOTICES[name] and NOTICES[name].se
|
|
if se and audio.has_sound(se) then audio.play_sound(se, "sound") end
|
|
end
|
|
|
|
-- fade the current stage out, then show `next` (nil: finished)
|
|
function BanaCoinPopup:leave(next)
|
|
if self.stage.out_t0 then return end
|
|
self.stage.out_t0, self.stage.next = self.now, next or false
|
|
end
|
|
|
|
function BanaCoinPopup:pay()
|
|
self.result = "pay"
|
|
self.balance = self.balance - self.cost
|
|
self:leave("remain")
|
|
end
|
|
|
|
function BanaCoinPopup:elapsed() return self.now - self.stage.t0 end
|
|
|
|
function BanaCoinPopup:seconds_left()
|
|
return math.max(0, CONFIRM_S - math.floor(math.max(0, self:elapsed() - FADE_MS) / 1000))
|
|
end
|
|
|
|
function BanaCoinPopup:update(current_ms)
|
|
self.now = current_ms
|
|
local st = self.stage
|
|
if self.done then return end
|
|
if st.out_t0 then
|
|
if self.now - st.out_t0 >= FADE_MS then
|
|
if st.next then self:enter(st.next) else self.done = true end
|
|
end
|
|
return
|
|
end
|
|
local el = self:elapsed()
|
|
if st.name == "touch" then
|
|
if el >= TOUCH_MS then self:leave("confirm") end
|
|
elseif st.name == "confirm" then
|
|
if self:seconds_left() == 0 then self:input(DECIDE) end
|
|
elseif el >= NOTICE_MS - FADE_MS then
|
|
self:leave(nil)
|
|
end
|
|
end
|
|
|
|
function BanaCoinPopup:input(action)
|
|
local st = self.stage
|
|
if self.done or st.out_t0 or self:elapsed() < FADE_MS then return false end
|
|
if st.name == "touch" then
|
|
if action == DECIDE then self:pay() else self:leave("confirm") end
|
|
return true
|
|
elseif st.name == "confirm" then
|
|
if action == DECIDE then
|
|
if st.sel == USE then self:pay() else self:leave(nil) end
|
|
return true
|
|
end
|
|
local target = action == LEFT and USE or NOT_USE
|
|
if target == st.sel then return false end
|
|
st.sel, st.border_t0 = target, self.now
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
function BanaCoinPopup:is_done() return self.done == true end
|
|
function BanaCoinPopup:get_result() return self.result end
|
|
|
|
function BanaCoinPopup:fade()
|
|
local st = self.stage
|
|
if st.out_t0 then return 1 - clamp01((self.now - st.out_t0) / FADE_MS) end
|
|
return clamp01(self:elapsed() / FADE_MS)
|
|
end
|
|
|
|
function BanaCoinPopup:draw_number(n, cx, cy, fade)
|
|
local s = tostring(math.max(0, math.floor(n)))
|
|
local slot = 1
|
|
for i = #s, 1, -1 do
|
|
if slot == 4 then
|
|
tex.draw_texture(self.tex.digits, { frame = COMMA, x = (cx + SLOTS[slot] - 25) * S, y = (cy - 39) * S,
|
|
fade = fade })
|
|
slot = slot + 1
|
|
end
|
|
tex.draw_texture(self.tex.digits, { frame = tonumber(s:sub(i, i)), x = (cx + SLOTS[slot] - 25) * S,
|
|
y = (cy - 39) * S, fade = fade })
|
|
slot = slot + 1
|
|
end
|
|
end
|
|
|
|
function BanaCoinPopup:draw_hint(fade)
|
|
local t = self.tex
|
|
local f = (self:elapsed() / F) % 120
|
|
local rim = f < 30 and f / 30 or (f < 60 and 0.965 * (1 - (f - 30) / 30) or 0)
|
|
tex.draw_texture(t.hint_drum, { fade = fade })
|
|
tex.draw_texture(t.hint_rim, { fade = fade * rim })
|
|
tex.draw_texture(t.hint_rim, { x = 24 * S, mirror = "horizontal", fade = fade * rim })
|
|
if f < 60 then
|
|
tex.draw_texture(t.hint_arrow, { x = -7 * S * f / 60, fade = fade * (1 - f / 60) })
|
|
end
|
|
end
|
|
|
|
function BanaCoinPopup:draw_notice(name, fade)
|
|
local t, n = self.tex, NOTICES[name]
|
|
tex.draw_texture(t.notice_box, { fade = fade })
|
|
if n.warn then
|
|
tex.draw_texture(t.warn, { x = n.warn[1] * S, y = n.warn[2] * S, fade = fade })
|
|
draw_lines(self.text[name], n.x, n.y, NOTICE_PITCH, NOTICE_MAX_W, fade, "left")
|
|
end
|
|
if n.balance then
|
|
tex.draw_texture(t.balance_label, { y = n.balance.dy * S, fade = fade })
|
|
self:draw_number(self.balance, n.balance.cx, n.balance.cy, fade)
|
|
end
|
|
if n.note then tex.draw_texture(t.remain_note, { fade = fade }) end
|
|
if n.ad then tex.draw_texture(t.banapass_ad, { fade = fade }) end
|
|
if name == "touch" then
|
|
self:draw_number(self.cost, 510.35, 250, fade)
|
|
tex.draw_texture(t.cost_line, { fade = fade })
|
|
draw_lines(self.text.unit, 690, 262.35, 0, 300, fade, "right")
|
|
draw_lines(self.text.consume, 708, 262.35, 0, 300, fade, "left")
|
|
tex.draw_texture(t.touch_card, { fade = fade })
|
|
tex.draw_texture(t.cancel_bar, { fade = fade })
|
|
draw_lines(self.text.cancel, 682.85, 480.3, 0, 300, fade)
|
|
self:draw_hint(fade)
|
|
end
|
|
end
|
|
|
|
function BanaCoinPopup:draw_confirm(fade)
|
|
local t, st = self.tex, self.stage
|
|
tex.draw_texture(t.confirm_box, { fade = fade })
|
|
draw_lines(self.text.question, CONFIRM_CX, 170, 0, 700, fade)
|
|
local ready = self:elapsed() >= FADE_MS and not st.out_t0
|
|
local border_t0 = st.border_t0 or (st.t0 + FADE_MS)
|
|
for i = 1, 2 do
|
|
local cx = CONFIRM_CX + (i == USE and -BUTTON_DX or BUTTON_DX)
|
|
local on = i == st.sel
|
|
tex.draw_texture(on and t.button_yellow or t.button_red,
|
|
{ x = (cx - 128) * S, y = (BUTTON_Y - 40) * S, fade = fade })
|
|
if on and ready and (self.now - border_t0) % BORDER_MS < BORDER_ON_MS then
|
|
tex.draw_texture(t.button_outline, { x = (cx - 128) * S, y = (BUTTON_Y - 40) * S, fade = fade })
|
|
end
|
|
draw_lines(i == USE and self.text.use or self.text.not_use, cx, BUTTON_Y, 0, 230, fade)
|
|
end
|
|
local ph = (self:elapsed() % 1000) / 1000
|
|
local a = ph < 0.5 and 1.0 or (0.965 * (1 - (ph - 0.5) * 2))
|
|
local ax = CONFIRM_CX + (st.sel == USE and (8 + 10 * ph) or -(13 + 10 * ph))
|
|
tex.draw_texture(t.arrow, { x = (ax - 24) * S, y = (BUTTON_Y - 24) * S, fade = fade * a,
|
|
mirror = st.sel == USE and "horizontal" or nil })
|
|
|
|
tex.draw_texture(t.timer_timer_glow, { x = TIMER_DX * S, y = TIMER_DY * S, scale = 0.8, fade = fade * 0.3 })
|
|
tex.draw_texture(t.timer_timer, { x = TIMER_DX * S, y = TIMER_DY * S, scale = 0.8, fade = fade })
|
|
local s = tostring(self:seconds_left())
|
|
local left = (TIMER_CX + TIMER_DX) * S - (#s * DIGIT_PITCH) / 2 - (48 * S * DIGIT_SCALE - DIGIT_PITCH) / 2
|
|
for i = 1, #s do
|
|
tex.draw_texture(t.timer_digits, { frame = tonumber(s:sub(i, i)), x = left + (i - 1) * DIGIT_PITCH,
|
|
y = TIMER_DY * S, scale = DIGIT_SCALE, fade = fade })
|
|
end
|
|
end
|
|
|
|
function BanaCoinPopup:draw()
|
|
if self.done then return end
|
|
local fade = self:fade()
|
|
if self.stage.name == "confirm" then
|
|
self:draw_confirm(fade)
|
|
else
|
|
self:draw_notice(self.stage.name, fade)
|
|
end
|
|
end
|