Crusader_Decomp/docs/f7-overlays.md

241 lines
12 KiB
Markdown
Raw Permalink Normal View History

2026-04-02 01:15:16 +02:00
# F7 Debug Overlays In Retail CRUSADER.EXE
This note documents the three cheat-gated F7-family overlays in the live NE `CRUSADER.EXE` database and what each one represents on screen.
The practical keyboard side is already summarized in [docs/ne-segment1.md](docs/ne-segment1.md). This file focuses on the overlay consumers themselves, the recovered geometry math, and what the map viewer can safely reproduce from static scene data.
## Toggle points
Inside `World_HandleKeyboardInput_13e8_14b4`, the three overlay hotkeys toggle three different globals before forcing a camera refresh through the active camera process:
- `13e8:1a7c` toggles `1478:2bca` for plain `F7`
- `13e8:1a50` toggles `1478:2bc9` for `Alt+F7`
- `13e8:1a20` toggles `1478:0ee0` for `Ctrl+F7`
All three are still gated by the broader cheat/debug latch at `1478:0844`, so they are part of the Laurie/debug family rather than always-on retail UI features.
The live database and exported evidence agree on the three consumers:
- `Camera_1180_15ef` handles the plain `F7` grid and the `Ctrl+F7` egg-hatcher overlay
- `Snap_1058_0814` handles the `Alt+F7` snap overlay
- `EggHatcher_1090_0921` handles the `Ctrl+F7` diamond outlines through the shared helper at `1180:1ce5`
## Palette colors and flash timing
The overlay colors are not hardcoded RGB values inside the overlay consumers. They are palette-indexed line draws that ride the shared `CycleProcess` palette animator in segment `1438`.
Recovered retail evidence now closes the important pieces:
- `CycleProcess_InitColorTables` (`1438:0480`) seeds the runtime color-cycle tables at `1478:6848..1478:6871`
- `CycleProcess_Update` (`1438:011b`) advances those rows by `+2` intensity units per update tick and writes them into palette entries `8..14`
- `Snap_1058_0814` pushes color index `0x09`
- `EggHatcher_1090_0921` pushes color index `0x0d`
- `Camera_1180_15ef` stores and uses color index `0x0e` for the plain `F7` grid lines
So the current overlay-to-palette mapping is:
- plain `F7` grid = palette color `14`
- `Alt+F7` snap overlay = palette color `9`
- `Ctrl+F7` egg-hatcher overlay = palette color `13`
The recovered cycle rows explain the visible behavior:
- color `9` is a blue ramp that rises `0 -> 63` in steps of `2` and wraps
- color `13` is a white ramp that rises `0 -> 63` in all three channels in steps of `2` and wraps
- color `14` is primarily a green ramp, but its row is flagged through the special `word-flag == 1` path, so each wrap injects an additional small random-looking RGB offset before the next ramp cycle
For the viewer that means the correct match is not one static CSS color per overlay. The viewer should simulate the same discrete palette-cycle phase and then derive the on-screen RGB from the live palette index assigned to each overlay.
The retail game is updating those colors in its normal engine/palette lane, not in a browser-style wall-clock timer. For the viewer it is acceptable to run the same phase logic at a slower cadence if that produces a closer visual impression on a modern display.
## Plain F7: coarse world-cell grid
### What it represents
Plain `F7` is the simple debug background grid. It is not an object-family overlay and it is not tied to egg processes.
Current best read from the live camera path is:
- the grid is aligned to the world coordinate lattice
- each coarse cell is `0x200` world units wide in both world axes
- the visible shape is an isometric diamond because the game projects the world lattice into screen space
That means the overlay is best understood as a coarse world-cell coordinate grid useful for orientation and spatial debugging.
### Viewer reproduction rule
The viewer should not anchor this grid to the middle of the screen. The right model is:
- use the world origin-aligned `0x200 x 0x200` lattice
- project every visible coarse cell that intersects the current viewport
- let the lattice continue indefinitely across the visible world instead of only drawing a fixed `3 x 3` patch around the viewport center
That is closer to the game-space meaning of the overlay than a screen-centered approximation.
## Ctrl+F7: egg-hatcher trigger diamonds
### What it represents
`Ctrl+F7` is not a third generic grid. It visualizes the trigger footprint used by live `EggHatcherProcess` objects.
The live control flow is:
- `13e8:1a20` toggles `1478:0ee0`
- `Camera_1180_15ef` checks `1478:0ee0`
- eligible live `EggHatcherProcess` objects are routed through `EggHatcher_1090_0921`
- `EggHatcher_1090_0921` calls the shared diamond helper at `1180:1ce5`
The process/runtime note in [docs/ne-segment1.md](docs/ne-segment1.md) already closes the gameplay meaning: for non-monster egg families, these processes watch an avatar footprint against egg-centered X/Y/Z trigger windows and fire hatch or unhatch behavior on boundary crossings.
### Geometry source
The recovered runtime uses the same egg range values that the process runner tests:
- `Egg_GetXRange(itemno)` returns the high nibble of the egg-range byte
- `Egg_GetYRange(itemno)` returns the low nibble of the same byte
- each nibble expands in steps of `0x40` world units in the current viewer-side evidence model
- the vertical window is about `+/- 0x30` Z units
For authored family-4 usecode-trigger eggs in the viewer, the packed range byte already exists in the decoded map item payload, so the static reproduction is defensible.
### Why it can appear blank in game
This overlay walks live `EggHatcherProcess` objects, not all possible egg-family map items. A blank `Ctrl+F7` result only means there is no currently eligible live egg-hatcher outline being drawn at that moment.
## Alt+F7: SnapProcess rectangles, not generic egg coverage
### The important correction
The first viewer approximation treated `Alt+F7` as a broad snap-oriented egg-family overlay. The recovered runtime is narrower than that.
The key proof comes from the object-entry path that feeds the snap process:
- the live/extracted logic only calls `Snap_AddSnapEgg` when `g_snapProcess != 0`
- and when the entering item's shape number is exactly `0x04fe`
The exported live note at `10a0:2c82` summarizes the same behavior directly: `when shape 0x4fe enters, we trigger the snap process`.
So `Alt+F7` is not a generic "all eggs" overlay. Its runtime source is the SnapProcess egg list, and the recovered producer for that list is currently tied to shape `0x04FE` items.
### Geometry source
The geometry is recovered more tightly now too, and the important detail is that the helper is not drawing a symmetric world-centered diamond from `item_x,item_y` alone.
The `Snap_1058_0814` consumer does this per active SnapProcess entry:
- `qHiHiNibble = (Item_GetQHi(itemno) >> 4) & 0x0f`
- `qHiLoNibble = Item_GetQHi(itemno) & 0x0f`
- `mapSigned = sign_extend_8(Item_GetMapArray(itemno))`
- `npcSigned = sign_extend_8(Item_GetNPCNum(itemno))`
Then it builds the helper inputs as:
$$
x_{helper} = item_x + qHiHiNibble \cdot 0x20 + mapSigned \cdot 0x20
$$
$$
y_{helper} = item_y + qHiLoNibble \cdot 0x20 + npcSigned \cdot 0x20
$$
$$
xRange_{helper} = qHiHiNibble \cdot 2
$$
$$
yRange_{helper} = qHiLoNibble \cdot 2
$$
and finally calls the shared overlay primitive with color `0x09`:
- `FUN_1180_1ce5(x_helper, y_helper, item_z, xRange_helper, yRange_helper, 9)`
That helper works in projected screen space. Starting from the projected helper center, it emits a four-segment polygon with these deltas:
- point `A` = center `+ ( yRange * 0x10, -yRange * 0x08 )`
- point `B` = point `A + ( -xRange * 0x10, -xRange * 0x08 )`
- point `C` = point `B + ( -yRange * 0x10, yRange * 0x08 )`
- close back to center
So the game is not drawing a simple `min/max` world rectangle and then projecting it. It is feeding an offset center and doubled nibble step counts into a dedicated screen-space overlay helper. That is why the earlier viewer reconstruction read too small.
In practical terms for the viewer:
- use the `BRO_BOOT` / `0x04fe` item as the authored source row
- offset the helper center by the packed signed bytes and nibble terms exactly as above
- use doubled nibble counts as helper step counts
- reproduce the helper polygon itself rather than a centered approximation
This is materially different from the `Ctrl+F7` egg-hatcher path:
- `Ctrl+F7` is tied to live `EggHatcherProcess` trigger windows
- `Alt+F7` is tied to the SnapProcess list and the explicit `Snap_GetSnapEggRange` rectangle math for shape `0x04fe`
### What the overlay represents
Current safest read is:
- `Alt+F7` visualizes snap-process coverage regions associated with the subset of runtime objects that are inserted into the SnapProcess egg list
- those regions are authored through the `0x04fe` item's own `QHi`, `mapNum`, and `npcNum` bytes rather than the generic family-4 egg-hatcher nibble layout
- this is related to, but not identical with, the family-4 egg trigger overlay shown by `Ctrl+F7`
### Is this related to camera snapping?
Probably related to the game's broader snap subsystem, but not proven as a pure camera-only feature.
What is directly supported by the recovered retail evidence is:
- the overlay consumer is `Snap_1058_0814`, not a camera helper
- the producer path adds only shape `0x04fe` (`BRO_BOOT`) items into the SnapProcess list
- an inline live note at `10a0:2c82` says that when shape `0x04fe` enters, it triggers the snap process
- another recovered note inside the SnapProcess body marks one related branch as `snap to this egg`
So the defensible statement is:
- yes, it is related to the engine's snap behavior
- no, the currently recovered evidence does not justify calling it only a camera-snap overlay
The best present label is still `SnapProcess coverage` or `snap-region overlay`, not `camera snap grid`. The subsystem may influence camera behavior, controlled-NPC placement, or both, but the proven path is the SnapProcess path itself.
### Why `BRO_BOOT` appears to own it
The viewer-side tie to `BRO_BOOT` is not arbitrary. That shape tie is exactly what the retail producer currently shows.
Recovered producer behavior:
- when `g_snapProcess != 0`
- and a newly entering item has shape `0x04fe`
- the game calls `Snap_AddSnapEgg`
That is why the static viewer reproduction binds Alt+F7 to `BRO_BOOT` items. It is not because `BRO_BOOT` is being used as a loose guess for a nearby effect; it is because the current live evidence says those are the items that seed the SnapProcess overlay list.
## Viewer guidance
The map viewer can reproduce these overlays with different confidence levels:
- plain `F7`: strong static reproduction, because it is just a world-aligned coarse grid
- `Ctrl+F7`: strong static reproduction for authored family-4 trigger eggs, because the packed X/Y range bytes are already decoded in scene data
- `Alt+F7`: narrower static reproduction using only shape `0x04fe` items and the exact recovered SnapProcess helper inputs and helper polygon; broader egg-family approximations should be avoided
Current viewer policy after the latest correction:
- keep the palette colors matched to the recovered cycle-color indices
- allow the viewer-side animation cadence to be slower than retail if the result reads more like the in-game flash on modern displays
- prefer reproducing the helper polygon exactly over re-expressing it as a simplified centered world diamond
## Evidence anchors
- `13e8:1a20`, `13e8:1a50`, `13e8:1a7c`: hotkey toggles in `World_HandleKeyboardInput_13e8_14b4`
- `1058:0137`: `Snap_GetSnapEggRange`
- `1058:021b`: `Snap_AddSnapEgg`
- `1058:0814`: `Alt+F7` overlay consumer
- `1090:0921`: `Ctrl+F7` overlay consumer
- `1180:15ef`: camera-side overlay helper
- `1180:1ce5`: shared diamond helper
- `10a0:2c82`: live note that shape `0x04fe` entering triggers the snap process
Cross-reference evidence also lives in:
- [docs/ne-segment1.md](docs/ne-segment1.md)
- [docs/crusader-disasm-reference.md](docs/crusader-disasm-reference.md)
- [exports/CRUSADER.EXE.xml](exports/CRUSADER.EXE.xml)