Crusader_Decomp/docs/editor-object-visibility.md

225 lines
9.3 KiB
Markdown
Raw Permalink Normal View History

2026-03-29 01:14:09 +01:00
# Editor-Object Visibility In Retail Crusader
## Question
Investigate whether the retail game explicitly hides editor-only map objects during gameplay, and whether any built-in debug, cheat, or command-line switch can enable those objects in the normal in-game renderer.
Active analysis target for the binary side was live `CRUSADER.EXE` in the Ghidra MCP session.
## Result
Current best answer:
- Yes, the retail executable explicitly hides editor-tagged shapes in the normal world-item render path, with at least two recovered `SI_EDITOR` gates.
- No built-in retail flag, cheat, or command-line switch has been recovered so far that re-enables those editor objects in the normal in-game renderer.
- The closest confirmed "show editor items" toggle is in ScummVM/Pentagram, not in the original retail executable.
## Binary Findings In `CRUSADER.EXE`
### 1. The downstream sprite painter explicitly skips `SI_EDITOR`
The strongest direct proof is live function `Item_PaintSprite` at `1198:02e4`.
Verified decompile behavior:
- It loads the current item's shape number from `g_itemShapeNos`.
- It resolves the matching `ShapeData` entry from `g_shapeData`.
- It immediately checks `local_8->flags2 & 1`.
- When that bit is set, the function returns before caching or drawing the shape.
Relevant instruction window at `1198:0332..033d`:
```text
1198:0332 MOV AL,byte ptr ES:[BX + 0x6]
1198:0336 AND AX,0x1
1198:0339 OR AX,AX
1198:033b JZ 1198:0340
1198:033d JMP 1198:0959
```
`1198:0959` is the function epilogue, so the branch is an unconditional early-out for bit `0` of shape byte `6`.
This aligns with the existing `SI_EDITOR` comment at `1198:0339` and with the open-source Crusader typeflag decoders, where Crusader `TYPEFLAG.DAT` byte `6`, bit `0` maps to `SI_EDITOR`.
### 2. The paired setup path still processes those items
The neighboring helper `Item_SetScreenBoxAndWorldCoords` at `1198:095d` still computes:
- world coordinates,
- projected screen coordinates,
- bounding boxes,
- and weapon-overlay extents.
That function does **not** perform an `SI_EDITOR` check.
Current safest read after the first pass was:
- editor objects remain present in the live item/cache/display-list setup state,
- but the normal sprite paint stage refuses to draw them.
That read turned out to be incomplete rather than wrong.
### 3. The active world-item render-list builder also skips `SI_EDITOR` earlier
Follow-up tracing from the live camera redraw path into segment `1180` found a more important upstream gate in the world-item render-list builder.
Relevant instruction window at `1180:0951..095c`:
```text
1180:0951 MOV AL,byte ptr ES:[BX + 0x6]
1180:0955 AND AX,0x1
1180:0958 OR AX,AX
1180:095a JZ 1180:095f
1180:095c JMP 1180:0bec
```
This is the same Crusader `TYPEFLAG.DAT` byte `6`, bit `0` test, but here the consequence is stronger than in `Item_PaintSprite`: the builder skips the world-item draw-node allocation path entirely before that item can ever reach the normal draw dispatch.
That closes the runtime discrepancy from the first patch attempt. Patching only the later `1198:033b` branch changed a real live draw method, but it did **not** make editor objects visible in-game because the active renderer had already filtered them out earlier at `1180:0951..095c`.
### 4. Why the first executable patch appeared to do nothing
The first public patch only flipped the downstream `Item_PaintSprite` branch:
```text
1198:033b JZ 1198:0340 -> EB 03
```
That patch was not dead code. Export relocation evidence still shows the draw-node method slot at `1478:2c1f` dispatching to `1198:02e4`, so the downstream painter is a real live node callback.
But the active world-item renderer never reached that callback for editor-tagged shapes, because the upstream builder branch at `1180:095a..095c` had already skipped node creation.
Current patching implication:
- patching `1198:033b` alone is insufficient,
- the upstream `1180:095a` branch is the controlling visibility gate for normal world-item rendering,
- and a practical visibility patch should flip both recovered `SI_EDITOR` branches together.
### 5. The recovered item-flag helpers match the same shape-bit model
`Item_GetTypeFlagCrusader` reads arbitrary shape/typeflag bits from the per-shape `ShapeData` records.
Additional direct flag helpers nearby include:
- `Item_IsTargetable`, which reads `flags2 & 0x10`
- `Item_IsShapeFlagOccl`, which reads `flags0 & 0x10`
That supports the interpretation that `Item_PaintSprite` is not testing some temporary per-item state. It is reading a stable per-shape editor flag.
## Negative Search For A Retail "Show Editor Items" Toggle
I checked the known retail debug/cheat/control lanes already documented in the live database and repo notes.
### `-debug`
Retail `-debug` is live at `1048:0a93`, but the current closed behavior is:
- raise `g_debugMsgLevel` to `10`
- print `Debugging mode ON.`
- set `1478:0845`
- enable the seg1468 video-player timing overlay via `1478:0859`
No recovered link from `-debug` reaches the `SI_EDITOR` skip in `Item_PaintSprite` or enables a parallel item-paint path.
### Cheat/debug hotkeys
Recovered cheat/debug hotkeys do toggle several overlays, but the confirmed ones are unrelated to editor-shape rendering:
- `Ctrl+F7` at `13e8:1a20` toggles `1478:0ee0`, the egg-hatcher trigger-range overlay.
- `Alt+F7` at `13e8:1a50` toggles `1478:2bc9`, another cheat-gated overlay lane.
- The remaining F7-family toggle uses `1478:2bca` for the coarse grid overlay.
Those paths force camera redraws, but the recovered behavior is overlay drawing, not "draw editor-tagged world items."
### Hidden usecode debugger / Laurie lane
The hidden seg109/seg1408 usecode-debugger lane remains real, and `-laurie` plus the cheat/debug latches still enable related hidden behavior. But no recovered debugger-side state or menu action currently bypasses the `Item_PaintSprite` editor skip, and no candidate global has been found that matches ScummVM's later `showEditorItems` concept.
### Event `0x410`
This lane remains unrelated.
Current live conclusion is unchanged:
- retail `0x410` toggles the CD transfer display state,
- it is not the immortality toggle,
- and it is not an editor-object visibility switch.
## Cross-Check Against Open-Source Engine Behavior
ScummVM and Pentagram both independently preserve the same semantic meaning for the Crusader editor flag.
### Flag mapping
In both codebases, Crusader `TYPEFLAG.DAT` byte `6`, bit `0` maps to `SI_EDITOR`.
ScummVM `type_flags.cpp`:
```cpp
if (data[6] & 0x01) si._flags |= ShapeInfo::SI_EDITOR;
```
Pentagram `TypeFlags.cpp`:
```cpp
if (data[6] & 0x01) si.flags |= ShapeInfo::SI_EDITOR;
```
### Runtime treatment
ScummVM `game_map_gump.cpp` shows the exact higher-level behavior one would expect from the retail binary finding:
```cpp
if (!showEditorItems && item->getShapeInfo()->is_editor())
continue;
```
Pentagram documents the same meaning directly in `docs/u8typeflag.txt`:
- `bit 4 : editor shape (don't render in-game)`
For Crusader, the bit location differs in the later 9-byte Crusader format, but the semantic meaning is the same.
### Important limit of this cross-check
ScummVM exposes its own engine variable and debugger command:
- engine field: `_showEditorItems`
- setter: `setShowEditorItems(bool flag)`
- debugger command: `showEditorItems [on|off]`
That is useful as behavioral confirmation, but it is **not** evidence that retail `CRUSADER.EXE` ships with an equivalent built-in toggle.
## Conclusion
The retail executable does contain explicit logic to hide editor-only objects during ordinary gameplay rendering.
Current best model:
1. map/editor objects exist in the same broad world-item/runtime data flow as ordinary items,
2. the active world-item builder in segment `1180` checks `SI_EDITOR` and skips draw-node allocation when that bit is set,
3. if such an item still reached the standard sprite painter, `Item_PaintSprite` would also early-out on the same bit,
4. and no recovered retail debug/cheat/argument lane currently bypasses either gate.
I did **not** recover any retail equivalent of:
- a `showEditorItems` global,
- a command-line switch that enables editor-object rendering,
- a cheat/debug hotkey that enables editor-object rendering,
- or a hidden debugger command that obviously bypasses the `SI_EDITOR` skip.
So the present evidence supports:
- explicit retail hiding behavior exists,
- but no official or currently recovered internal toggle exists to reveal those objects in the shipped game.
If the goal is to see them in the original executable, the most direct current path is no longer searching cheats or args first. It is either:
- finding a second, debug-only display-list builder that bypasses the `1180:0951` gate, or
- patching both recovered `SI_EDITOR` branches in a writable copy.
## Next RE Follow-Up If Revisited
- Recover the exact containing function and wider caller chain for the `1180:0951` world-item builder skip and check whether any alternate debug-only builder exists.
- Inspect the segment `1180` redraw/display-list builders for a second path that draws world items outside the normal world-item allocation branch.
- If a runtime proof is wanted, patch both recovered `SI_EDITOR` branches only on a writable executable copy and test whether editor shapes become visible without destabilizing collision or selection.