Crusader_Decomp/docs/map_renderer/editor-object-survey.md
2026-04-05 18:27:09 +02:00

124 lines
No EOL
15 KiB
Markdown

# Editor/Helper Object Survey
This pass widened the renderer research beyond egg and NPC spawner objects and focused on editor/helper shapes that already carry useful classification data in the exported scene payload.
## Evidence Base
- The renderer already exports `shapeDefinitions[*]` entries with `kind`, `family`, `dimensions`, `visibilityTags`, `traits`, and the matching catalog entry.
- The public catalogs already distinguish many non-gameplay helper families that are currently easy to miss in the UI.
- Representative catalog anchors in Remorse include:
- `0x005A`, `0x005B`, `0x005C`, `0x005D`, `0x0066`-`0x0069`: invisible/editor wall objects
- `0x01B8`: `camera`
- `0x0290`, `0x0336`: `LIGHT_BRIDGE_*`
- `0x0337`, `0x0361`: placeholder cubes and placeholder UI/editor markers
- `0x0108`, `0x0113`, `0x01B9`, `0x01BA`, `0x025F`, `0x0260`, `0x02F0`, `0x0373`, `0x0399`, `0x03A1`, `0x04C8`: `wallgun_shape_*` helper cluster
## `0x0251` Frame `0`: `VALUEBOX`
- The recovered usecode corpus now closes `0x0251` directly as `VALUEBOX` in both retail games, not as a generic placeholder cube.
- Current safest read is `local payload box`, not `world prop`: the object stores numeric or text-selection data for nearby authored controllers instead of behaving like a visible gameplay pickup.
- The caller side is much clearer than `VALUEBOX.slot_20(...)` itself. Checked bodies that explicitly scan nearby `shape=0x0251` items include:
- `MONITNS` (`0x0102`) and `MONITEW` (`0x0165`)
- `WALLMNS` (`0x0367`) and Regret `WALLMEW` (`0x0436`)
- Regret `SECURNS` (`0x03FB`) and `SECUREW` (`0x043D`)
- Regret `WATCHNS` (`0x04C6`) and `WATCHEW` (`0x04DE`)
- `KEYPAD` (`0x0A0E` in Remorse, `0x0A0D` in Regret)
- Those families usually match the box on shared `QLo`, then call `VALBOX.slot_20(valueBox)` to recover the stored value. Monitor, wall-display, and security-terminal paths also feed `Item.getQHi(valueBox)` into `TEXTFILE.slot_23(...)`, so `QHi` behaves like a second text/value selector rather than dead metadata.
- `VALUEBOX::cachein` also hints at a small self-initialization lane: when the decoded value is zero it calls `FREE.slot_20(0x0383)` and writes a replacement through `VALUEBOX.slot_20(...)`. The slot-20 decompilation is still weak, so the conservative read is `possible value initialization`, not a fully closed random-number claim.
- Decompressed `.cache` scenes back the helper role strongly:
- Remorse retail caches currently expose `299` frame-0 placements. The strongest nearby controller families in the local scan are `MONITEW` (`90` hits), `MONITNS` (`65`), and `WATCHEW` (`18`).
- Regret retail caches currently expose `171` frame-0 placements. The strongest nearby controller families are `MONITEW` (`32`), `MONITNS` (`21`), `WATCHEW` (`20`), `SECURNS` (`11`), `SECUREW` (`6`), and `WALLMNS` (`6`).
- Most frame-0 boxes still carry `quality = 0`, which is consistent with the common `QLo == 0` default-link path in the callers.
- Rare authored payloads do exist: Regret map `29` has a `VALUEBOX` with `quality 23` (`QLo 23`), and Remorse maps `25`, `26`, and `141` all contain a nonzero example at `53118,30558,96` with `quality 828` (`QLo 60`, `QHi 3`, `npcNum 2`).
- Many placements occur in small chained local clusters through `nextItem`, which fits a backing-store/helper role better than a standalone scene-prop interpretation.
- Practical renderer implication: `0x0251` should be labeled `VALUEBOX`, should keep `QLo`, `QHi`, and `nextItem` visible in tooltips, and should be treated as a local controller payload object rather than as a generic placeholder cube.
## What This Means For The Renderer
- A lot of the useful information is already present without more reverse-engineering. The main problem was presentation, not raw data availability.
- Editor/helper objects often carry meaningful `mapNum`, `npcNum`, `quality`, or `nextItem` values even when they are not DTABLE-backed NPC spawners. Those raw linkage values are worth exposing because they help separate placeholder geometry from logic markers.
- Catalog names already identify several broad classes that deserve different handling in the UI:
- invisible walls/editor walls
- camera/helper markers
- light bridge / forcefield / editor-authored bridge surfaces
- placeholder cubes and placeholder UI markers
- auto-derived helper shapes tied to specific USECODE families like `WALLGUN`
## Focused Caution: Suspicious Map Objects Are Not Always Helpers
- The map-13 jump-start follow-up around the rare jump-through wall found one especially suspicious nearby placement: `fixed:4767`, shape `0x0135`, frame `0`, at world `47966,53598,97` in the decoded retail cache.
- That object looks tempting as an editor/helper candidate when viewed only from map placement, but the decoded reference data says otherwise: `0x0135` is `shape:309`, a `terrain` item with dimensions `4 x 4 x 0` and traits `solid`, `fixed`, and `land`.
- The useful classification came from USECODE rather than from the exported editor/helper buckets. In the extracted corpus, class `0x0135` is `FFFLOOR`, an environmental hazard/controller family with live `gotHit`, `equip`, and `unequip` bodies.
- The nearby map-13 companion object is not an editor wall flag or a hidden collision override either. The closest local trigger on the same upper platform is the family-4 egg `fixed:4770` (`shape 17`, egg id `37`, subtype selector `QLo 4`), which currently resolves to `CHANGER`, not to a direct wall-solidity helper.
- Practical renderer implication: when a placement looks suspicious in map context, do not assume it belongs in the editor/helper bucket just because it sits beside editor markers. `0x0135` is a good counterexample: it is a gameplay-side environmental floor tile that only becomes legible once the USECODE class is identified.
## Implemented UI Enrichment
The tooltip now exposes generalized metadata for editor/helper objects instead of reserving extra detail almost entirely for NPC spawners:
- `Dimensions`: shape dimensions from the exported shape definition
- `Tags`: exported visibility tags such as `editor`, `helper`, `egg`, `roof`, and `oob`
- `Traits`: exported rendering/collision traits such as `occluding`, `translucent`, `solid`, `fixed`, and nonzero animation type
- `Role hint`: a cautious catalog-backed note for important helper families like invisible walls, cameras, light bridges, placeholders, `WALLGUN` helper shapes, `0x04D0` NPC spawners, and `0x024F` monster eggs
- `Raw linkage`: `map`, `npc`, `quality`, and `next` fields for editor/helper/egg objects so unresolved objects still expose their control data
## Practical Next Targets
- Add dedicated filters or list views for helper subfamilies such as invisible walls, camera markers, light bridges, and placeholder cubes.
- Add shape-family frequency summaries so repeated helper markers can be audited across a map.
- Decode more shape-specific field semantics for the still-unresolved editor objects, especially the remaining non-promoted invisible-wall, camera/helper, music-controller, and secret-door-switch families, and keep folding any new results back into the dedicated USECODE-link note.
- Find the No Regret replacement for the Remorse `0x024F` monster-egg workflow instead of assuming the same shape is reused.
## `0x0318` Frame `0`: `CRUMORPH`
- The older `placeholder cube` label is no longer the best behavioral read for `0x0318`. Both extracted corpora now name class `0x0318` as `CRUMORPH`: Remorse `EUSECODE_extracted/class_event_index.tsv` entry `173` and Regret `REGRET_USECODE_extracted/class_event_index.tsv` entry `174` both expose a live `equip` body at slot `0x0A`.
- The two recovered `equip` bodies differ slightly in helper naming, but they agree on the same high-level lane. Both scan nearby family-`6` actors, compare the pad `QLo` against mutable actor field `0x63`, reject dead actors, transfer control to the first live match, wait until control sticks, and then dispatch `TRIGGER.slot_20` lane `0` or `1` depending on whether that controlled actor is still alive.
- Current best read is therefore `control-transfer morph pad`, not decorative cube and not DTABLE-backed NPC spawner. The object's authored low quality byte is the local control key; `npcNum` does not carry the actor target directly, and the actor-side match is not a stable exported scene field.
- Static scene evidence is strongest in Regret, which is why the viewer promotion was first justified there. The decompressed `.cache` scenes repeatedly show nearby same-`QLo` `0x04B1` helpers close enough to expose a cautious local `CRUMORPH -> CMD_LINK` overlay rule.
- The deeper actor-target side remains intentionally unexported. The same actor-key follow-up that covered `NPC_ONLY` still applies here: the compared actor byte is mutable field `0x63`, and recovered `TRIGGER.slot_29` / `slot_2B` lanes can rewrite it after load. That keeps `CRUMORPH -> actor` arrows out of the viewer for now.
- Practical viewer implication: `0x0318` should be labeled `CRUMORPH`, should expose its `QLo` / `QHi` / `mapNum` / `npcNum` / `nextItem` bytes in tooltip metadata, should open `CRUMORPH::equip` from the USECODE action, and should keep only the already-evidenced nearby same-`QLo` `0x04B1` arrows.
## Newly Promoted Regret-Only Controllers
- `0x0366` remains `NPC_ONLY`, but the latest decompressed `.cache` sweep tightens its practical viewer behavior: actor-target arrows are still not justified, while cautious local `NPC_ONLY -> 0x04B1` same-`QLo` arrows are now strong enough to expose.
- `0x04c6` / `0x04de` are now promoted as `WATCHNS` / `WATCHEW`, not generic editor leftovers. Their recovered `slot_20` bodies scan nearby `0x0510` posts by shared `QLo` and then bracket `TRIGGER.slot_20` around a watcher-specific follow-up lane.
- `0x0510` is now better treated as a `SECRET_DOOR_POST` helper target rather than an unresolved standalone controller. The strongest current viewer behavior is a cautious local arrow from `WATCHNS` / `WATCHEW` plus tooltip decoding of its `QLo`/`QHi` bytes.
- `0x05e1` is closed as `CRYOBOX`, and nearby `0x05df` / `0x05e0` pressure-barrier faces are now promoted out of the unresolved bucket as local arrow targets keyed by shared `QLo`.
- `0x0451` / `0x05ae` are now closed as `CRAZYEW` / `CRAZYNS`, small Regret-only hit-driven NPC wake-up relays rather than vague contextual map labels.
- `0x056d` is now closed as `VIDEOBOX`, a gated controller with a direct `equip` body, even though its higher-level gameplay meaning is still less explicit than the watcher and cryobox lanes.
## Shared Trigger Follow-Up: `0x00A2`, `0x03C1`, And `0x04E7`
### `0x04E7` Frame `0`: `DEATHBOX` In Both Games
- The `npc death` icon label now has a clean cross-game closure, not just a Remorse-side guess. Both extracted corpora expose class `0x04E7` as `DEATHBOX`, and both corpora keep the active exported body at slot `0x0A` (`equip` / `func0A`).
- That means the Remorse equivalent is exact rather than approximate: same shape id, same class label, same nearby-`DEATHBOX` scan from `NPCDEATH.slot_20`, and the same practical viewer interpretation as an NPC-death helper/controller keyed by local `QLo`.
- Practical viewer implication: Regret should no longer leave `0x04E7` as an anonymous editor object when the underlying usecode/export evidence already matches Remorse exactly.
### `0x00A2`: `PANELEW`
- Both extracted corpora now close `0x00A2` directly as `PANELEW`, the east-west counterpart to `PANELNS`, not as a generic unnamed wall button.
- Recovered body `PANELEW::use` is small but consistent across both games:
- if `frame == 0`, it returns immediately
- otherwise, if the panel's map byte is clear, it dispatches `TRIGGER.slot_20` lane `0` from the panel item itself
- The handler does not need to read a second bespoke target field because the downstream trigger family already uses the panel's local `QLo` as the practical authored link id.
- Practical viewer implication: `0x00A2` should be labeled `PANELEW`, should open `PANELEW::use` from the USECODE action, and should participate in the same cautious nearby same-`QLo` `0x04B1` helper-arrow rule already used for `PANELNS` and other local switch/controller shapes.
### `0x03C1`: `GENERATR`
- The old `generator` hunch is directionally right, but the extracted name is now explicit in both games: class `0x03C1` is `GENERATR`.
- The direct active lane is very small and decisive. `GENERATR::gotHit` does not contain a long custom destruction script; it simply excludes the source item and immediately spawns `TRIGGER.slot_20` lane `0` from that same item.
- Current safest read is therefore `destroyable generator/controller` rather than `free-standing scripted puzzle object`: destroying it is useful because it forwards the object's local trigger key into the standard trigger network.
- There is also a second, narrower set-piece lane in Remorse. Recovered `SATARG::use` explicitly scans nearby `shape=0x03C1` items during its countdown/shutdown sequence and drives them through `ITEM.slot_28` beside the related `0x03BF` bank, which fits authored generator-bank or power-node shutdown scenes rather than a different standalone class meaning.
- Practical viewer implication: `0x03C1` should be labeled `GENERATR`, should open `GENERATR::gotHit` from the USECODE action, and should expose the same cautious nearby same-`QLo` `0x04B1` helper arrows as other trigger-source objects because its recovered destruction lane feeds directly into `TRIGGER`.
## Actor-Key Family Follow-Up
- The latest actor-link follow-up did not justify exporting a stable `NPC_ONLY -> actor` or `CRUMORPH -> actor` overlay from static map/cache data alone.
- Current best read is that the compared value is mutable actor field `0x63`, not a DTABLE row and not a field that the current scene export already carries.
- A direct Regret DTABLE row check at byte offset `0x63` is not enough to rescue that idea: sampled rows still read as zero there, so the actor key is not simply a persistent `NPCDat` attribute that can be copied through the existing preview pipeline.
- The same corpus also shows why the field is unstable: `TRIGGER.slot_29` / `slot_2B` can rewrite actor field `0x63` on nearby matched NPCs, so the relevant actor-side link id can change after startup.
- The broader family using this same hidden actor-key mechanism is now clearer though: `CRUMORPH`, `NPC_ONLY`, `WATCHNS`, `WATCHEW`, `THRMBCKN`, `THRMBCKE`, `SURCAMNS`, and `SURCAMEW` all compare controller-local bytes against actor field `0x63` in one of their recovered lanes.
- Practical renderer stance stays conservative: keep only the already-evidenced local helper arrows in the overlay, and surface the actor-key behavior in metadata/tooltips until a runtime or spawn-time export can close field `0x63` directly.
`0x04B1` now has a stable `CMD_LINK -> TRIGGER.slot_20` viewer target, and `0x04E3` is already promoted as `SKILLBOX::equip`, so they no longer belong in the unresolved editor-object bucket here.