re: the player-facing chatter partition; correcting the wingman claim
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
This commit is contained in:
@@ -4390,3 +4390,45 @@ firing, probability, pick, queue, priority, one-shot flag and cooldown.
|
||||
numeric attenuation of the ducking level, the meaning of node states other than
|
||||
`0x10`/`0x20`, and whether `sub_822168E8`/`sub_82217980` do more than
|
||||
stop/resume. ❔ Still open elsewhere: which of the 49 speakers are wingmen.
|
||||
|
||||
---
|
||||
|
||||
## ✅❌ 2026-08-27 — the player-facing partition, and a correction I owed
|
||||
|
||||
**Docs checked, and two of them already owned the question I set out to ask:**
|
||||
|
||||
* [`isl-condition-builtins.md`](structures/isl-condition-builtins.md) **owns the
|
||||
wingman roster** — `UNITS` in `GP_HANGAR_ARSENAL.pak` is seven fields pairing a
|
||||
craft slot with a pilot (`Bird1-Sandra`, `Bird2-Billy`, `Bird3-Antonius`,
|
||||
`Bird4-Carl`, `Rhino1-Raymond`, `Rhino2-Katana`, `Rhino3-Ellen`) and names it
|
||||
as such. So "which of the 49 speakers are wingmen" **did not need an
|
||||
experiment**.
|
||||
* [`unit-group-table.md`](structures/unit-group-table.md) **owns the player
|
||||
link** — Stage 02's own flight is `UN_f001_TCAF_DeltaSaber_T_Player` with
|
||||
`msg=MessageSet_Katana`, `id=Character_Player_Test`, beside
|
||||
`msg=MessageSet_Ellen`.
|
||||
* `hangar-loadout-system.md` owns the `PlayerSET_*` rows.
|
||||
|
||||
❌ **Correction to `preset-message-rules.md` (mine).** It said the 14
|
||||
player-facing events are "voiced only by the wingman tables". Wrong twice: the 13
|
||||
tables are **all `PresetMessage_Katana_*`**, i.e. the **player's** sets, and the
|
||||
wingman roster is a separate already-settled list in which `Rhino2-Katana` is the
|
||||
player's own slot. Corrected in place with a ❌ note.
|
||||
|
||||
✅ **What the experiment did establish — an exact partition.** Of the 144 rule
|
||||
tables: **13 voice all 14** player-facing events, **131 voice none**, and
|
||||
**0 voice some**. Not a tendency — zero partials. And **none of the 129
|
||||
non-Katana tables voices *any* of the 14**, so `LOCKON_*`, `PLAYER_HP_LESS_*`,
|
||||
`PLAYER_AMMO_LESS_*` and `ORDER_WINGMAN_*` are exclusively the player's own
|
||||
callouts. `CharacterKATANA` is the **only** one of the 15 speakers in that set
|
||||
exclusive to it. Regenerator extended; artefact carries the partition.
|
||||
|
||||
🟡 **Residual:** there are **15** Katana tables, and the two that voice none are
|
||||
`Katana_09_S10-1` and `Katana_14_S16-2` — all-or-nothing like the rest. Why those
|
||||
two stage-phases opt out is not established from the disc.
|
||||
|
||||
**Method note:** the grep that would have caught this ran *after* the experiment
|
||||
was designed, not before. The rule says grep the doc that owns the DATA first;
|
||||
here the data was "wingman", the owning doc was `isl-condition-builtins.md`, and
|
||||
reading it first would have reframed the iteration rather than corrected it
|
||||
afterwards.
|
||||
|
||||
@@ -98,7 +98,7 @@ files, which is how the same ground got covered twice.
|
||||
| [`structures/sound-pak-contents.md`](structures/sound-pak-contents.md) | Census of `sound.pak`, and the limit of the leading-region rule | ✅ CONFIRMED, 5 135/5 135 names hash into the TOC, **9 519/9 519** entries accounted for, and a full 4 114-bank manifest (408.3 min of audio) computed from PsuedoBytesPerSec without decoding; ⚠️ leading-region rule holds for 1 571/4 382 eng and 0/5 100 jpn |
|
||||
| [`structures/sound-cue-table.md`](structures/sound-cue-table.md) | The cue index in `tables.pak` — message id -> cue -> sound id -> `.slb` bank | ✅ CONFIRMED, 1 326/1 338 script message ids bind to a bank; SOUNDS and FILES agree on the same 12 absentees, 0 orphan files |
|
||||
| [`structures/cutscene-message-table.md`](structures/cutscene-message-table.md) | Cutscene dialogue — speaker, portrait, on-screen seconds, audio cue per page | ✅ CONFIRMED, field count = 9·PageCount+2 for all 7 PageCounts, 1 252/1 252 caption keys match, 138 ids close both ways |
|
||||
| [`structures/preset-message-rules.md`](structures/preset-message-rules.md) | The reactive-chatter rule table — 64 events, the (message, Yes/No) pair payload, the `Sperkers` roster | ✅ CONFIRMED, the seven fields read off `sub_82213980`, phase tables merge non-additively (224 variants discarded); 8 firing sites share one entry, Probability is a per-cent roll against rand*100.0; Yes = one-shot line; queue is Priority-ordered and capped at 128; the cooldown is Interval + Fluctuation*rand held in the lingering queue node (three are seconds ×60, the Yes/No element is a u32 mask, hence the 32 clamp); MessageCount·2 == positional count in 9 216/9 216; five naming routes union to 144/144 (six names predicted from the speaker roster before hashing, control 0/4); 2 388/2 405 message ids bind to a cue |
|
||||
| [`structures/preset-message-rules.md`](structures/preset-message-rules.md) | The reactive-chatter rule table — 64 events, the (message, Yes/No) pair payload, the `Sperkers` roster | ✅ CONFIRMED, the seven fields read off `sub_82213980`, phase tables merge non-additively (224 variants discarded); 8 firing sites share one entry, Probability is a per-cent roll against rand*100.0; Yes = one-shot line; queue is Priority-ordered and capped at 128; the cooldown is Interval + Fluctuation*rand held in the lingering queue node; the 14 player-facing events are voiced by the player set alone (13/15 Katana tables, 0/129 others) (three are seconds ×60, the Yes/No element is a u32 mask, hence the 32 clamp); MessageCount·2 == positional count in 9 216/9 216; five naming routes union to 144/144 (six names predicted from the speaker roster before hashing, control 0/4); 2 388/2 405 message ids bind to a cue |
|
||||
| [`structures/isl-mission-timer.md`](structures/isl-mission-timer.md) | The mission timer — two sequential countdowns, not a limit and a warning | ✅ CONFIRMED — read from `sub_822639B8`; the threshold reading is |
|
||||
| [`structures/archive-naming.md`](structures/archive-naming.md) | Which archives the disc can name — 100% for menu paks, **0.0%** for the six 2D paks and `GP_READY_ROOM` | ✅ CONFIRMED — 6027 names × 16 prefixes, bimodal |
|
||||
| [`structures/hud-config.md`](structures/hud-config.md) | The in-game HUD configuration — 16 records, 419 asset paths, and a per-stage `ResourceTable` | ✅ CONFIRMED — 0 of 419 config paths resolve as pak entries |
|
||||
|
||||
@@ -193,6 +193,27 @@ distinct declared PresetMessage tables: 137
|
||||
CONTROL — PresetMessage_* names that hit an IDXD entry but are NOT rule tables: 137
|
||||
their record-name families: {('Generic', 'Message_'): 134, ('Generic',): 3}
|
||||
|
||||
player-facing events (14 of the 64): who voices them
|
||||
tables voicing ALL of them : 13
|
||||
tables voicing NONE : 131
|
||||
tables voicing SOME : 0 []
|
||||
all message\PresetMessage_Katana_01_S01-1.tbl
|
||||
all message\PresetMessage_Katana_02_S01-2.tbl
|
||||
all message\PresetMessage_Katana_03_S02-1.tbl
|
||||
all message\PresetMessage_Katana_04_S02-2.tbl
|
||||
all message\PresetMessage_Katana_05_S03-3.tbl
|
||||
all message\PresetMessage_Katana_06_S07-1.tbl
|
||||
all message\PresetMessage_Katana_07_S09-2.tbl
|
||||
all message\PresetMessage_Katana_08_S09-3.tbl
|
||||
all message\PresetMessage_Katana_10_S11-1.tbl
|
||||
all message\PresetMessage_Katana_11_S12-1.tbl
|
||||
all message\PresetMessage_Katana_12_S13-1.tbl
|
||||
all message\PresetMessage_Katana_13_S15-1.tbl
|
||||
all message\PresetMessage_Katana_15_S28.tbl
|
||||
speakers in the all-set: 15
|
||||
exclusive to it: ['CharacterKATANA']
|
||||
also elsewhere : 14
|
||||
|
||||
rule tables, by name:
|
||||
0xde14f001 message\PresetMessage_ADAN_Camael.tbl
|
||||
0x31092781 message\PresetMessage_ADAN_Commander.tbl
|
||||
|
||||
@@ -67,10 +67,12 @@ mismatches. That is what pins the pairing. The second element of each pair is
|
||||
The full census with per-event table counts is in the artefact. The shape of
|
||||
that census is itself informative:
|
||||
|
||||
* **Player-facing events are voiced only by the wingman tables.** Every
|
||||
`LOCKON_1..12`, `LOCKON_UP`, `LOCKON_DOWN`, `PLAYER_HP_LESS_10/30/50` and
|
||||
`PLAYER_AMMO_LESS_00/10/40` record carries lines in **exactly 13** of the 144
|
||||
tables — the same 13 as `ORDER_WINGMAN_{FORMATION,ATTACK,COVER,EXTENDED}`.
|
||||
* **Player-facing events are voiced only by the PLAYER's own tables** — see
|
||||
"the player-facing partition" below. Every `LOCKON_1..12`, `LOCKON_UP`,
|
||||
`LOCKON_DOWN`, `PLAYER_HP_LESS_10/30/50` and `PLAYER_AMMO_LESS_00/10/40`
|
||||
record carries lines in **exactly 13** of the 144 tables, the same 13 as
|
||||
`ORDER_WINGMAN_{FORMATION,ATTACK,COVER,EXTENDED}`. (❌ An earlier revision of
|
||||
this document called those "the wingman tables". They are not.)
|
||||
* The squad/squadron order events carry lines in **7** tables each.
|
||||
* Generic combat events are near-universal: `DAMAGED_BY_PLAYER_GREATER_50` 128,
|
||||
`DAMAGED` 124, `HP_LESS_10` and `HP_LESS_50` 121 each.
|
||||
@@ -532,8 +534,58 @@ among *higher*-priority pending nodes) that makes `+20` a ducking level: raised
|
||||
while a higher-priority line plays, released when it retires. The exact
|
||||
attenuation is not established here.
|
||||
|
||||
## ✅ The player-facing partition — and a correction
|
||||
|
||||
**Settled 2026-08-27.** The 13 tables that voice every player-facing event are
|
||||
**all `PresetMessage_Katana_NN_S<stage>-<phase>.tbl`** — the player character's
|
||||
own chatter sets.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| tables voicing **all 14** player-facing events | **13** |
|
||||
| tables voicing **none** | **131** |
|
||||
| tables voicing **some** | **0** |
|
||||
|
||||
Zero partials: the split is exact, not a tendency. And it is not merely that few
|
||||
others voice them — **none of the 129 non-Katana tables voices *any* of the 14**.
|
||||
|
||||
There are **15** Katana tables in total; the two that voice none are
|
||||
`Katana_09_S10-1` and `Katana_14_S16-2`, and they are all-or-nothing like
|
||||
everything else. Why those two stage-phases opt out is not established.
|
||||
|
||||
Of the 15 speakers named across the 13, **`CharacterKATANA` is the only one
|
||||
exclusive to them**; the other 14 all appear in other tables too.
|
||||
|
||||
[`unit-group-table.md`](unit-group-table.md) supplies the link that makes this
|
||||
make sense — the player's own flight in Stage 02 reads
|
||||
|
||||
```
|
||||
unit=UN_f001_TCAF_DeltaSaber_T_Player msg=MessageSet_Katana id=Character_Player_Test
|
||||
unit=UN_f001_TCAF_DeltaSaber_T msg=MessageSet_Ellen id=ELLEN
|
||||
```
|
||||
|
||||
so `MessageSet_Katana` is the message set attached to the **player craft**.
|
||||
`LOCKON_*` is the player's own lock-on callout, `PLAYER_AMMO_LESS_*` the player's
|
||||
own ammunition, and `ORDER_WINGMAN_*` the player *issuing* an order — all of
|
||||
which belong on the player's set and nowhere else.
|
||||
|
||||
### ❌ Correction, and the doc that already owned the question
|
||||
|
||||
The question this iteration set out to answer — "which of the 49 speakers are
|
||||
wingmen" — was **already answered elsewhere and did not need this experiment**.
|
||||
[`isl-condition-builtins.md`](isl-condition-builtins.md) owns it: the `UNITS`
|
||||
record in `GP_HANGAR_ARSENAL.pak` is seven fields pairing a craft slot with a
|
||||
pilot — `Bird1-Sandra`, `Bird2-Billy`, `Bird3-Antonius`, `Bird4-Carl`,
|
||||
`Rhino1-Raymond`, `Rhino2-Katana`, `Rhino3-Ellen` — and it names that **the
|
||||
wingman roster**. [`hangar-loadout-system.md`](hangar-loadout-system.md) owns the
|
||||
`PlayerSET_*` rows that go with it.
|
||||
|
||||
So the earlier phrasing here was wrong twice over: the 13 are the **player's**
|
||||
tables, not wingman tables, and the wingman roster is a separate, already-settled
|
||||
list in which `Rhino2-Katana` is the player's own slot.
|
||||
|
||||
## 🟡 Not settled
|
||||
|
||||
* The *policy* the seven fields drive — see "what this does not settle" above.
|
||||
* Which of the 49 speakers is a wingman — the "13 tables" partition above is a
|
||||
count, not a verified roster.
|
||||
* Why `Katana_09_S10-1` and `Katana_14_S16-2` opt out of the player-facing
|
||||
events entirely.
|
||||
|
||||
@@ -362,6 +362,39 @@ def main():
|
||||
oshape[tuple(sorted({r['squadron'].rstrip('0123456789') for r in recs}))] += 1
|
||||
print(' their record-name families: %s' % dict(oshape))
|
||||
|
||||
|
||||
# the player-facing partition: which tables voice LOCKON_* / PLAYER_* /
|
||||
# ORDER_WINGMAN_*, and who speaks in them
|
||||
PLAYER_FACING = ['LOCKON_1', 'LOCKON_12', 'LOCKON_UP', 'LOCKON_DOWN',
|
||||
'PLAYER_HP_LESS_10', 'PLAYER_HP_LESS_30', 'PLAYER_HP_LESS_50',
|
||||
'PLAYER_AMMO_LESS_00', 'PLAYER_AMMO_LESS_10', 'PLAYER_AMMO_LESS_40',
|
||||
'ORDER_WINGMAN_ATTACK', 'ORDER_WINGMAN_COVER',
|
||||
'ORDER_WINGMAN_EXTENDED', 'ORDER_WINGMAN_FORMATION']
|
||||
full, none_, partial = [], [], []
|
||||
spk_full, spk_rest = set(), set()
|
||||
for h, recs in rule.items():
|
||||
byname = {r['squadron']: r for r in recs}
|
||||
got = [k for k in PLAYER_FACING
|
||||
if any(n is None for _, n, _ in byname[k]['fields'])]
|
||||
who = [n for _, n, _ in byname['Sperkers']['fields'] if n]
|
||||
nm = A.get(h, '%#010x' % h)
|
||||
if len(got) == len(PLAYER_FACING):
|
||||
full.append(nm); spk_full.update(who)
|
||||
elif not got:
|
||||
none_.append(nm); spk_rest.update(who)
|
||||
else:
|
||||
partial.append((nm, len(got))); spk_rest.update(who)
|
||||
print()
|
||||
print('player-facing events (%d of the 64): who voices them' % len(PLAYER_FACING))
|
||||
print(' tables voicing ALL of them : %d' % len(full))
|
||||
print(' tables voicing NONE : %d' % len(none_))
|
||||
print(' tables voicing SOME : %d %s' % (len(partial), partial))
|
||||
for nm in sorted(full):
|
||||
print(' all %s' % nm)
|
||||
print(' speakers in the all-set: %d' % len(spk_full))
|
||||
print(' exclusive to it: %s' % sorted(spk_full - spk_rest))
|
||||
print(' also elsewhere : %d' % len(spk_full & spk_rest))
|
||||
|
||||
print()
|
||||
print('rule tables, by name:')
|
||||
for h in sorted(rule, key=lambda k: A.get(k, '~%#010x' % k)):
|
||||
|
||||
Reference in New Issue
Block a user