Files
YataiDONNijiiro/Scripts/entry/entry.lua
T
2026-10-01 14:47:49 -04:00

276 lines
10 KiB
Lua

Entry = {}
Entry.__index = Entry
function Entry.new()
local self = setmetatable({}, Entry)
__hss_entry_reset = true
self.bg_flicker = tex.get_animation(1, "entry")
self.side_select_fade = tex.get_animation(0, "entry")
self.bg_flicker:start()
self.text_cancel = text.create_text("entry_cancel", { 255, 255, 255, 255 }, { 40, 24, 12, 255 }, false, 3, 0)
self.text_credit_p = {
text.create_text("entry_credit_1p", { 0, 0, 0, 255 }, { 255, 255, 255, 255 }, false, 7.0 / tex.get_screen_scale(), 0),
text.create_text("entry_credit_2p", { 0, 0, 0, 255 }, { 255, 255, 255, 255 }, false, 7.0 / tex.get_screen_scale(), 0),
}
local CREDIT_BORDER = 7.0 / tex.get_screen_scale()
self.text_credit_msg = text.create_text("entry_credit_start", { 255, 255, 255, 255 }, { 0, 0, 0, 255 }, false, CREDIT_BORDER, 0)
self.text_credit_msg_hl = text.create_text("entry_credit_start", { 255, 255, 255, 255 }, { 255, 236, 67, 255 }, false, CREDIT_BORDER, 0)
self.bg_bg = tex.get_texture("entry/background/bg")
self.bg_tower = tex.get_texture("entry/background/tower")
self.bg_shops_center = tex.get_texture("entry/background/shops_center")
self.bg_people = tex.get_texture("entry/background/people")
self.bg_shops_left = tex.get_texture("entry/background/shops_left")
self.bg_shops_right = tex.get_texture("entry/background/shops_right")
self.bg_lights = tex.get_texture("entry/background/lights")
self.bg_street_lit = tex.get_texture("entry/background/street_lit")
self.bg_glow = tex.get_texture("entry/background/glow")
self.bg_twinkle = tex.get_texture("entry/background/twinkle")
self.bg_t0 = tex.get_current_ms()
self.cr_pill = tex.get_texture("entry/side_select/credit_pill")
self.cr_flash = tex.get_texture("entry/side_select/credit_flash")
self.cr_t0 = tex.get_current_ms()
self.cr_blink_t0 = self.cr_t0
self.cr_side = 1
self.cr_frame = 0
self.cr_seen_frame = nil
self.cr_decide_t0 = nil
self.cr_decide_row = nil
local arcade_cfg = tex.get_skin_config("entry_credit_arcade")
self.cr_arcade = (arcade_cfg ~= nil and (arcade_cfg.x or 0) >= 1)
self.cr_pending_decide = nil
self.guide = tex.get_texture("global/indicator/background")
self.guide_t0 = self.cr_t0
self.any_joined = false
return self
end
local GUIDE_STEP = 2
local GUIDE_SPLIT = math.floor(420 / GUIDE_STEP)
local GUIDE_END = math.floor(650 / GUIDE_STEP)
function Entry:draw_control_guide()
if not self.guide then return end
if self.any_joined then return end
local n = GUIDE_END - GUIDE_SPLIT
local f = GUIDE_SPLIT + math.floor((tex.get_current_ms() - self.guide_t0) * 0.06 / GUIDE_STEP) % n
tex.draw_texture(self.guide, { frame = f, x = 0, y = 0 })
end
function Entry:update(current_ms)
self.bg_flicker:update(current_ms)
self.side_select_fade:update(current_ms)
self.cr_frame = (self.cr_frame or 0) + 1
end
function Entry:start_side_select()
self.side_select_fade:start()
self:restart_credit()
end
function Entry:restart_side_select()
self.side_select_fade:restart()
self:restart_credit()
end
function Entry:restart_credit()
local now = tex.get_current_ms()
self.cr_t0 = now
self.cr_blink_t0 = now
self.cr_side = 1
self.cr_decide_t0 = nil
self.cr_decide_row = nil
self.cr_seen_frame = nil
self.cr_pending_decide = nil
end
function Entry:get_side_select_fade()
return self.side_select_fade.attribute
end
local ea_ok, entry_anim = pcall(require, "anim/sampler")
if not ea_ok then entry_anim = { safe = function()
local n = {}
n.at = function() return nil end; n.get = function() return nil end
n.label = function() return nil end; n.has = function() return false end
n.first = function() return 0 end; n.last = function() return 0 end
return n
end } end
local entry_bg = entry_anim.safe("anim/entry_bg")
local credit_row = entry_anim.safe("anim/credit_row")
local credit_fade = entry_anim.safe("anim/credit_fade")
local CREDIT_CX = 960
local ROW_CY = { 432 + 72, 432 + 248 }
local LABEL_INK_X = CREDIT_CX - 443
local MSG_INK_X = CREDIT_CX + 190
local TEXT_INK_DY = 0.5
local PAD5 = 9
local PAD_LABEL = 2
local CANCEL_CX, CANCEL_CY = 960, 838
local ROW_LOOP_F0, ROW_LOOP_LEN = 5, 120
local DIM_UNSELECTED = 0.72
local DECIDE_FLASH_F = 24
local entry_bg_len = math.max(1, entry_bg:last() - entry_bg:first() + 1)
local BG_LIT_END = 19
local ENTRY_TWINKLES = {
{ 0, 0, "#11@1/#5@0/#3@0" },
{ 1, 0, "#11@1/#9@2/#3@0" },
{ 2, 1, "#11@1/#8@1/#6@0" },
{ 3, 1, "#11@1/#10@3/#6@0" },
}
function Entry:draw_background()
local f = ((tex.get_current_ms() - self.bg_t0) * 0.06) % entry_bg_len -- arcade frames @ 60 fps
tex.draw_texture(self.bg_bg)
if self.bg_twinkle then
for i = 1, #ENTRY_TWINKLES do
local tw = ENTRY_TWINKLES[i]
local s, a = entry_bg:at(tw[3], f)
if a and a > 0.002 then
tex.draw_texture(self.bg_twinkle,
{ index = tw[1], frame = tw[2], center = true, scale = s, fade = a })
end
end
end
if self.bg_glow then
local ga = entry_bg:get("#16@3/#14@0", f, "a") or 1.0
for i = 0, 1 do
tex.draw_texture(self.bg_glow, { index = i, frame = i, fade = ga })
end
end
if self.bg_street_lit then
local lf = (tex.get_current_ms() - self.bg_t0) * 0.06 -- monotonic, NOT wrapped
if lf < BG_LIT_END then
local la = entry_bg:get("#17@7", lf, "a") or 0.0
if la > 0.002 then tex.draw_texture(self.bg_street_lit, { fade = la }) end
end
end
end
function Entry:draw_side_select()
end
-- One player row. `row` is 1 (upper, 1P) or 2 (lower, 2P); `f` is the row timeline frame.
function Entry:draw_credit_row(row, f, a)
if a <= 0.002 then return end
tex.draw_texture(self.cr_pill, { index = row - 1, fade = a })
local cy = ROW_CY[row]
local lbl = self.text_credit_p[row]
if lbl then
lbl:draw({ x = LABEL_INK_X - PAD_LABEL, y = cy + TEXT_INK_DY - lbl.height / 2, fade = a })
end
-- fill alpha and the outline copy colour multiply, both read from the export
local ma = credit_row:get("text_message_instance", f, "a") or 1
local _, cr = credit_row:at("text_message_instance_2", f)
local yellow = (cr and cr / 256.0) or 0.0 -- 0 = black outline, 1 = (256,236,67)
local msg = self.text_credit_msg
if msg and ma > 0.002 then
local x = MSG_INK_X - msg.width / 2
local y = cy + TEXT_INK_DY - msg.height / 2
msg:draw({ x = x, y = y, fade = a * ma })
if yellow > 0.001 and self.text_credit_msg_hl then
self.text_credit_msg_hl:draw({ x = x, y = y, fade = a * ma * yellow })
end
end
end
function Entry:draw_credit(a, sel, blink_f, flash_row, flash_a, loop_rows)
for row = 1, 2 do
local armed = (row == sel)
local ra = (sel == nil or sel == 0 or armed) and a or (a * DIM_UNSELECTED)
local rf = (armed or loop_rows) and blink_f or ROW_LOOP_F0
self:draw_credit_row(row, rf, ra)
end
local now = tex.get_current_ms()
if sel and sel > 0 then
local pulse = 0.08 + 0.10 * math.sin((now - self.cr_blink_t0) * 0.006)
tex.draw_texture(self.cr_flash, { index = sel - 1, fade = a * pulse })
end
if flash_row and flash_a and flash_a > 0.002 then
tex.draw_texture(self.cr_flash, { index = flash_row - 1, fade = a * flash_a })
end
end
function Entry:draw_side_select_buttons(side)
local now = tex.get_current_ms()
__hss_entry_guide = { ms = now, phase = "side" }
if side ~= self.cr_side then
self.cr_side = side
self.cr_blink_t0 = now -- a newly armed row restarts its attract cycle
end
self.cr_seen_frame = self.cr_frame
self.cr_decide_t0 = nil
local fi = math.min(15, 5 + (now - self.cr_t0) * 0.06)
local a = credit_fade:get("player1_text_instance", fi, "a") or 1.0
local sel = (not self.cr_arcade)
and ((side == 0 and 1) or (side == 2 and 2) or 0)
or nil
local blink_f = ROW_LOOP_F0 + ((now - self.cr_blink_t0) * 0.06) % ROW_LOOP_LEN
self:draw_credit(a, sel, blink_f, nil, nil, self.cr_arcade)
-- depth 28 > depth 23: the guide sits OVER the credit rows.
self:draw_control_guide()
YD_ENTRY_CREDIT_MS = now
end
function Entry:draw_footer(p1_joined, p2_joined)
YD_ENTRY_BOTH_JOINED = (p1_joined == true and p2_joined == true)
self.any_joined = (p1_joined == true or p2_joined == true)
end
function Entry:draw_player_entry()
self:draw_credit_decide()
end
function Entry:decide_side_select(side)
self.cr_pending_decide = (side == 2) and 2 or 1
end
function Entry:draw_credit_decide()
local now = tex.get_current_ms()
if self.cr_seen_frame ~= nil and self.cr_decide_t0 == nil then
if self.cr_frame > self.cr_seen_frame then
self.cr_seen_frame = nil
if self.cr_pending_decide then
-- arcade model: the engine told us which row entered
self.cr_decide_t0 = now
self.cr_decide_row = self.cr_pending_decide
self.cr_pending_decide = nil
elseif self.cr_side == 0 or self.cr_side == 2 then
-- cursor model: side 1 is 「つかわない」 -- cancelled, not decided.
self.cr_decide_t0 = now
self.cr_decide_row = (self.cr_side == 0) and 1 or 2
end
end
end
if self.cr_decide_t0 == nil then return end
local t = (now - self.cr_decide_t0) * 0.06 -- frames since decide
local flash_a = credit_row:get("#8@4", 125 + math.min(t, DECIDE_FLASH_F), "a") or 0
local fade_f = 16 + math.max(0, t - (DECIDE_FLASH_F + 1)) -- fade_out starts after
local a = credit_fade:get("player1_text_instance", math.min(fade_f, 35), "a") or 0
if a <= 0.002 then
self.cr_decide_t0 = nil
return
end
self:draw_credit(a, nil, ROW_LOOP_F0, self.cr_decide_row, flash_a)
YD_ENTRY_CREDIT_MS = now
end