diff --git a/docs/re/entity-count-discrepancy-resolved.md b/docs/re/entity-count-discrepancy-resolved.md new file mode 100644 index 0000000..a16318f --- /dev/null +++ b/docs/re/entity-count-discrepancy-resolved.md @@ -0,0 +1,82 @@ +# ❌ The "82 turrets are projectiles" hypothesis is refuted — the count inflation is page duplication plus wave arrivals + +Measured 2026-08-26, fresh boot, save slot 01, Stage 01, live flight HUD, +movers precondition checked first (**23 798** in the entity window). + +## ❌ Refuted: they are not projectiles + +[The previous note](entity-readout-working.md) flagged 82 objects typed +`UN_e007_ADAN_Turret` moving at ~375/s and suggested they were shots carrying +their shooter's definition pointer. Three independent checks all say no. + +**1. They persist.** Enumerated, waited 15 s, re-enumerated: + +``` +survivors: 42/42 + of the FAST (>300/s): 22/22 survived + of the SLOW (<=300/s): 20/20 survived +``` + +Every fast object was still there 15 seconds later. A shell fired at a target +2–5 km away does not live 15 s. + +**2. They carry a per-type hull at `pos+0x154`,** the documented anchor: + +| entity | speed | hull `+0x154` | +|---|---|---| +| `UN_f001_TCAF_DeltaSaber_T` | 850.0 | **1000.0** | +| `UN_e007_ADAN_Turret` | 354.2 | **100.0** | +| `UN_e007_ADAN_Turret` | 354.2 | **79.0** | + +Clean round numbers, constant within a type and different between types — and one +turret reading **79.0** against its siblings' 100.0, i.e. a unit that has taken +damage. A projectile does not carry a damageable per-type hull. + +**3. The speed was never suspicious in the first place.** +[`structures/isl-builtins.md`](structures/isl-builtins.md) already records that +`UN_e007_ADAN_Turret`'s definition carries `MaximumVelocity` **500** and +`CruisingVelocity` **280** — "the data models turrets as if mobile". 354–375/s +sits inside that envelope. My hypothesis rested on "a turret is a fixed +emplacement", which is an inference from the *name* that the corpus had already +contradicted from the *data*. Fifth time this session that grepping first would +have been cheaper than measuring. + +## ✅ What the inflated count actually is + +**Two effects, both measured, neither of them mis-typing.** + +**(a) Exactly 2× duplication.** In one enumeration: **64 raw hits → 32 after +deduplicating by (name, rounded position)**, and every per-name count halves +exactly — + +| name | raw | dedup | +|---|---|---| +| `UN_e007_ADAN_Turret` | 32 | 16 | +| `UN_e010_ADAN_Attacker_S` | 16 | 8 | +| `UN_f001_TCAF_DeltaSaber_T` | 14 | 7 | +| `UN_f001_TCAF_DeltaSaber_T_Player` | 2 | 1 | + +The commonest gap between consecutive hit offsets is **`0x1000`** (52 of 63) — +one page. So each entity is found twice, one page apart. `entities2.py`'s `main()` +already dedups by `(name, rounded pos)`; a caller using `typed()` directly does +not, and will double-count. + +**(b) The population grows.** The same enumeration 15 s later returned **60** +where it had returned 42 — consistent with the wave arrivals in +[`mission-wave-arrivals.md`](mission-wave-arrivals.md). + +So comparing "101 from the `+0x130` method" against "42 from the vtable scan" was +never a fair comparison: the two numbers came from different mission times, and at +least one of them was un-deduplicated. + +## ❔ Not settled + +* Whether the earlier **101** figure was raw or deduplicated — it is odd, so it + cannot be a pure 2× artifact, and I did not re-check which of the two numbers + the tool prints at that point. +* Whether the `0x1000` duplication is a genuine second copy of the object or the + same physical page appearing at two file offsets. The exact one-page spacing + suggests aliasing, but nothing here distinguishes them. +* The two enumerations still describe **different sets** — the vtable scan sees + asteroids and the `_Player`, the `+0x130` method mostly sees combatants — so + "which is the entity list" remains open even though the count gap is explained.