The 13 tables that voice every player-facing event are all
PresetMessage_Katana_*, the PLAYER's own sets -- unit-group-table.md already
names the link (DeltaSaber_T_Player carries msg=MessageSet_Katana). Exact
partition: 13 voice all 14 events, 131 voice none, 0 voice some, and no
non-Katana table voices any of them. CharacterKATANA is the only speaker
exclusive to the set.
Corrects preset-message-rules.md, which called those the wingman tables. The
wingman roster is owned by isl-condition-builtins.md (UNITS: Bird1-Sandra ...
Rhino2-Katana, Rhino3-Ellen) and needed no experiment.
Residual: Katana_09_S10-1 and Katana_14_S16-2 voice none of the 14.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
sub_82210C38 retires a line and is the only chatter function reading the rule's
+20/+24 as words: node[+16] = Fluctuation * rand01 + Interval frames, state
0x20. Interval is a floor, Fluctuation a uniform additive jitter.
The queue node IS the cooldown timer -- the tick counts +16 down and only then
frees the slot, so the already-queued bail in sub_82210670 and the cooldown are
one mechanism: a speaker cannot repeat an event until its node expires. A
lingering node still holds one of the 128 pool slots.
Also: +20 on the node is a ducking level, raised while a higher-priority line
plays and released on retirement.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
sub_822109B0 allocates 68-byte nodes from a pool capped at 128; the Pattern
byte at +23 doubles as the occupancy flag. Priority orders the pending list
descending; the >=10 front-push arm is dead because the disc's range is 1..9.
The tick sub_8220FC50 does rule[+36] |= (1 << node[+25]) & rule[+32], so a
line enters the used mask only if its Yes bit is set: Yes = one-shot, No =
repeatable. 388 one-shot lines on the disc. Closes what reads +32.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
36 of the 64 event names are executable strings, so the string-xref join names
the eight sites that raise them; all eight share one entry, sub_8220FA98.
Probability is a per-cent roll -- the scaling constant at 0x820856F8 is exactly
100.0. sub_82210670 suppresses an already-queued (speaker, rule) pair, treats
a message as eligible only if its bit is clear in the runtime mask at +36, and
picks uniformly, then hands Pattern/Priority/EffectiveTime to sub_822109B0.
Measured negative: the pick path never reads +20, +24 or +32.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
sub_82215A58 reads CrewCount + PresetMessage_Phase1/2/3 and reaches the loader
from ONE call site, so the three phase tables fold into one map keyed by the
event record name. Every merge collides; sub_82213840 reconciles on the
message list plus +16/+17/+18/+20/+24/+28 (NOT the +32 Yes mask), and the
incumbent always wins. Measured: 26432 collisions, 26208 identical, 224
different (189 differ only in the message list), 0 mask-only differences.
Refuted handle: intersecting functions by the object's offsets finds dozens of
unrelated layouts -- offset shape is not an identifier.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
The one function referencing all seven field-name strings is the rule table's
loader. Interval / IntervalFluctuation / EffectiveTime are SECONDS, emitted
as *60 frame counts; Probability is a percentage and zero skips the record;
Pattern is a 4-arm enum of which only Sound and Window ship; the Yes/No pair
element is a u32 bitmask, which is why MessageCount is clamped to 32 (max on
disc is 26). 40-byte object layout recorded. 13 dead records characterised.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
Three more naming routes: strip _msg from a message table (137/144, superset
of route 1, zero non-rule hits), predict the name from the Sperkers roster
(6/6, control 0/4), and sweep the naming grammar (1/144). Union 144/144,
and every rule table has its _msg companion.
Refutes the reading left by 3f20787: the undeclared tables are two story-stage
tables and six TCAF fleet/ship tables, no tutorial content at all.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
864 records = 144 rule tables per language pack x 6. One schema for all
9216 event records; MessageCount*2 == positional count with zero mismatches.
Named 136/144 by two independent routes that agree as sets. 2388/2405
message ids join the settled sound-cue table.
Corrects squadron-orders.md: the executable misspells all four SQUADRON
entries of the 0x820AEEB0 enum as ORDOR_, and the disc data matches.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE