-`STATIC_REGRET` in this workspace now includes `FIXED.DAT`
- the renderer still accepts `--fixed-dat` so alternate map copies can be tested without changing the rest of the static asset path
The immediate goal is practical and narrow: load a fixed map, expand glob placements, decode the required shapes from `SHAPES.FLX`, apply `GAMEPAL.PAL`, and render a deterministic PNG.
## Source Cross-Checks Used
The first renderer is grounded in the overlapping parts of three sources rather than in ad hoc guesses.
1. Pentagram Crusader shape/map loaders
-`convert/crusader/ConvertShapeCrusader.cpp`
-`graphics/Shape.cpp`
-`graphics/ShapeFrame.cpp`
-`world/Map.cpp`
-`world/MapGlob.cpp`
-`graphics/Palette.cpp`
-`graphics/TypeFlags.cpp`
2. ScummVM Ultima8 Crusader paths
-`gfx/shape_archive.cpp`
-`gfx/type_flags.cpp`
-`world/map.cpp`
-`world/glob_egg.cpp`
-`world/coord_utils.h`
-`world/item_sorter.cpp`
-`world/sort_item.cpp`
3. Local workspace evidence
-`docs/scummvm-crusader-reference.md`
-`docs/pentagram-crusader-reference.md`
-`docs/raw-0007-rendering.md`
-`crusader-disasm/shapedata.txt`
-`crusader-disasm/mapdump/mapdump.py`
## File Formats Used By The First Tool
### `FIXED.DAT`
The map container is treated as a header plus a map table:
- map count at file offset `0x54`
- map table at file offset `0x80`
- each table row is `<u32 offset, u32 size>`
Each map payload is read as packed 16-byte item records:
-`x: u16`
-`y: u16`
-`z: u8`
-`shape: u16`
-`frame: u8`
-`flags: u16`
-`quality: u16`
-`npc_num: u8`
-`map_num: u8`
-`next: u16`
Crusader-specific coordinate adjustment matches the Pentagram and ScummVM runtime loaders:
- world `x = disk_x * 2`
- world `y = disk_y * 2`
### `GLOB.FLX`
`GLOB.FLX` is handled as a normal FLEX archive, not as a one-off format.
Each non-empty glob object contains:
- object count: `u16`
- repeated entries of `x:u8 y:u8 z:u8 shape:u16 frame:u8`
Glob expansion matches the Crusader `GlobEgg::enterFastArea()` rule in ScummVM/Pentagram:
The current tool does not yet use the footpad values for full ItemSorter-equivalent overlap resolution.
## Current Projection And Painting Rules
The renderer anchors each shape at the same world-to-screen bottom point used by the runtime shape painter:
$$
screen_x = \frac{x - y}{4}
$$
$$
screen_y = \frac{x + y}{8} - z
$$
Frame placement then follows the shape-frame offsets used by the runtime sorter:
- unflipped: `left = screen_x - xoff`
- flipped: `left = screen_x + xoff - width`
-`top = screen_y - yoff`
The renderer now uses a ScummVM/Pentagram-style dependency graph sorter rather than a plain scalar key.
The current implementation ports the crucial parts of `SortItem` and `ItemSorter`:
- footpad-derived world boxes from `TYPEFLAG.DAT`
- screen-diamond overlap and containment checks
-`below()` ordering rules for flat pieces, tall pieces, roofs, translucent items, and Crusader inventory-item families
- dependency expansion so overlapping items are painted only after everything behind them
This is materially better than the initial `z / x+y` heuristic and is the main path for reducing wall and prop overdraw artifacts, though it still omits some of the engine's more specialized runtime-only cases.
The batch runner also accepts an optional `start_map end_map` range for partial runs while validating changes:
```cmd
render_maps.bat remorse 1 3
```
You can also forward extra renderer arguments through the `RENDER_ARGS` environment variable, for example a bounded validation run:
```cmd
set RENDER_ARGS=--world-rect 0 0 16384 16384
render_maps.bat regret 5 5
```
Batch behavior notes:
- empty maps are skipped in batch mode and do not produce PNG or JSON outputs
- there is no default max-pixel cap anymore; full-map renders are attempted unless you pass `--max-pixels`
- batch item-count skipping is now opt-in only; set `BATCH_MAX_ITEMS` to a positive value if you want the batch runner to skip very large full maps
- the renderer emits progress by default every 2000 items; pass `--progress-every 0` through `RENDER_ARGS` to silence it
- batch runs now default to `include_editor=true`, `include_hidden_markers=true`, and `include_roofs=false`
Metadata notes:
-`invalid_items` contains a capped sample of bad `(shape, frame, x, y, z, source, reason)` records so broken `FIXED.DAT` references can be inspected without rerunning a scan
-`usage` is conservative: it reports known reference-backed map hints when available and otherwise stays `unknown`; it does not yet prove orphan status
-`base_item_summary` reports how many roof, editor, egg-family, invisible, and NPC-linked records were present in the raw map payload
-`filters` records whether the render included roofs, editor shapes, and hidden marker content
This tool is a start, not a complete engine clone.
Current gaps:
1. It renders `FIXED.DAT` only. It does not yet merge save-state or `NONFIXED.DAT` style movable items.
2. It expands globs, but it does not yet emulate broader fast-area/runtime-driven materialization behavior.
3. It skips several egg-family placements instead of trying to visualize their hidden runtime helpers.
4. It now implements the core dependency graph sorter, but it still omits experimental occlusion grouping and some runtime-only sprite/highlight cases.
5. It does not yet consume `ANIM.DAT`, `DAMAGE.FLX`, `DTABLE.FLX`, `WPNOVLAY.DAT`, or palette transforms such as `XFORMPAL.DAT`.
6. It uses `GAMEPAL.PAL` directly and does not yet model alternate or transformed palettes.
7. It writes a plain RGBA PNG using only the standard library; there is no zoomed viewer, tile atlas exporter, or sprite manifest yet.
8. Some maps still contain invalid shape/frame references in `FIXED.DAT`; the renderer now skips those items instead of aborting the whole map, but that means some broken placements remain missing until the source of those references is understood.
1. Validate and tune the dependency sorter against representative Remorse and Regret rooms, especially tall wall seams and dense prop clusters.
2. Add optional atlas export for all shapes touched by a chosen map.
3. Add a second path for movable/dynamic content once the relevant Crusader save/runtime files are pinned down for both games.
4. Compare a few rendered regions against known in-game screenshots to tighten projection and ordering errors.
5. Add optional per-item manifest output with `(shape, frame, x, y, z, source)` rows for debugging bad composites.
6. Revisit raw `0007` rendering notes and the live executable only if the current Pentagram/ScummVM overlap model proves insufficient for specific remaining errors.