Updated knowledge
This commit is contained in:
parent
5f22a8e2fd
commit
1ad746ba82
21 changed files with 882 additions and 9 deletions
187
docs/map_renderer/editor-item-animation.md
Normal file
187
docs/map_renderer/editor-item-animation.md
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
# Editor-Item Animation In Retail Crusader And The Viewer
|
||||
|
||||
This note closes the current map-viewer animation lane for editor/helper objects that flash, cycle colors, or step through multiple frames.
|
||||
|
||||
The key result is that two different animation systems overlap in this lane:
|
||||
|
||||
- frame animation from Crusader `TYPEFLAG.DAT`
|
||||
- palette-slot animation from the shared `CycleProcess` rows written into palette entries `8..14`
|
||||
|
||||
The viewer now reproduces both, with one important caveat:
|
||||
|
||||
- the frame-selection semantics are evidence-backed, but the exact retail wall-clock tick length is still not directly closed from the live DOS executable, so the viewer uses the recovered `animType/animData/animSpeed` rules with a viewer-side step cadence that stays in family with the already-recovered palette cycle
|
||||
|
||||
## Short Answer
|
||||
|
||||
The flashing editor walls and helper surfaces are not a separate editor-only special effect process.
|
||||
|
||||
Current best model:
|
||||
|
||||
1. many editor/helper shapes are tagged `SI_TRANSL` and use the low Crusader translucent xform slots
|
||||
2. those low slots map heavily to source palette indices `8..14`
|
||||
3. `CycleProcess_Update` advances those same palette rows globally
|
||||
4. when editor/helper sprites use those slots, they inherit the flashing/cycling colors automatically
|
||||
5. some shapes also have nonzero `animType`, so they change frame as well as color
|
||||
|
||||
## Evidence Base
|
||||
|
||||
### Live `CRUSADER.EXE` / Ghidra side
|
||||
|
||||
- `CycleProcess_Update` at `1438:011b` advances the shared palette-cycle rows and writes them back through `SuperVGA_SetPaletteColor` at `1438:0366`
|
||||
- the same recovered cycle rows already explained the F7-family overlay colors in [docs/f7-overlays.md](docs/f7-overlays.md)
|
||||
- `Gamepal_InitXformpalDatStruct` / `Gamepal_LoadXformpalDat` / `Gamepal_ReadXformpal_1028_0348` at `1028:01db`, `1028:0250`, and `1028:0348` are the live xform-palette load path
|
||||
- `ItemType_LoadTypeflagDat` / `Item_GetTypeflagData` at `10f8:0275` / `10f8:0336` are the live typeflag load/access anchors for the 9-byte Crusader `TYPEFLAG.DAT` rows
|
||||
- `Item_PaintSprite` at `1198:02e4` still skips `SI_EDITOR` in the normal gameplay renderer, so most of this animation is normally only visible on debug/editor/helper lanes unless another overlay or engine-side viewer exposes those objects
|
||||
|
||||
### Renderer-side direct pixel evidence
|
||||
|
||||
The earlier translucency pass already established that representative editor/helper translucent shapes are dominated by source palette slots `8`, `9`, `10`, `11`, `13`, and sometimes `14`.
|
||||
|
||||
Representative probe families:
|
||||
|
||||
- invisible/editor walls `0x005A..0x0069`
|
||||
- helper `0x00E9`
|
||||
- `Zappy_Surface_*` `0x044D` / `0x044E`
|
||||
|
||||
That evidence is recorded in [docs/map_renderer/translucency-xformpal.md](translucency-xformpal.md).
|
||||
|
||||
### Open-source Crusader engine cross-check
|
||||
|
||||
The missing frame-animation semantics are closed well enough by the Crusader-specific loaders in ScummVM and Pentagram:
|
||||
|
||||
- Crusader `TYPEFLAG.DAT` is a 9-byte format
|
||||
- byte `4` high nibble = `animType`
|
||||
- byte `5` low nibble = `animData`
|
||||
- byte `5` high nibble = `animSpeed`
|
||||
- byte `6` bit `0` = `SI_EDITOR`
|
||||
|
||||
The ScummVM Crusader item update path also preserves a concrete `animateItem()` switch for `animType` values `1..6`, which is enough to reproduce the frame-selection rules even though the exact DOS tick duration is still not directly timed in the live retail pass.
|
||||
|
||||
## Retail Model
|
||||
|
||||
### 1. Shared palette-cycle rows drive the flashing colors
|
||||
|
||||
This part is already directly evidenced from the live NE executable.
|
||||
|
||||
- `CycleProcess_InitColorTables` seeds the cycle rows
|
||||
- `CycleProcess_Update` advances them
|
||||
- the rows are written back into live palette entries `8..14`
|
||||
|
||||
Those same rows already drive:
|
||||
|
||||
- plain `F7`
|
||||
- `Alt+F7`
|
||||
- `Ctrl+F7`
|
||||
|
||||
The editor/helper flashing lane reuses that same machinery rather than inventing a separate color animator.
|
||||
|
||||
### 2. XFORMPAL makes the low slots matter for translucent editor/helper shapes
|
||||
|
||||
The editor/helper walls and related helper surfaces are often translucent, and their source pixels disproportionately use the low slot family `8..14`.
|
||||
|
||||
That means their apparent color is effectively delegated to the current shared cycle rows.
|
||||
|
||||
In practice, the visible rule is:
|
||||
|
||||
- if a translucent editor/helper sprite is built from those low slots, its hues will flash when the shared cycle rows change
|
||||
|
||||
### 3. Some editor/helper shapes also have real frame animation
|
||||
|
||||
Crusader typeflags also carry per-shape animation metadata.
|
||||
|
||||
Closed field layout for the Crusader 9-byte row:
|
||||
|
||||
- `animType` = byte `4` high nibble
|
||||
- `animData` = byte `5` low nibble
|
||||
- `animSpeed` = byte `5` high nibble
|
||||
|
||||
Recovered/open-source animation behaviors used by the viewer:
|
||||
|
||||
- `animType 1` / `3`: increment through frames; `animData` chooses unconditional, 50%, or block-loop behavior
|
||||
- `animType 2`: random frame changes
|
||||
- `animType 4`: random start, then run through frames
|
||||
- `animType 5`: usecode-driven animation hook; viewer leaves this as static because the live script side is not reproduced here
|
||||
- `animType 6`: loop from frame `1` while leaving frame `0` as a resting/sentinel state
|
||||
|
||||
## Viewer Implementation
|
||||
|
||||
### Metadata fixes
|
||||
|
||||
The map renderer now carries all three Crusader animation fields through the exported shape definition:
|
||||
|
||||
- `animType`
|
||||
- `animData`
|
||||
- `animSpeed`
|
||||
|
||||
The tooltip trait list now shows all three when present.
|
||||
|
||||
### Atlas/reference change
|
||||
|
||||
Static scene exports previously only guaranteed the specific frame used by the authored map item.
|
||||
|
||||
That is not enough for animated shapes.
|
||||
|
||||
The reference build now expands visible shapes with nonzero `animType` to include every frame from the shape archive, so the client can step frames at render time instead of getting stuck on the authored start frame.
|
||||
|
||||
### Palette-cycle reproduction
|
||||
|
||||
The client now carries a compact rendering descriptor for the low xform-cycle slots:
|
||||
|
||||
- source slot `8..14`
|
||||
- the baked atlas RGBA that corresponds to the current XFORMPAL-remapped translucent output
|
||||
|
||||
At render time, translucent editor/helper sprites are copied into a small sprite canvas and any pixel matching one of those slot RGBA values is recolored from the current shared cycle-row RGB for the same slot.
|
||||
|
||||
That lets the viewer animate the same low-slot translucent editor/helper art without inflating the scene payload with raw pixel streams.
|
||||
|
||||
### Frame animation reproduction
|
||||
|
||||
The client now steps `animType` shapes using the recovered Crusader rules above.
|
||||
|
||||
Current approximation boundary:
|
||||
|
||||
- the semantic frame-advance rules are evidence-backed
|
||||
- the exact retail wall-clock tick size is still not directly timed from live DOS execution
|
||||
- the viewer therefore uses a stable viewer-side animation step that stays aligned with the already recovered palette-cycle cadence instead of pretending to know the exact original millisecond rate
|
||||
|
||||
That is good enough to reproduce the visible behavior for map-viewer/editor purposes without overclaiming a closed retail timing constant.
|
||||
|
||||
## Practical Scope In The Viewer
|
||||
|
||||
The current viewer pass targets the objects the user actually asked about:
|
||||
|
||||
- editor-tagged shapes
|
||||
- helper/occluding editor geometry
|
||||
- translucent editor/helper surfaces that inherit the low cycle slots
|
||||
|
||||
The frame-animation support is intentionally a little broader than the pure color-cycle support, because `animType` is real shape metadata and some animated map shapes are not strictly editor-only.
|
||||
|
||||
The palette-cycle recolor stays narrower and only applies to the translucent editor/helper lane where the evidence is strong.
|
||||
|
||||
## What Is Closed Versus Still Open
|
||||
|
||||
### Closed enough for the viewer
|
||||
|
||||
- the shared flashing colors come from `CycleProcess` rows written into palette entries `8..14`
|
||||
- translucent editor/helper sprites really do depend heavily on those same low slots
|
||||
- Crusader `TYPEFLAG.DAT` carries real `animType/animData/animSpeed` fields in the 9-byte row
|
||||
- the viewer can now reproduce both the frame-step and palette-cycle sides in a defensible way
|
||||
|
||||
### Still open
|
||||
|
||||
- the exact live DOS wall-clock interval behind the per-item `gametick` animation updates
|
||||
- whether any remaining helper families need a different XFORMPAL table than the current viewer default for hue reproduction
|
||||
- the full live usecode behavior behind `animType 5` shapes
|
||||
|
||||
## Ghidra Notes Applied In This Batch
|
||||
|
||||
This batch also adds concise live-DB comments to keep the provenance visible at the key shared anchors:
|
||||
|
||||
- `10f8:0336` for the Crusader 9-byte typeflag animation/editor field layout
|
||||
- `1438:011b` for the shared palette-cycle row reuse by both F7 overlays and translucent editor/helper colors
|
||||
|
||||
## Related Notes
|
||||
|
||||
- [docs/map_renderer/translucency-xformpal.md](translucency-xformpal.md)
|
||||
- [docs/editor-object-visibility.md](../editor-object-visibility.md)
|
||||
- [docs/f7-overlays.md](../f7-overlays.md)
|
||||
|
|
@ -24,6 +24,14 @@ This pass widened the renderer research beyond egg and NPC spawner objects and f
|
|||
- 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:
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ Current Crusader viewer work now closes one additional family-4 detail for the `
|
|||
- `quality & 0xFF` is the subtype selector for the authored family-4 usecode class.
|
||||
- The runtime resolves that class as `0x0900 + QLo`.
|
||||
- The currently verified authored subtype sets are:
|
||||
- Remorse: `QLo 0, 1, 2, 13` -> `TRIGEGG`, `ONCEEGG`, `FLOOR1`, `MISS1EGG`
|
||||
- Remorse: `QLo 0, 1, 2, 4, 13` -> `TRIGEGG`, `ONCEEGG`, `FLOOR1`, `CHANGER`, `MISS1EGG`
|
||||
- Regret: `QLo 0, 1, 2, 5, 8, 10, 13, 24` -> `TRIGEGG`, `ONCEEGG`, `FLOOR1`, `MHATCHER`, `CHANGER`, `DOOREGG`, `MISS1`, `VIDEOEGG`
|
||||
- `npcNum` does not behave like a DTABLE row here.
|
||||
- `xRange = (npcNum >> 4) & 0x0f`
|
||||
|
|
@ -88,6 +88,15 @@ Current Crusader viewer work now closes one additional family-4 detail for the `
|
|||
|
||||
That is why the renderer now treats `0x0011` as a proximity/usecode-trigger egg with a projected footprint overlay, a subtype-aware USECODE landing point, and only the narrower local-arrow rules that are actually justified by the recovered subtype body.
|
||||
|
||||
One checked Remorse example now makes the `CHANGER` subtype concrete.
|
||||
|
||||
- Map 13 `fixed:4770` is `item:12473:fixed:17:0:47888:53256:96` with `mapNum = 37`, `quality = 4`, and `npcNum = 64`.
|
||||
- `quality & 0xff = 4` resolves the usecode class to `0x0904`, which matches the extracted Remorse `CHANGER::hatch` body.
|
||||
- `CHANGER::hatch` reads `eggNum = Egg.getEggId(arg_06)` from `mapNum`, walks nearby `roof` candidates, compares each candidate's low quality byte against that egg id, and destroys the matching roof when `Item.getQLo(roof) == eggNum`.
|
||||
- The same local decoded scene contains nearby roof placements (`shape:538`, kind `roof`) whose `quality & 0xff = 37`, matching the egg id from `mapNum`.
|
||||
|
||||
Current safest read for `CHANGER` is therefore `keyed roof-destruction trigger`, not generic collision override logic and not a Regret-only family-4 subtype.
|
||||
|
||||
### Monster eggs
|
||||
|
||||
ScummVM's monster egg accessor exposes:
|
||||
|
|
|
|||
|
|
@ -68,10 +68,11 @@ Current conclusion: `0x024F` frame `0` monster eggs are not the same authoring f
|
|||
|
||||
### `0x04D0` Field Ambiguity Notes
|
||||
|
||||
- The strongest current activation control lives in `MONSTER.slot_0F enterFastArea`, not in DTABLE. That script only checks `0x04D0` objects when `frame == 0`, then blocks the automatic enter-area lane if `mapNum & 0x08` is set.
|
||||
- The strongest current activation control lives in `MONSTER.slot_0F enterFastArea`, not in DTABLE. That script only checks `0x04D0` objects when `frame == 0`, then reaches the automatic enter-area lane when `(mapNum & 0x08) == 0`.
|
||||
- Current safest read: `frame 0` is the only state that participates in the automatic `enterFastArea` spawn path, while `frame 1` skips that hook and is therefore more likely to be used in paired or externally signaled setups.
|
||||
- Confirmed map-1 and map-248 pairs now also show that `frame 0 controller` should not be collapsed into `visible spawned NPC`. In the strongest checked auto-enabled cases, the paired frame-1 record is the better practical preview of the NPC that actually appears.
|
||||
- `mapNum` is not consistently populated across `0x04D0` objects. Some records carry a non-zero map value and others leave it at `0`, so the field is probably contextual control data rather than a universal NPC selector.
|
||||
- Within that contextual control data, bit `0x08` is now evidence-backed as an `auto-enter disabled` flag for the `MONSTER.enterFastArea` lane.
|
||||
- Within that contextual control data, bit `0x08` is now evidence-backed as an `auto-enter blocked` flag for the `MONSTER.enterFastArea` lane.
|
||||
- `quality` is also not specific to the DTABLE row. For example, `quality = 1285` shows up on unrelated non-`0x04D0` shapes in the exported scenes, so that value should not be read as proof of a particular NPC identity.
|
||||
- `quality` low byte still does not look like the primary `spawn immediately vs wait` control. The current exported scripts do not use it in `MONSTER.enterFastArea`, although Regret `ALARMHAT` does compare nearby `0x04D0` `Item.getQLo(...)` values against difficulty lanes `0/1/2` before equipping those helpers.
|
||||
- DTABLE row `0` is named `Crusader`, but the open-source engine does not use DTABLE row `0` to bootstrap the player. ScummVM's `CruGame::startGame()` takes the main actor stats from `getNPCDataForShape(1)`, while the generic Crusader actor-creation path accepts `npcNum = 0` as an ordinary DTABLE index.
|
||||
|
|
@ -164,6 +165,13 @@ For editable fixed-record `0x04D0` items, the inspector now also exposes two evi
|
|||
|
||||
The side panel now also exposes a dedicated `Monster Spawners` audit list for `0x04D0` records, including a filter for the `auto-enter blocked` subset. Clicking an entry centers and pins that spawner so its raw fields and editable controls can be audited quickly.
|
||||
|
||||
Recent viewer follow-up tightened that audit lane further:
|
||||
|
||||
- fixed-record `0x04D0` items now surface their stable `fixed:<mapSourceIndex>` ids prominently and provide copy buttons
|
||||
- the tooltip keeps the explicit `☑ auto-enabled` versus `☒ dormant` state label, while the scene preview itself now carries the color signal instead of a separate on-map checkbox badge
|
||||
- tooltip and list wording now separate the verified frame-0 control lane from the current practical frame-1 preview heuristic instead of asserting that frame 0 always supplies the final visible NPC
|
||||
- paired `0x04D0` previews now use a single carrier per pair instead of drawing both records: blue for the currently active preview carrier, red for dormant controller previews
|
||||
|
||||
The viewport's `Show verified link arrows` overlay now draws two evidence-backed link families:
|
||||
|
||||
- teleport eggs point from teleporter eggs to teleport destinations that share the same teleport ID
|
||||
|
|
@ -180,7 +188,9 @@ When a valid DTABLE row is present, the viewport renders a semitransparent blue
|
|||
- `0x04D0` DTABLE-backed editor spawners
|
||||
- `0x024F` frame `0` Remorse monster eggs that carry a non-zero `npcNum`
|
||||
|
||||
The current renderer uses frame `0` for NPC previews by default, except for `Observer`, which is forced to frame `0x00F` because the earlier frames are blank/broken in the retail assets.
|
||||
The current renderer still uses each resolved row's known preview frame, except for `Observer`, which is forced to frame `0x00F` because the earlier frames are blank/broken in the retail assets. For paired `0x04D0` rows, the UI now treats frame-1 previews as the stronger practical cue in confirmed auto-enabled examples, even though the frame-0 control path remains the verified automatic trigger lane.
|
||||
|
||||
The stronger reason for that rule is no longer just a few hand-picked examples. A broader cache scan across the active Remorse map-1 and map-248 exports found many auto-enabled mismatched valid pairs plus many auto-enabled cases where the frame-0 controller row does not resolve to a valid Remorse DTABLE preview at all while the paired frame-1 row does. That is why the renderer now suppresses duplicate pair ghosts instead of tinting both sides.
|
||||
|
||||
Representative exported scene pairs:
|
||||
|
||||
|
|
|
|||
|
|
@ -65,14 +65,15 @@ That is why the viewer opens `TRIGGER.slot_20` for pinned `0x04B1` helpers inste
|
|||
- `quality & 0xFF` is the subtype selector for this family.
|
||||
- The runtime resolves the usecode class as `0x0900 + QLo`.
|
||||
- Current authored subtype sets are:
|
||||
- Remorse: `0, 1, 2, 13` -> `TRIGEGG`, `ONCEEGG`, `FLOOR1`, `MISS1EGG`
|
||||
- Remorse: `0, 1, 2, 4, 13` -> `TRIGEGG`, `ONCEEGG`, `FLOOR1`, `CHANGER`, `MISS1EGG`
|
||||
- Regret: `0, 1, 2, 5, 8, 10, 13, 24` -> `TRIGEGG`, `ONCEEGG`, `FLOOR1`, `MHATCHER`, `CHANGER`, `DOOREGG`, `MISS1`, `VIDEOEGG`
|
||||
- `npcNum` packs `xRange = high nibble` and `yRange = low nibble`.
|
||||
- Crusader multiplies each nibble by `64` world units and uses a `+/-48` Z window for the trigger test.
|
||||
- `TRIGEGG` and `ONCEEGG` route into `TRIGGER.slot_20` on hatch/unhatch, so the renderer now draws local arrows to nearby `0x04B1` helpers by shared `QLo`.
|
||||
- Regret `MHATCHER` scans nearby frame-0 `0x04D0` helpers whose `QLo` matches the egg id in `mapNum`, so the renderer now draws that local helper lane too.
|
||||
- Regret `DOOREGG` scans nearby family-1 door objects whose `QLo` matches the egg id in `mapNum`, so the renderer now exposes that local door lane.
|
||||
- `FLOOR1`, `CHANGER`, `MISS1*`, and `VIDEOEGG` remain subtype-aware in the tooltip and USECODE target, but they do not yet justify a generic local-arrow rule.
|
||||
- Map-13 Remorse `CHANGER` example `fixed:4770` now gives the subtype a concrete local read: egg id `37` (`mapNum`) sits beside roof tiles whose `QLo` is also `37`, matching the extracted `CHANGER::hatch` body that destroys nearby roofs keyed by egg id.
|
||||
- `FLOOR1`, `CHANGER`, `MISS1*`, and `VIDEOEGG` remain subtype-aware in the tooltip and USECODE target, but they still do not justify a generic local-arrow rule.
|
||||
|
||||
### `0x04C9 TIMER`
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue