Files
YataiDONNijiiro/Scripts/background/bg_objects/collab_dancer_model.lua
T
2026-09-02 14:25:04 -04:00

71 lines
3.1 KiB
Lua

-- YataiDON-HSS child-only helper (ROUND 52, r52-lua-divergence-fixes).
--
-- The COLLAB dancer-count model, ported from the reference build
-- `EnsoGraphicDancer::Process` (EnsoGraphicDancer.obj.c:2723-2752). The
-- function is evaluated EVERY frame and is a pure function of the gauge; the
-- byte at +108 selects between the two branches:
--
-- r = current_gauge / CLEAR_POINT -- +327256 / +327164
-- normal troupes (+108 set): n = clamp(floor(2r + 3), 3, 5) -- ROUND 18
-- dance_b_* collabs (+108 0): n = clamp(floor(4r + 1), 1, 5) -- THIS FILE
--
-- i.e. a collab troupe starts at ONE dancer, gains one for every quarter of
-- the CLEAR point (not the full bar), and is symmetric -- the count falls
-- again when the gauge falls. The skin's collabs used the pre-R18 model
-- (floor(progress * MAX) over the whole bar); ROUND 52 replaces it with this.
-- The upper clamp is the collab's own MAX_DANCERS (per the user's directive
-- the per-collab maxima stay as shipped; reference's own clamp is 5).
--
-- The divisor handling (clear_progress plumbed as handle_gauge's 5th arg,
-- with the "learn the difficulty at the is_clear flip" fallback) is the same
-- fail-soft scheme background.lua:handle_dancer_count has used since R18.
local CollabDancerModel = {}
CollabDancerModel.__index = CollabDancerModel
local GAUGE_MAX = 87.0
local CLEAR_STARTS = { 52, 60, 69 } -- easy / normal / hard+oni
local CLEAR_DEFAULT = 69 / GAUGE_MAX
function CollabDancerModel.new(max_dancers)
return setmetatable({ max = max_dancers, clear_progress = nil }, CollabDancerModel)
end
-- Returns the absolute troupe size for this frame.
function CollabDancerModel:target(progress, is_clear, clear_progress)
if clear_progress and clear_progress > 0 then
self.clear_progress = clear_progress
elseif is_clear and not self.clear_progress then
local p = progress or 0
for _, cs in ipairs(CLEAR_STARTS) do
if p < cs / GAUGE_MAX + 0.0005 then
self.clear_progress = cs / GAUGE_MAX
break
end
end
self.clear_progress = self.clear_progress or CLEAR_DEFAULT
end
local cp = self.clear_progress or CLEAR_DEFAULT
local r = (progress or 0) / cp
local n = math.floor(4 * r + 1)
-- the engine flips is_clear one gauge unit early; honour it so the full
-- troupe and the クリア state arrive on the same frame (same nudge R18
-- applies to the normal troupes).
if is_clear then n = self.max end
if n < 1 then n = 1 elseif n > self.max then n = self.max end
return n
end
-- Drives a BaseDancerGroup-shaped object (add_dancer/remove_dancer/
-- active_count) to the target count. Skips quietly until the group has
-- spawned its initial dancer (active_count 0 = first update not yet run).
function CollabDancerModel:apply(group, progress, is_clear, clear_progress)
local cur = group.active_count or 0
if cur <= 0 then return end
local n = self:target(progress, is_clear, clear_progress)
for _ = 1, n - cur do group:add_dancer() end
for _ = 1, cur - n do group:remove_dancer() end
end
return CollabDancerModel