Files
Sylpheed/docs/re/structures/preset-message-rules.md
Claude (auto) ecf54a4512 re: all 144 chatter rule tables named; the undeclared 8 are NOT tutorial
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
2026-08-27 16:22:19 +00:00

201 lines
9.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
211, 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 14 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 **S18S23, 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.