241 lines
12 KiB
Markdown
241 lines
12 KiB
Markdown
|
|
# 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)
|