Crusader_Decomp/docs/editor-object-visibility.md

9.3 KiB

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:

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:

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:

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:

if (data[6] & 0x01) si._flags |= ShapeInfo::SI_EDITOR;

Pentagram TypeFlags.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:

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.