Files
YataiDONNijiiro/Scripts/entry/entry.lua
T
TaikoZhongandClaude Fable 5.1 91529eeb53 entry: drop the 「つかわない」 caption
The cabinet's entry screen has no such caption; the cancel side is simply
the un-armed state, so the label sat as stray text under the prompts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 23:04:54 +08:00

493 lines
28 KiB
Lua

-- YataiDON-HSS override of PyTaikoGreen/entry/entry.lua: cancel text centring uses cancel_info.x2[1]/y2[1];
-- sets __hss_entry_reset for the arcade mode-select list in box.lua
Entry = {}
Entry.__index = Entry
function Entry.new()
local self = setmetatable({}, Entry)
-- EntryScreen::on_screen_start creates BoxManager (4 EntryBox) then this script;
-- flag the box list so the NEXT batch of EntryBox.new starts fresh (see box.lua).
__hss_entry_reset = true
tex.load_animations("entry")
self.bg_flicker = tex.get_animation(1, "entry")
self.side_select_fade = tex.get_animation(0, "entry")
self.bg_flicker:start()
-- ROUND 11 -- the arcade "credit wait" screen (lumen entry/entry_credit) has no
-- 「どちらでつかいますか?」 box: two pills carrying 「1人プレイ」/「2人プレイ」 on the pale
-- left half and 「太鼓をたたいてスタート!」 on the yellow right half. Strings are the
-- cabinet's own wordlist rows credit_play_1p / credit_play_2p / credit_start.
-- Outline thickness is scaled by screen_scale (1.5) in OutlinedText's ctor, so the
-- arcade's DefineEditText border 7 is passed as 5 (= 7.5 px).
-- ROUND 11b: 「つかわない」 is drawn as a small CAPTION, only while side 1 is armed.
-- The arcade credit screen has no cancel widget at all (entry_main.lua leaves the screen
-- on the 60 s timer, never on a button), but the engine's side 1 is real and reachable.
self.text_cancel = text.create_text("entry_cancel", { 255, 255, 255, 255 }, { 40, 24, 12, 255 }, false, 3, 0)
self.text_credit_p = {
-- ROUND 15 -- outline thickness 0, NOT the record's border 7. ET7 is `#000000` with
-- border 7, and the cabinet's outline colour is the renderer default (black), so the
-- border on this field is a pure 7.5-screen-px BLACK-ON-BLACK dilation: it carries no
-- colour information at all, it only fattens the glyphs. On 「1人プレイ」 that is
-- invisible (kana strokes are far apart), but at font 56 it closes every counter of a
-- Latin string and「1-Player」/「2-Player」 render as an unreadable black smear
-- (`scratchpad/r15ae/z_1player_en.png`). Deliberate deviation, documented here: the
-- fill colour is unchanged, so the label still reads exactly as the cabinet's black
-- text, and it is legible in every language this skin ships.
text.create_text("entry_credit_1p", { 0, 0, 0, 255 }, { 0, 0, 0, 255 }, false, 0, 0),
text.create_text("entry_credit_2p", { 0, 0, 0, 255 }, { 0, 0, 0, 255 }, false, 0, 0),
}
-- the message is ONE string drawn in two colourways: the arcade stacks a black-tinted
-- outline copy (ET3 via cmul (0,0,0)) under a white fill copy (ET5), and its blink
-- recolours that outline to (256,236,67). Cross-fading these two reproduces it.
-- ROUND 16 (r16-outline): `entry/entry_credit` ET3 `text_credit` carries border 7.0,
-- which is already in 1080p stage px; OutlinedText multiplies by screen_scale (1.5),
-- so it is divided out rather than rounded to 5 (which drew 7.5 px).
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.load_texture("entry/background/bg")
self.bg_tower = tex.load_texture("entry/background/tower")
self.bg_shops_center = tex.load_texture("entry/background/shops_center")
self.bg_people = tex.load_texture("entry/background/people")
self.bg_shops_left = tex.load_texture("entry/background/shops_left")
self.bg_shops_right = tex.load_texture("entry/background/shops_right")
self.bg_lights = tex.load_texture("entry/background/lights")
-- ROUND 6 — layered arcade backdrop (lumen entry/entry, entrybg_instance MC 19).
-- Arcade display list: d0 shape1 (gold sky) / d1 MC11 twinkles / d2 shape12 (street, sky
-- transparent) / d3-6 MC16 glow x4 (blend=add) / d7 shape17 (fully lit street, opaque).
-- `bg` is byte-exact shape1+shape12, so only d3-d7 have to be re-added.
self.bg_street_lit = tex.load_texture("entry/background/street_lit")
self.bg_glow = tex.load_texture("entry/background/glow")
self.bg_twinkle = tex.load_texture("entry/background/twinkle")
self.bg_t0 = tex.get_current_ms()
-- ROUND 11: the parent's nine-slice box / 1P / 2P / cancel / footer art is no longer
-- drawn (the arcade credit screen has none of it). These four are the arcade's own
-- shapes, baked by Graphics/entry/bake_credit.py; both pills are the SAME 1160x200
-- plate placed twice (texture.json carries the two placements).
self.cr_pill = tex.load_texture("entry/side_select/credit_pill")
self.cr_flash = tex.load_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
-- ROUND 12 -- opt-in arcade CREDIT WAIT interaction model. skin_config_entry.json's
-- `entry_credit_arcade` = 1 switches the engine (EntryScreen::handle_input) to the
-- cabinet's rule: the two rows are PROMPTS, both lit, no cursor, no 「つかわない」;
-- whichever SEAT hits its own don enters as that player. PyTaikoGreen leaves the key
-- out and keeps the kat-moved side cursor.
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.global_player_entry = tex.load_texture("entry/global/player_entry")
-- ROUND 18 (r18-misc) -- the top-left control guide belongs on the CREDIT WAIT
-- screen and we were not drawing it there at all.
--
-- The cabinet's own source settles it. `entry.nulm` root (sprite 54) carries
-- `controlguide_instance` at depth 28, i.e. a sibling of `entrycredit_instance`
-- (depth 23) and drawn OVER it. `script_lua/entry/entry_main.lua` CreditWait,
-- lines 957-966:
-- if this.start_guide_ == false then
-- if PlayDataManager.GameCost1 <= PlayDataManager.GetNowCoin()
-- or PlayDataManager.IsFreePlay() == true then
-- this.control_guide_:SetVisible(true)
-- this.control_guide_:Start(1)
-- and `common/ControlGuide.lua` `Start(mode)` -> `ChangeMode(1)` ->
-- `taiko_small:GotoAndStop("decide")`. So on a free-play cabinet the guide is up
-- from the first frame of the credit screen, in its **decide** state -- exactly the
-- second half of the loop `global/indicator/background` already bakes (arcade frames
-- 420..649; see Scripts/global/indicator.lua). It is torn down again at
-- ModeSelectFinishActionWait (l.1233) and at each TaikoSelect start (l.481, l.797).
--
-- Engine-side, `Indicator` is owned by `EntryPlayer`, so it only ever draws once a
-- seat has ENTERED; before that `players` is empty and nothing draws the panel.
-- Drawing it from here is the whole fix -- same texture, same (0,0), same frames.
--
-- NOT ported: `ControlGuide.Update` slides the panel to screen centre after 3 s with
-- no input (`to_center`) and back on the next hit (`to_default`). The baked loop has
-- no centre variant, so the guide stays parked top-left.
self.guide = tex.load_texture("global/indicator/background")
self.guide_t0 = self.cr_t0
self.any_joined = false
return self
end
-- Same split as Scripts/global/indicator.lua: the bake is two guides back to back at
-- one cell per 2 arcade frames, and cells GUIDE_SPLIT..GUIDE_END-1 are the 「決定」 half.
local GUIDE_STEP = 2
local GUIDE_SPLIT = math.floor(420 / GUIDE_STEP)
local GUIDE_END = math.floor(650 / GUIDE_STEP)
-- NOTE: do NOT gate this on `tex.get_texture_info("indicator","background").frame_count`.
-- The bake is a `crop` list in texture.json, not a frame-count sheet, and
-- `get_texture_info` only reports a real count for `FramedTexture` -- for this one it
-- returns 1, so a `frame_count >= 325` guard silently drew nothing. `indicator.lua`
-- reads the same value into `self.frame_count` and never uses it, which is why the
-- discrepancy had not been noticed. `draw_texture{frame=N}` indexes the crop list fine.
function Entry:draw_control_guide()
if not self.guide then return end
-- One frame of overlap with EntryPlayer's own Indicator on the join frame is
-- possible (draw_footer publishes the join flags after this runs); both draws are
-- the same cell at the same place, so it is invisible.
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)
-- ROUND 11: a frame counter is the only way Lua can see that the engine has LEFT
-- EntryState::SELECT_SIDE (draw_side_select_buttons simply stops being called), which
-- is what triggers the arcade's `choose` flash + fade_out. update() runs once per
-- frame, before draw().
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
-- credit_instance `fade_in` (sprite 11 f5..f15) restarts, and so does the row's attract
-- loop; any pending decide overlay is dropped (the 2P join re-enters SELECT_SIDE).
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
-- ROUND 8 -- the whole `entrybg_instance` rig (lumen `entry/entry.nulm` MC 19) is now
-- SAMPLED out of `Scripts/anim/entry_bg.lua` instead of the closed forms round 6 fitted
-- to it. See ANIM_PIPELINE.md. Tracks, in the cabinet's own display order:
-- d1 twinkles `#11@1/#5@0/#3@0` (f0), `#11@1/#8@1/#6@0` (f90),
-- `#11@1/#9@2/#3@0` (f120), `#11@1/#10@3/#6@0` (f219)
-- d3-d6 glows `#16@3/#14@0` (all four are the same 120-frame curve, in phase)
-- d7 lit street `#17@7`
-- The table covers a whole 360-frame (6 s) cycle, so every channel is just read at
-- `f % 360` -- no per-channel modulo bookkeeping.
--
-- What the export changed: the two SMALL twinkles were drawn 25 % too small at the end of
-- their burst. Round 6 modelled every burst as one envelope times a per-burst multiplier
-- (0.5 for the small pair), which gives 0.25 -> 0.5 -> 0.75. The cabinet runs them
-- 0.25 -> 0.5 -> 1.0: the multiplier applies to the GROW phase only, and the expand phase
-- adds the same absolute +0.5 to big and small alike. Alpha, the big bursts, the glow
-- triangle and the street blink were all confirmed exact.
-- ROUND 8: fail soft. `pcall` the require and use `anim.safe`, which hands back a NULL
-- clip when a generated table is missing or malformed, so every reader below falls
-- through to its `or <constant>` default instead of taking the screen down.
local _ea_ok, entry_anim = pcall(require, "anim/sampler")
if not _ea_ok then entry_anim = { safe = function()
-- sampler.lua itself is missing: hand back the same NULL clip shape it would
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 EBG = entry_anim.safe("anim/entry_bg")
-- ROUND 11 -- entry/entry_credit.nulm, exported by scratchpad/anim_export.py:
-- CR = sprite 10 (one player row): labels init 0 / start 5 / choose 125. Tracks
-- `text_message_instance` (the white fill copy), `text_message_instance_2` (the
-- outline copy -- fields cr,cg,cb carry its black<->yellow recolour) and `#8@4`
-- (the white decide flash, shape 8, only placed from f125).
-- CF = sprite 11 (credit_instance): fade_in f5..f15, fade_out f16..f35.
local CR = entry_anim.safe("anim/credit_row")
local CF = entry_anim.safe("anim/credit_fade")
-- Layout, straight out of the lumen (stage 1920x1080; the root places credit_instance at
-- (960,432) and the row sprite is placed twice, at ty +72 and +248):
local CREDIT_CX = 960
local ROW_CY = { 432 + 72, 432 + 248 } -- 504 / 680
-- DefineEditText records (payload map: [2] text sym, [4] colour, [5] rect, byte 32 align,
-- f32@44 size, f32@60 outline -- Graphics/result/MAPPING.md ROUND 6):
-- ET7 text_player rect (-2,-2,306,94) placed at (-448,-62.5), align 0 (LEFT), size 56, border 7
-- ET3 message rect (-2,-2,638,94) placed at (-137,0)+(2,-62.5), align 2 (CENTRE), size 56, border 7
-- The cabinet text rule is pen.y = box.top + (box.h - lineHeight)/2 + border, so the INK
-- CENTRE is box.top + box.h/2 + border -- independent of which font raster is used:
local LABEL_INK_X = CREDIT_CX - 443 -- box.left(-450) + border(7), left-aligned
local MSG_INK_X = CREDIT_CX + 190 -- box.left(-137) + border(7) + box.w/2(320)
local TEXT_INK_DY = -9.5 -- box.top(-64.5) + box.h/2(48) + border(7)
local PAD5 = 9 -- OutlinedText pad for thickness 5: (int)(5*1.5)+2
-- ROUND 15: the two player labels are now built with thickness 0 (see `text_credit_p` above),
-- so their texture pad is (int)(0*1.5)+2 = 2, not 9. Using PAD5 here would shift the ink 7 px
-- right of the cabinet's pen at box.left + border.
local PAD_LABEL = 2
-- The cancel widget is NOT an arcade element (MAPPING.md ROUND 11): the same pill at 0.5,
-- centred under the two rows.
local CANCEL_CX, CANCEL_CY = 960, 838
local ROW_LOOP_F0, ROW_LOOP_LEN = 5, 120 -- the row timeline loops `start`..f124
-- NOT arcade: the cabinet lights both rows equally because it has no cursor (each row is
-- armed for that side's own drum). The engine DOES have a cursor, so the row that is not
-- armed is dimmed and the armed one carries a pulse of the arcade white `choose` overlay.
local DIM_UNSELECTED = 0.72
local DECIDE_FLASH_F = 24 -- `choose` blink f125..f149
local EBG_LEN = math.max(1, EBG:last() - EBG:first() + 1) -- 360 frames = 6 s
-- `entrybg_instance` is 30 frames and STOPS at f29 (see draw_background); after that the
-- lit-street layer is gone for good. 19 is where its alpha first reaches 0.
local BG_LIT_END = 19
-- {json index, texture frame (0 = shape3, 1 = shape6), track}
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) % EBG_LEN -- arcade frames @ 60 fps
tex.draw_texture(self.bg_bg)
-- d1 twinkles (arcade draws them under the street; the street is baked into `bg`, so
-- they sit on top here -- only visible where a burst overlaps stall art). A burst is
-- only placed on the frames it is alive, so a track that has no row at `f` returns its
-- clamped end value (alpha 0) and draws nothing.
if self.bg_twinkle then
for i = 1, #ENTRY_TWINKLES do
local tw = ENTRY_TWINKLES[i]
local s, a = EBG: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
-- d3-d6 warm glows. The arcade blends these additively; each patch is pre-composited
-- (bg + glow, clamped) so a plain alpha fade reproduces `bg + a*glow` exactly.
if self.bg_glow then
local ga = EBG:get("#16@3/#14@0", f, "a") or 1.0
for i = 0, 3 do
tex.draw_texture(self.bg_glow, { index = i, frame = i, fade = ga })
end
end
-- d7 lit street: a ONE-SHOT "blur in", NOT a loop. `entrybg_instance` (MC 19) is only
-- 30 frames long and carries DoAction index 0 at BOTH f4 and f29. f4 must be Stop() --
-- `entry_main.lua` StartBGIn is what starts the clip (`this.bg_:GotoAndPlay("start")`,
-- label `start` = f5), so the clip is parked at f4 until then -- and f29 carries the
-- SAME action index, therefore f29 is Stop() too. So the cabinet plays f5..f29 once and
-- holds: shape17 (the SHARP, fully-lit street, lapvar 1508) fades 1 -> 0 over f5..f19,
-- leaving shape12 (the DEPTH-OF-FIELD BLURRED street, lapvar 48) as the steady state.
-- That matches every arcade reference capture ("festival street, blurred").
-- `Scripts/anim/entry_bg.lua` was exported with `--range 0,359`, which wrapped this
-- 30-frame clip twelve times and turned the one-shot into a 2 Hz sharp/blurry strobe
-- (the user's 「背景模糊亂跳」). Only THIS channel wraps: the twinkle MCs (5/8/9/10)
-- really are 360 frames and the glow MC (16) really is 120, so `f` stays modulo for them.
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 = EBG: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
-- ROUND 11 -- the arcade credit screen has no panel behind the buttons: the pills sit
-- straight on the festival street. The parent nine-slice box and its
-- japanese caption are therefore not drawn at all.
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 = CR:get("text_message_instance", f, "a") or 1
local _, cr = CR: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
-- ROUND 26 (r26-entrytextfade): `msg`/`msg_hl` are each a whole baked
-- OutlinedText -- white fill AND its outline in ONE texture (OutlinedText
-- bakes them together; text.cpp's draw() only has a single `fade` alpha for
-- the whole quad, there is no separate fill/outline alpha). The old code
-- faded BOTH copies down independently ((1-yellow) and yellow), so around
-- the blink midpoint BOTH copies were simultaneously translucent: the white
-- fill of each stopped being opaque, the pill's yellow bled through it, and
-- the outline ring (itself fading through a yellow close to the pill's own
-- colour) lost all contrast -- exactly the reported "白字跟外框fade時機不同,
-- 出現純白字" flash (captured live at scratchpad/r26etf/before/f20_9530ms.png,
-- ~9.5s into the credit-wait loop, i.e. inside the `choose` blink f64..f95).
-- Fix: draw the black-outline copy ALWAYS fully opaque as the base (fade =
-- a*ma, no `(1-yellow)` term), then cross-dissolve the yellow-outline copy
-- ON TOP of it at fade = a*ma*yellow. Back-to-front opaque compositing means
-- the result is fully opaque at every point of the blend -- the fill stays
-- solid white throughout (yellow copy's white fill lands on the already-
-- opaque white fill beneath, a no-op), and the outline smoothly darkens from
-- black to yellow while never dropping below full alpha, so it is never
-- washed out by the pill background behind it.
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
-- The whole credit_instance: both rows + the cancel widget, at `a` (its fade_in/out alpha).
-- `sel` is the highlighted row: 1 / 2 / 0 = the cancel widget / nil = nothing (the decide
-- overlay, where the selection has already been consumed). `flash_row`/`flash_a` drive the
-- `choose` overlay.
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)
-- Arcade: the cabinet runs the row attract loop (`start`..f124, the yellow-outline
-- blink at f64..f95) on BOTH rows in CreditWait, because neither is "armed". The
-- cursor model only animated the armed row and froze the other at f5.
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
-- the selected pill also carries a gentle version of the arcade white `choose`
-- overlay, standing in for a cursor the cabinet does not need (there, a row is
-- picked by hitting that side's own drum).
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
-- The cabinet never shows a 「つかわない」 caption on this screen (the cancel side is
-- just the un-armed state), so nothing is drawn for sel == 0.
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
-- The engine thinks in SIDES, not in a player COUNT: `side` is 0 = left/1P, 1 = cancel
-- (back to title, or back to mode select when a 2nd player is joining), 2 = right/2P,
-- moved with kat and confirmed with don. The arcade screen has no cursor at all -- each
-- row is armed for that side's own drum -- so side 0/2 map onto the upper/lower row and
-- side 1 onto the cancel widget. See Graphics/entry/MAPPING.md ROUND 11.
function Entry:draw_side_select_buttons(side)
local now = tex.get_current_ms()
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
-- credit_instance fade_in: sprite 11, f5..f15 (10 frames), clamped after
local fi = math.min(15, 5 + (now - self.cr_t0) * 0.06)
local a = CF:get("player1_text_instance", fi, "a") or 1.0
-- Arcade: no cursor at all -- `sel = nil` keeps BOTH rows at full alpha, draws no
-- selection pulse and no 「つかわない」 caption (the cabinet has neither).
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()
-- entry_main.lua only makes `credit_side_[n]` visible in ModeSelectStartWait, i.e. AFTER
-- the credit screen is finished; coin_overlay.lua reads this to keep the 2P invite cloud
-- off the credit screen. EntryScreen::draw runs the screen scripts before the overlay.
YD_ENTRY_CREDIT_MS = now
end
-- The parent's floor band starts at y=862 (575*1.5) and is drawn AFTER the mode-select /
-- costume menu, so it would cover the arcade costume panel bottom (y<=957) and the
-- えらぶ/決定 drum hint (833..921). Squeeze it to start at y=940 (nameplate / フリープレイ
-- still sit on it). Values are screen px (1080p).
-- The arcade entry screen has no floor band: the nameplate and フリープレイ sit
-- straight on the street background. Nothing to draw.
-- ROUND 6 (2P): the engine has no "two players are in" flag Lua can read (entry.cpp resets
-- global_data.player_num to P1 when the 2nd player joins), but it does pass the two join flags
-- here, and EntryScreen::draw calls draw_footer BEFORE coin_overlay.draw() - so publish them.
function Entry:draw_footer(p1_joined, p2_joined)
YD_ENTRY_BOTH_JOINED = (p1_joined == true and p2_joined == true)
-- ROUND 18: once a seat is in, EntryPlayer's own Indicator draws the guide, so the
-- credit screen must stop drawing its own copy (see draw_control_guide).
self.any_joined = (p1_joined == true or p2_joined == true)
end
function Entry:draw_player_entry()
tex.draw_texture(self.global_player_entry)
self:draw_credit_decide()
end
-- ROUND 11 -- the arcade decide beat. `entry_main.lua` runs CreditWait:Decide(1) ->
-- CreditDecideAction (the row plays `choose`, f125..f149: a white overlay blinking on an
-- 8-frame triangle, peak 205/256) -> CreditWait:Finish() -> `fade_out` (f16..f35) -> only
-- THEN ModeSelectStart. Our engine flips EntryState to SELECT_MODE on the same frame the
-- don lands, so the beat has to be played out from here: draw_player_entry() is called in
-- EVERY entry state (EntryScreen::draw), and the mode boxes stay invisible until each
-- player's cloud animation has finished (~1.2 s), which is longer than the 750 ms this
-- takes. The transition is detected by draw_side_select_buttons() no longer being called
-- (Entry:update bumps cr_frame once per frame).
-- ROUND 12 -- EntryScreen::join_player names the row the cabinet's CreditWait:Decide(playerId)
-- would flash (`credit_wait.tlb` plays `choose` on `text_mc_[playerId]`, i.e. the row of the
-- player who hit -- not always row 1). Called on the frame the seat enters, while the engine
-- is still in SELECT_SIDE, so draw_credit_decide() below picks it up next frame.
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 = CR: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 = CF: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