4d7a7c3373
The big finding is structural, and it corrects something I asserted two commits ago.
Stage prefabs reference 11 script classes and UmaViewer implemented one of them, leaving
892 MonoBehaviour instances with a null script. I claimed those serialized fields were
unreachable at runtime and that reimplementing the scripts would need name heuristics or a
baked parameter table. That was wrong, and the diagnostic I wrote to confirm it disproved it
instead: among the 7 surviving components were AssetHolder (bundle namespace Gallop) and
StageController (Gallop.Live). Unity resolves MonoScript by class name + namespace +
assembly, and Assets/Scripts/umamusume.asmdef is named "umamusume", exactly matching every
MonoScript in the bundles. The project was organised for this from the start — it is the same
mechanism LiveTimelineWorkSheet relies on to read cutt data.
So the 892 are simply unwritten. Declaring a class with the matching signature makes Unity
deserialize the real fields.
BillboardController (Gallop.Live, 164 instances) is the first one rebuilt, and it confirms
the mechanism: missing-script count drops 892 -> 728, exactly -164. _rotationType is now read
per object rather than guessed. Type 0 (106 objects) faces the camera; type 2 (58, all
glow_ramp) is logged and left alone — the enum's meaning is not recoverable, see below.
BlinkLight palette slots — the mirror ball was solid pink
* color0Array/powerArray are a 10-slot palette and every renderer was taking slot 0.
The slot is the object's own or nearest ancestor's `lightNNN_` / `alphaNNN_` name prefix.
Confirmed across all groups on live10149: the number of distinct prefixes equals the
number of distinct colours in slots 0..N-1, with slots N..9 uniform white filler, in
every group without exception. mirrorball 3 (pink/blue/yellow), wash_truss 4, glow_object
2, audience_light 9.
* Renderers with no prefix are not palette lights and are now skipped when the group has
any. The mirror ball body uses Gallop/3D/Bg/BgMirrorBall, whose _MulColor0 belongs to
BgColor1 — which drives it to white, the silver ball seen in the reference footage.
BlinkLight runs after BgColor1 and was overwriting it with palette pink.
Stage animation is now driven by the timeline, not Unity's clock. Using Animation.Play() plus
a default clip with playAutomatically meant the stage kept moving while the live was paused
and ignored scrubbing. AnimationState.time is now set from currentLiveTime and sampled.
CLAUDE.md gains a ground-truth map: what the bundles yield, the rebinding trick above with
its signature table, and what is permanently blocked. global-metadata.dat is neither on disk
nor inside GameAssembly.dll (magic 0xFAB11BAF absent, identifier strings absent) — the game's
anti-tamper keeps it encrypted until runtime. Every enum's meaning, the original method
bodies, and any tuning constants are therefore unrecoverable; rebuilding a signature yields
data, never behaviour. The mirror ball's 2.0s-per-revolution is authored in the clip and
reproduced faithfully; if the reference looks slower, the factor lives in code we cannot read.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>