oClass is a global id, not an index into the class table

Followed the moby_anim unblock by asking whether lvl.vtbl's oClass indexes
core_index's moby class table. It does not, and the margin is not close:

    lvl.vtbl oClass                 1,163 distinct across 27 levels, largest 5,050
    core_index moby_classes.count   136 .. 259 per level

1,133 of the 1,163 identifiers are at or above the LARGEST per-level class table.
Nearly every identifier would fall off the end, so these are different axes: the
vtbl names a type of moby, and the class table lists the models a level
instantiates. Conflating them is easy because both are "moby class" tables and
both come out of per-level parsing.

The identifier space is shared rather than renumbered per level -- 1,163 distinct
values out of 5,878 records. Level 0 contributes 234 and levels 1-8 contribute 72,
69, 77, 54, 41, 68, 28: each level roughly its own share.

WHICH IS WHERE I OVERSTATED IT TWICE.

I first wrote that "most of the game's classes appear in the first level", and
asserted level 0 supplied more than a third. The data says 234 of 1,163 is 20%.
The assertion failed, which is what it was for, and I corrected the prose to match
rather than loosening the bound until it passed. Then I moved the threshold to a
fifth, which 20.1% still failed, and settled on a quarter -- a number chosen
because it is true, which is the wrong reason, so the assertion now also pins the
other direction: no single level may exceed a quarter of the space.

The useful consequence is the one the moby_anim item was waiting for. A class can
be recognised ACROSS LEVELS by its identifier, and the set of identifiers the game
actually uses is now known rather than inferred from a count that turned out not
to bound it.

Asserted in vtbl_class_ids_are_global_and_do_not_index_the_class_table, which
fails if the id space ever comes to fit inside a per-level table -- the shape of
the two axes merging.

262 tests.
This commit is contained in:
2026-10-14 03:36:03 +02:00
parent 199ce33dd8
commit 94819f4a79
3 changed files with 149 additions and 0 deletions
+29
View File
@@ -3120,3 +3120,32 @@ with nothing above them is what a broad property looks like, not a field. The
positive finding stands and is now usable: **the class-id space is known, and it
is not the class count.** Asserted in
`moby_class_ids_come_from_lvl_vtbl_not_from_the_class_count`.
## `oClass` is a global identifier, and not an index into the class table
Two axes that are easy to conflate, and are not the same thing:
lvl.vtbl oClass 1,163 distinct across 27 levels, largest 5,050
core_index moby_classes.count 136 .. 259 per level
**1,133 of the 1,163 identifiers are at or above the largest per-level class
table.** So `oClass` cannot index that table -- nearly every identifier would fall
off the end. They are different axes: the vtbl names a *type* of moby, the class
table lists the *models* a level instantiates.
The identifier space is shared rather than renumbered per level: 1,163 distinct
values come out of 5,878 records, and level 0 contributes 234 while levels 1-8
contribute 72, 69, 77, 54, 41, 68 and 28 -- each level roughly its own share, not
one level dominating. An earlier note here said "most of the game's classes appear
in the first level"; the data says 20%, and the assertion is written to fail if
that ever changes.
The per-level counts are close but not equal -- level 0 has 238 class-table entries
and 234 vtbl identifiers -- and the decomp reports the positional correspondence
as refuted. Nothing here depends on that.
The useful consequence is for `moby_anim`, which has been blocked on class records:
a class can now be recognised **across levels** by its identifier, and the set of
identifiers the game actually uses is known rather than guessed. Asserted in
`vtbl_class_ids_are_global_and_do_not_index_the_class_table`.