This repository has been archived on 2026-09-16. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Syplheed-Reborn/docs/re/structures/asteroid-fields.md
Sylpheed RE agent 168bebc700 re: S28 does have an asteroid definition - it borrows Stage 14's
Withdrawing my own repeated claim that S28_p1 has an asteroid volume with no
definition. It came from a set-difference over FILE NAMES, which cannot see
reuse. Joining the fields per PHASE settles it:

  S28.Phase_1   MapMesh = S28_p1_AsteroidVolume_wp.col
                AsteroidDefinition = S14_p2_asteroid.tbl

S28 scatters Stage 14 phase 2's rock list inside its own volume. Nine
definition tables and nine _AsteroidVolume_wp meshes, with one table shared by
two phases: S14_p2_asteroid.tbl -> S14.Phase_2 and S28.Phase_1.

The earlier census's own arithmetic already contained the answer: the four
values counted x12 (2 per language pack) were S01, S04, S05 - two phases of ONE
stage - and S14_p2, which is two phases of TWO different stages. The count was
right; reading x12 as "two phases of the same stage" was the error.

test_s8p1_asteroid.tbl is not test-only either - it is what S02.Phase_1 ships
with. A test_ name here is a leftover, not a dead table.

The true residual is two phases and it is the opposite pairing to what I
recorded: S01.Phase_3 has the volume and no definition (S01 keeps its volume
across all three phases but scatters rocks only in 1-2), and S02.Phase_1 has a
definition and no volume (MapMesh is the plain mapmesh_box_500km).

All fifteen artefacts byte-identical.
2026-08-28 03:44:14 +00:00

196 lines
10 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 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.
⚠️ Sharper still, found a turn later: **[[stage-mission-tables]] already says it
in words** — "The prefix is `stage\``name_hash` is case-insensitive, so its
case is free" — and its `stagetbl.py` examples pass `'stage\AIParams_S02.tbl'`.
The answer to my "🔴 does not reproduce by hash" was written in the doc that owns
the stage tables.
## ✅ 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…N1` | **37 518 / 37 518**, max **587** — and the largest `EnumAsteroid`/`FrameCount` is **588**, so the last index is `N1`. ⚠️ 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 |
| 14 | 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)` |
| 57 | 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.
## 🔴✅ CORRECTION — S28 does have a definition: it borrows Stage 14's (2026-08-28)
Withdrawing "`S28_p1` has an asteroid volume with no definition". That came from a
set-difference over **file names**, which cannot see reuse. Joining the fields
**per phase** instead:
| table | the phases that declare it |
|---|---|
| `S01_asteroid.tbl` | S01.Phase_1, S01.Phase_2 |
| `S04_asteroid.tbl` | S04.Phase_1, S04.Phase_2 |
| `S05_asteroid.tbl` | S05.Phase_1, S05.Phase_2 |
| `S08_p1` / `S08_p2` | S08.Phase_1 / S08.Phase_2 |
| `S13_asteroid.tbl` | S13.Phase_1 |
| `S14_p1_asteroid.tbl` | S14.Phase_1 |
| **`S14_p2_asteroid.tbl`** | **S14.Phase_2 *and* S28.Phase_1** |
| `test_s8p1_asteroid.tbl` | **S02.Phase_1** |
**S28.Phase_1 declares `S14_p2_asteroid.tbl` while using its own
`S28_p1_AsteroidVolume_wp` mesh** — it scatters Stage 14's rock list inside its
own volume. Nine tables, nine volumes, one table shared by two phases.
⚠️ The earlier census's own arithmetic contained this: the four values counted
**×12** (2 per language pack) were `S01`, `S04`, `S05` — two phases of one stage —
and `S14_p2`, which is **two phases of two different stages**. Reading ×12 as
"two phases of the same stage" was the error.
⚠️ **`test_s8p1_asteroid.tbl` is not test-only either** — it is what **S02.Phase_1**
ships with. A `test_` name here is a leftover, not a dead table.
**The true residual is two phases, and it is the opposite pairing:**
`S01.Phase_3` has the volume and no definition (S01 keeps its volume across all
three phases but scatters rocks only in 12), and `S02.Phase_1` has a definition
and no volume (its `MapMesh` is the plain `mapmesh_box_500km`).
## 🟡 Not settled
* Whether the quaternion is object→world or world→object, and its handedness —
the norm test cannot tell them apart.