Files
PyTaikoGreen/Scripts/entry/bncoin_popup.lua
T
2026-11-02 20:35:36 -05:00

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