-- YataiDON-HSS override of PyTaikoGreen/global/indicator.lua -- Arcade common/control_guide: white top-left panel with a drum; 'main/taiko_small/all' is a 650-frame -- loop at the lumen's 60 fps (10.83 s): frames 0..209 right stick + up arrow (hits @60,145), -- 210..419 left stick + down arrow (hits @270,355), 420..649 both sticks + 「決定」 pill (face flash -- 521..569). Round 4: re-baked every 2nd frame (325 cells = 30 fps, 352x276 cells, y offset 12 in -- texture.json, origin = screen (0,0) so skin_config *_indicator positions should be {0,0}) into -- global/indicator/background; the previous bake was every 5th frame (12 fps) which read as sluggish. -- Wall-clock loop length is the arcade's (elapsed ms * 60 / FRAME_STEP). The other parent textures -- (text, drum_face/kat/don, blue_arrow) are not drawn. All engine call sites use State.SELECT; -- the other states fall back to the same loop. Indicator = {} Indicator.__index = Indicator Indicator.State = { SKIP = 0, SIDE = 1, SELECT = 2, WAIT = 3, } local FRAME_STEP = 2 -- arcade frames per baked frame (bake_guide.py STEP) local ARCADE_FPS = 60 function Indicator.new(state) local self = setmetatable({}, Indicator) self.state = state self.background = tex.load_texture("global/indicator/background") self.frame_count = tex.get_texture_info("indicator", "background").frame_count self.t0 = tex.get_current_ms() return self end function Indicator:update(current_ms) end -- The baked loop is two guides back to back: cells 0..SPLIT-1 (arcade frames 0..419) = rim hit -- + arrow (song select: "pick with the rim"), cells SPLIT..DECIDE_END-1 (arcade frames 420..649, -- 230 frames = 3.83 s, the same length as the arcade's taiko_small 'decide' loop) = both sticks + -- 決定 pill (entry: "decide with the face"). The engine passes the same State everywhere, -- so which guide a screen wants is carried by its skin_config position: the -- song-select keys are set to y=1 (see skin_config), entry/others to y=0. local SPLIT, DECIDE_END = math.floor(420 / FRAME_STEP), math.floor(650 / FRAME_STEP) -- cells 210..324 = both sticks + 決定 __hss_costume_panel = __hss_costume_panel or {} __hss_entry_guide = __hss_entry_guide or nil -- { ms, phase = "side" | "mode", count } from the entry scripts -- ROUND 11 drew the 段位道場 shutter doors from HERE, purely for layering: the -- indicator was the last skin-owned draw on SONG_SELECT and therefore the only slot -- that landed over the whole HUD (LUA_CAPABILITIES C-dan-3). ROUND 14 added the real -- slot -- `SongSelect:draw_top(dan_progress)`, called from SongSelectScreen::draw() -- immediately before dan_transition->draw() -- so the doors now live in -- song_select.lua where they belong and this file is back to being the indicator. -- -- The fallback is kept for one reason only: on an engine WITHOUT draw_top, nothing -- would ever call the rig. `dan_doors.draw()` returns false unless a transition is -- actually running, and song_select.lua marks the frame it already drew, so on the -- current engine this costs one table lookup and draws nothing twice. local _dd_ok, dan_doors = pcall(require, "song_select/dan_doors") function Indicator:draw(x, y, fade) fade = fade or 1.0 if _dd_ok and not dan_doors.drawn_this_frame() and dan_doors.draw() then return end -- arcade costume select (entry/costume.nulm) hides the top-left control guide: its own -- title box (100..898 x 142..268) sits where the guide panel is; costume_menu.lua keeps -- __hss_costume_panel[side] fresh while a costume menu is open (skin_config gives both -- indicator_entry_* the same (0,0), so the side cannot be told apart here -> hide for any side) local now = tex.get_current_ms() for _, t in pairs(__hss_costume_panel) do if now - t < 100 then return end end -- entry phases (published by entry.lua / entry/box.lua while they draw): TaikoSelect hides -- the guide, ModeSelect with several boards shows the `all` loop instead of `decide` local g = __hss_entry_guide local show_all = false if g and now - g.ms < 100 then if g.phase == "side" then return end show_all = (g.phase == "mode") and (g.count or 1) > 1 end local elapsed = tex.get_current_ms() - self.t0 local frame if (y or 0) >= 1 or show_all then frame = math.floor(elapsed / 1000 * ARCADE_FPS / FRAME_STEP) % SPLIT if (y or 0) >= 1 then y = y - 1 end else local n = DECIDE_END - SPLIT frame = SPLIT + math.floor(elapsed / 1000 * ARCADE_FPS / FRAME_STEP) % n end tex.draw_texture(self.background, { frame = frame, x = x, y = y, fade = fade }) end -- ROUND 17 — the top-most slot on DAN_SELECT. -- -- `DanSelectScreen::draw()` calls this as its very last statement, after the coin -- overlay, the GLOBAL::DAN_SELECT plate and the allnet chip (Indicator::draw_top, -- src/objects/global/indicator.{h,cpp}). It is DAN_SELECT's equivalent of -- `SongSelect:draw_top`, and it exists for exactly one thing: the 段位道場 shutter's -- SECOND half. The cabinet's doors OPEN on the dan-select screen -- (`loading_dani_intro` f187..200) — up to round 16 our transition ended with a -- hard cut on SONG_SELECT and the reveal was never played at all. -- -- `dan_doors.draw_open()` returns false unless a transition actually handed over, -- so on every other visit to DAN_SELECT this is one table lookup and no draw. function Indicator:draw_top() if _dd_ok then dan_doors.draw_open() end end