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
201 lines
9.6 KiB
Markdown
201 lines
9.6 KiB
Markdown
# The reactive-chatter rule table
|
||
|
||
**✅ Settled 2026-08-27** (static; regenerator
|
||
`tools/re-capture/preset_messages.py`, artefact
|
||
[`data/preset-messages.txt`](../data/preset-messages.txt)).
|
||
|
||
The corpus already knew *that* `PresetMessage_*` is reactive combat chatter —
|
||
[`ixud-localised-text.md`](ixud-localised-text.md) names the IXUD family
|
||
("the bulk: reactive combat chatter keyed by speaker class") and
|
||
[`mission-phase-deployment.md`](../mission-phase-deployment.md) found the link
|
||
that reaches it (`message\UnitMessageSet_S<NN>.tbl` gives every crew a
|
||
`PresetMessage_Phase1/2/3`). What neither says is **what is inside the table** —
|
||
which events exist, what governs when a line fires, and how a line reaches a
|
||
recording. That is this document.
|
||
|
||
## Two files per set
|
||
|
||
A chatter set is a **pair** of IDXD objects in each `dat/GP_MAIN_GAME_<lang>.pak`:
|
||
|
||
| name | role |
|
||
|---|---|
|
||
| `message\PresetMessage_<who>.tbl` | the **rule** table — this document |
|
||
| `message\PresetMessage_<who>_msg.tbl` | the **message** table |
|
||
|
||
The `_msg` half is *not* new: it is exactly the record layout
|
||
[`cutscene-message-table.md`](cutscene-message-table.md) settled for
|
||
`MSG_DEMO_*` — a `Generic` count record plus `Message_NNN` records with `ID` /
|
||
`PageCount` and 9 positional fields per page. What that document does not say is
|
||
that the same layout carries **137 more objects per language pack** for
|
||
in-mission chatter, against the 32 cutscene ones it counted. The control run
|
||
confirms the split cleanly: of the 273 `PresetMessage_*` names that hit an IDXD
|
||
entry, **136 are rule tables and 137 are `Generic`+`Message_NNN` message
|
||
tables** (3 of those are `Generic`-only, i.e. empty).
|
||
|
||
## The rule table
|
||
|
||
**144 per language pack, 864 across the six `GP_MAIN_GAME_<lang>.pak`** — one
|
||
identical count in each, and `ORDOR_SQUADRON_EXTENDED` (the misspelling is the
|
||
game's, see below) is the largest single record name on the disc for exactly
|
||
that reason.
|
||
|
||
Every one of the 144 has the **same 65 records**: 64 event records plus a
|
||
`Sperkers` (sic) roster. One record-name tuple, no variants.
|
||
|
||
### Event records
|
||
|
||
All 9 216 event records (144 × 64) share **one** named-field schema, with no
|
||
exceptions:
|
||
|
||
```
|
||
EffectiveTime Interval Priority Probability MessageCount
|
||
IntervalFluctuation Pattern
|
||
```
|
||
|
||
After the seven named fields come the record's positional payload —
|
||
[a record's unnamed positional fields are its payload] — in **pairs**:
|
||
|
||
(message id, Yes/No) e.g. ('MSG_BIRD_020', 'No')
|
||
|
||
`MessageCount` is the number of pairs, and that identity is **exact**:
|
||
`MessageCount · 2 == positional-field count` in **9 216 of 9 216** records, zero
|
||
mismatches. That is what pins the pairing. The second element of each pair is
|
||
`No` 8 940 times and `Yes` 388 times; its meaning is not settled.
|
||
|
||
### The 64 events
|
||
|
||
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}`.
|
||
* 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.
|
||
* **Five events are declared everywhere and populated nowhere** —
|
||
`DAMAGED_BY_PLAYER_50_30`, `HP_LESS_30`, `RETURNED`, `TALK`, `TM_TO_PLAYER`
|
||
have `MessageCount 0` in all 144 tables. They are slots the shipped data never
|
||
uses.
|
||
|
||
Note the near-miss pairs that are *not* redundant: `HP_LESS_*` (the speaker's
|
||
own hull) versus `PLAYER_HP_LESS_*`, and `AVOIDED_BY_PLAYER` versus
|
||
`AVOIDED_PLAYER_ATTACK`. The `_PLAYER` half of each pair is the smaller
|
||
population.
|
||
|
||
### `Sperkers`
|
||
|
||
The last record is a roster of speaker names as **field names** with empty
|
||
values — `CharacterELLEN`, `CharacterADANCAMAEL`, … **49 distinct speakers**
|
||
across the 144 tables. Most tables name one speaker (84 of 144); the rest name
|
||
2–11, and three name none.
|
||
|
||
## The join to audio is the settled cue table
|
||
|
||
The pair's first element is a message id, and it resolves through the cue index
|
||
[`sound-cue-table.md`](sound-cue-table.md) already documents — `MSG_` becomes
|
||
`VOICE_`, then `SOUNDS` in `dat/tables.pak` gives the numeric id and `FILES` the
|
||
`.slb` bank.
|
||
|
||
**2 388 of 2 405 distinct message ids resolve (99.3 %).** The 17 that do not
|
||
are characterised, not swept up: 8 are ordinary four-letter-family ids
|
||
(`MSG_ADAN_569/668/788/789`, `MSG_ADPL_045/046/047`, `MSG_RHIN_341`) and 9 are
|
||
`MSG_VOICE_*`, seven of them the consecutive run `MSG_VOICE_D_443`…`_449`. A
|
||
consecutive run with no recording reads as content cut late. All 17 are listed
|
||
in the artefact.
|
||
|
||
⚠️ **The wrinkle in that doc is real and I walked into it.** Rewriting `MSG_` to
|
||
`VOICE_` unconditionally gives `VOICE_VOICE_D_443`, which resolved only 2 353 of
|
||
2 405 and would have been written up as "52 lines with no recording", with 44 of
|
||
them a naming artefact. `sound-cue-table.md` states the exception explicitly —
|
||
a name that already carries `VOICE_` keeps the one it has — and applying it
|
||
moves the count to 2 388. Recorded here because the same trap is one line of
|
||
code away in any port.
|
||
|
||
## Naming — five routes, and all 144 land
|
||
|
||
The rule tables are pak entries keyed by `name_hash`, so they had to be named
|
||
before any of this could be read. Five routes, each an independent transform:
|
||
|
||
| route | source | named |
|
||
|---|---|---|
|
||
| 1 | any `PresetMessage_*` string appearing in any IDXD object, under `message\` | 136/144 |
|
||
| 2 | the `PresetMessage_Phase1/2/3` values of the 22 `UnitMessageSet_S<NN>.tbl` | 136/144 |
|
||
| 3 | strip `_msg` from a **message**-table name to get its rule table's name | 137/144 |
|
||
| 4 | predicted from the `Sperkers` roster (below) | 6/144 |
|
||
| 5 | sweep of the naming grammar over the 86 `<who>` tokens routes 1–4 expose | 1/144 |
|
||
| | **union** | **144/144** |
|
||
|
||
Routes 1 and 2 name **the same set** (checked as a set identity, not a matching
|
||
count). Route 3 is a strict superset of route 1 and hits **no** entry that is not
|
||
a rule table. Every one of the 144 has its `_msg` companion present.
|
||
|
||
Names are of the form `PresetMessage_<who>[_<NN>][_S<stage>-<phase>].tbl`, e.g.
|
||
`PresetMessage_Ellen_03_S03-3.tbl`, `PresetMessage_ADAN_Camael.tbl` — a set is
|
||
scoped to a character *and* a stage-phase, which is what makes 144 of them.
|
||
|
||
### ✅ The roster predicts the name
|
||
|
||
Route 4 is the interesting one, because the prediction came first. Among the
|
||
tables already named, the two-speaker ones expose a transparent rule:
|
||
|
||
<FACTION>_Fleet<X><Y> speaks (<FACTION>OP<Y>, <FACTION><X>)
|
||
TCAF_..Ship<X> speaks (ADANPL<X>, TCAF<X>)
|
||
|
||
`TCAF_Fleet{AA,AB,BA,CA,CB}` and `TCAF_17thFleet{AA,AB,BA}` and
|
||
`TCAF_17thFleetShipC` are named; **six gaps in those series** were predicted from
|
||
the six unnamed tables' rosters and then hashed:
|
||
|
||
| roster | predicted name | result |
|
||
|---|---|---|
|
||
| `TCAFOPB`, `TCAFB` | `TCAF_FleetBB` | ✅ `0x59d48b31` |
|
||
| `TCAFOPB`, `TCAFB` | `TCAF_17thFleetBB` | ✅ `0x9d29575d` |
|
||
| `TCAFOPA`, `TCAFC` | `TCAF_17thFleetCA` | ✅ `0x9d4c5353` |
|
||
| `TCAFOPB`, `TCAFC` | `TCAF_17thFleetCB` | ✅ `0x9e527c53` |
|
||
| `ADANPLA`, `TCAFA` | `TCAF_17thFleetShipA` | ✅ `0xee14c77c` |
|
||
| `ADANPLB`, `TCAFB` | `TCAF_17thFleetShipB` | ✅ `0xef1af07c` |
|
||
|
||
**6 of 6.** The control matters as much as the hits: four same-shaped names the
|
||
series does *not* contain — `TCAF_FleetCC`, `TCAF_17thFleetCC`, `TCAF_ShipA_01`,
|
||
`TCAF_17thFleetShipC_01` — score **0 of 4**, so this is not "any plausible name
|
||
hits an unnamed slot".
|
||
|
||
### 🔴 Refuted: the undeclared tables are not tutorial chatter
|
||
|
||
The eight tables that neither route 1 nor route 2 names are undeclared because
|
||
**nothing on the disc declares them** — no `UnitMessageSet` entry, and their own
|
||
names appear as strings nowhere. `UnitMessageSet_S<NN>.tbl` exists for 22 of the
|
||
28 stages and the six missing are exactly **S18–S23, the tutorials**, so
|
||
"the undeclared eight are tutorial chatter" was the obvious reading. It is
|
||
**wrong**: the eight are `PresetMessage_Katana_14_S16-2` and
|
||
`PresetMessage_Margras_04_S14-1` — both **story** stages — plus the six TCAF
|
||
fleet/ship tables above, which carry no stage tag at all. Not one is a tutorial.
|
||
|
||
What that leaves is a different and smaller claim: the tutorials ship **no**
|
||
`UnitMessageSet`, and no rule table is tagged `_S18`…`_S23`, so on this evidence
|
||
the six tutorial missions have no reactive chatter of their own.
|
||
|
||
## ✅ Correction to `squadron-orders.md`
|
||
|
||
That document renders the voice-line enum at `0x820AEEB0` as
|
||
`ORDER_{WINGMAN,SQUAD,SQUADRON}_{FORMATION,ATTACK,COVER,EXTENDED}`. The
|
||
executable's own strings are **not** uniform: the WINGMAN and SQUAD names are
|
||
`ORDER_`, and all four SQUADRON names are misspelled **`ORDOR_`**. The disc data
|
||
carries the identical misspelling, which is how the two sides were joined.
|
||
|
||
That document's 🔴 warning — that this enum is a UI/comms axis and is **not** the
|
||
order-class enum — is **confirmed and now explained**: all twelve names are
|
||
record names in this chatter rule table, i.e. they are the twelve *events*
|
||
"a squadron order was issued", each with its own lines, priority and interval.
|
||
They are triggers for barks, not order types.
|
||
|
||
## 🟡 Not settled
|
||
|
||
* What `EffectiveTime`, `Interval`, `IntervalFluctuation`, `Priority`,
|
||
`Probability` and `Pattern` mean numerically — they are read here as a schema,
|
||
not measured against the running game.
|
||
* The `Yes`/`No` second element of each pair.
|
||
* Which of the 49 speakers is a wingman — the "13 tables" partition above is a
|
||
count, not a verified roster.
|