Grepping FrameCount first would have saved the previous iteration an overclaim. Withdrawn: the 8-value layout was not a discovery. stage-mission-tables already documents Route_* records as (time, quat.x,y,z,w, pos.x,y,z) with len(fields) == 8*FrameCount + 1 holding 1449 of 1449, and unit-group-table already documents 1 + 8*FrameCount for formation slot lists. I re-derived the shape from the asteroid tables and called slot 0 an "index" - it is the corpus's time slot. Corrected in asteroid-fields.md. What the asteroid pass did add is the norm test. The census the corpus was missing: both prior statements were measured on one table family each. Sweeping every IDXD record on the disc carrying a FrameCount - 10986 records, 10974 with anon == 8 * FrameCount, and the entire residual is 12 records: Formation_Fleet_01 (ratio 136) and Formation_Fleet_02 (ratio 4), six each, one per language pack. The two exceptions the doc already names are the only two on the disc. The quaternion is now measured, not asserted: over 78762 frames in five families, |q| is within 1e-4 of 1.0 on 78762 / 78762. Slot 0 is time only where something moves. It equals the running ordinal 0..N-1 in every formation (2196/2196), asteroid (384/384), placement (36/36) and test-route (24/24) record, and in only 6228 of 8334 real routes - the one family that stores true timestamps there. Two record families new to the corpus: Placement_00..02 and RouteTest_00..01, sharing a six-record template object whose sixth record is named Enumeration, two such objects per language pack. All fifteen artefacts byte-identical.
158 lines
8.4 KiB
Markdown
158 lines
8.4 KiB
Markdown
# The asteroid-field tables (`AsteroidGroup_00N`)
|
||
|
||
**Status: ✅ decoded statically, 2026-08-28.** Every number below is an exact
|
||
disc census over all 41 `.pak` archives using the corpus reader.
|
||
|
||
## Where it sits in the chain
|
||
|
||
[[stage-definition-table]] and [[stage-mission-tables]] already record that a
|
||
stage's `Phase_N` block carries an **`AsteroidDefinition`** field naming a `.tbl`
|
||
(`test_s8p1_asteroid.tbl` in the `_Test` template, `""` in most phases), and
|
||
[[unit-datasheet-static]] already partitions the `Generic` records and finds
|
||
**10 per pack whose only field is `{EnumAsteroidGroup}` — "an asteroid-group
|
||
file"**. What neither says is what is *inside* those ten files.
|
||
|
||
```
|
||
Stage_SNN . Phase_N . AsteroidDefinition = "S04_asteroid.tbl"
|
||
└── that object: Generic . EnumAsteroidGroup = "7"
|
||
AsteroidGroup_000 … AsteroidGroup_006
|
||
AsteroidModelName = mob_n050
|
||
AsteroidFrameName = Frame_S04_Asteroid_01
|
||
EnumAsteroid = 39
|
||
```
|
||
|
||
## ✅ The table
|
||
|
||
**10 objects per pack × 6 `GP_MAIN_GAME_<lang>` packs = 60**, holding **384
|
||
`AsteroidGroup_*` records** in total. Each object has **4 or 7** groups (12
|
||
objects with 4, 48 with 7). Every group record carries exactly **three** fields,
|
||
present on all 384 with no partials:
|
||
|
||
| field | distinct values | what it is |
|
||
|---|---:|---|
|
||
| `AsteroidModelName` | 28 | the mesh — `mob_n050`…`mob_n056` (24 uses each) and `mob_n040`…`mob_n043` (18 each) |
|
||
| `AsteroidFrameName` | 64 | `Frame_S<NN>[_pN]_Asteroid_NN` — the placement frame |
|
||
| `EnumAsteroid` | 37 | a **count**, 1 … 588; 6 633 asteroids per pack summed |
|
||
|
||
`Frame_S<NN>_Asteroid` is the same name [[regn-map-grid]] reports the mission
|
||
`Route` tables showing independently — the frame is the join to the map data.
|
||
|
||
## ✅ `Enum<Thing>` is a count; `Enumerate<Thing>` names a table
|
||
|
||
`EnumAsteroidGroup` equals the number of `AsteroidGroup_*` records in its own
|
||
object in **54 of 60** objects, and `EnumAsteroid` is a plain integer. That is
|
||
the opposite of the `Enumerate*` convention, where the value is a child table's
|
||
file name. **The `-erate` is the difference**, and it is worth checking the
|
||
spelling before assuming which kind a field is.
|
||
|
||
⚠️ `Count`-style fields still are not safe: the **6 failures are one object,
|
||
repeated once per language pack** — it declares `4` and contains `7`.
|
||
|
||
## 🔑 The tenth object is unreferenced, and it is the one that miscounts
|
||
|
||
Joining the ten objects to the nine `AsteroidDefinition` values by frame prefix:
|
||
|
||
| object's frame prefix | groups | declared | named by |
|
||
|---|---:|---:|---|
|
||
| `Frame_S01_Asteroid` | 4 | 4 | `S01_asteroid.tbl` |
|
||
| `Frame_S04/S05/S13/S14_p1/S14_p2/S08_p1/S08_p2_Asteroid` | 7 | 7 | the matching `SNN[_pN]_asteroid.tbl` |
|
||
| `Frame_Test_Asteroid` | 4 | 4 | `test_s8p1_asteroid.tbl` |
|
||
| **`Frame_Alpha_S01_Asteroid` + `Frame_Alpha_S01_Haze` + `Frame_TAlpha_S01_Asteroid`** | **7** | **4** | **nothing** |
|
||
|
||
Nine `AsteroidDefinition` names, nine matching objects, and a **tenth that no
|
||
phase names** — an `Alpha` Stage-01 variant with its own `Haze` frames and a
|
||
`TAlpha` set. It is also the only object whose declared count is wrong, which is
|
||
what an abandoned table looks like.
|
||
|
||
## 🔑 S28 has an asteroid *volume* but no asteroid *definition*
|
||
|
||
[[mcol-collision]] lists **nine** `_AsteroidVolume_wp` collision meshes: S01,
|
||
S04, S05, S08_p1, S08_p2, S13, S14_p1, S14_p2 and **S28_p1**. The nine
|
||
`AsteroidDefinition` values are the same eight stages plus **`test_s8p1`**. So
|
||
the two sets share eight and differ by one each: **`S28_p1` has the collision
|
||
volume for an asteroid field but no table saying which rocks to scatter in it**,
|
||
while `test_s8p1` has a table with no volume of its own.
|
||
|
||
## ✅ `EnumAsteroid` is the frame record's `FrameCount` — 57/57 (2026-08-28)
|
||
|
||
Each `AsteroidFrameName` is itself an **IDXD record name**. Those records carry
|
||
one named field, **`FrameCount`**, and a run of anonymous ones:
|
||
|
||
* **`EnumAsteroid` equals that record's `FrameCount` in 57 of 57** frames that
|
||
have a record — **zero mismatches**, and the map is single-valued for all 64
|
||
frame names.
|
||
* The anonymous fields number **exactly 8 × `FrameCount`, on all 384 occurrences**
|
||
(ratio histogram: `{8.0: 384}`). So the frame record is a table of
|
||
**8 values per asteroid** — the placement data — and `EnumAsteroid` counts the
|
||
rocks.
|
||
|
||
[[mission-wave-arrivals]] already met these records: they were the outliers that
|
||
broke its route-speed measurement, and it filtered them out by name, noting "the
|
||
corpus already records that the 8-value frame is not universal". [[regn-map-grid]]
|
||
already guessed the link — "a map's asteroids and debris, which the mission
|
||
`Route` tables independently show as `Frame_S<NN>_Asteroid` records… stated as a
|
||
reading, not a measurement; nothing here counts objects." **What neither says is
|
||
that the count is exact.** Now it is measured.
|
||
|
||
🔑 **The abandoned table lost its placement data too.** Exactly **7 of the 64**
|
||
frame names have no record, and they are exactly the seven groups of the
|
||
unreferenced object: `Frame_Alpha_S01_Asteroid_{01,02,04}`,
|
||
`Frame_Alpha_S01_Haze_{01,02,03}`, `Frame_TAlpha_S01_Asteroid_03`. `57 + 7 = 64`,
|
||
zero partials on either side.
|
||
|
||
## ✅ The name→object join DOES reproduce by hash — with the path prefix
|
||
|
||
Withdrawing the 🔴 from the first write-up. `AsteroidDefinition` values resolve
|
||
to their objects' pak entry keys as **`name_hash("stage\" + name)`**, **9 of 9**,
|
||
each landing on the object the frame-prefix join independently predicted. My
|
||
first attempt hashed the bare file name; [[archive-naming]]'s resolver hashes
|
||
`prefix + name` over a list of path prefixes, and `stage\` is the one that
|
||
works here. (`Stage\` scores identically — the hash lower-cases.)
|
||
|
||
**Two independent joins, 9/9 agreement**, and both leave the same tenth object
|
||
unreferenced.
|
||
|
||
## ✅ The 8 values are `index, quaternion(x,y,z,w), position(x,y,z)` (2026-08-28)
|
||
|
||
Measured over every `Frame_*Asteroid*` record on the disc — **384 records,
|
||
37 518 items** (6 253 per language pack × 6; the other 380 per pack belong to the
|
||
seven record-less `Alpha` frames).
|
||
|
||
🔴 **The layout itself is not new** — [[stage-mission-tables]] already documents
|
||
`(time, quat.x, quat.y, quat.z, quat.w, pos.x, pos.y, pos.z)` for `Route_*`, and
|
||
[[unit-group-table]] already documents `1 + 8·FrameCount` for formations. I wrote
|
||
this up as if the shape were a discovery and called slot 0 an "index" without
|
||
checking; the correction and the disc-wide census are in
|
||
[[stage-mission-tables]]. What the asteroid measurement does add is the **norm
|
||
test** and the fact that this family sets `time` to the ordinal. One raw item,
|
||
for shape:
|
||
|
||
```
|
||
0 0.268744 0.505919 0.579670 0.579487 15667.14 -7199.61 -58794.88
|
||
1 -0.053496 -0.540765 -0.260996 0.797868 -43571.37 -60583.68 -3417.80
|
||
2 -0.696396 0.682371 …
|
||
```
|
||
|
||
| slot | test | result |
|
||
|---|---|---|
|
||
| 0 | equals the running index `0…N−1` | **37 518 / 37 518**, max **587** — and the largest `EnumAsteroid`/`FrameCount` is **588**, so the last index is `N−1`. ⚠️ This slot is the **`time`** of the shared keyframe layout ([[stage-mission-tables]]); the asteroid tables, having nothing that moves, set it to the ordinal — see the disc-wide census there |
|
||
| 1–4 | is it a **unit quaternion**? `\|q\|` | **37 518 / 37 518 within 1e-4 of 1.0** (min 0.999999, max 1.000001) |
|
||
| 4 | sign of the real part | min **+0.001**, never negative — the canonical **w-last** form, so the order is `(x, y, z, w)` |
|
||
| 5–7 | position, metres | ranges **−163 858…+79 572**, **−114 308…+76 422**, **−105 224…+110 263** — inside the ±250 km half-extent of `mapmesh_box_500km` |
|
||
|
||
And `len(anonymous fields) == 8 × FrameCount` on **384 of 384** records, zero
|
||
mismatches.
|
||
|
||
The unit-norm test is the control: an arbitrary 4-tuple has no reason to sit on
|
||
the unit sphere, and **every one of the 37 518 does**. World unit = 1 metre, so
|
||
these are metres from the map origin. **There is no scale slot** — an asteroid's
|
||
size comes from its group's `AsteroidModelName`, which is why one group is one
|
||
model plus one placement list.
|
||
|
||
## 🟡 Not settled
|
||
|
||
* Why `S28_p1` has an asteroid volume and no definition (S28 is the challenge
|
||
mission that alone flies `rou_f004`, see [[stage-numbering-and-player-craft]]).
|
||
* Whether the quaternion is object→world or world→object, and its handedness —
|
||
the norm test cannot tell them apart.
|