18 KiB
18 KiB
Project: Diablo II v1.13c Authentic Isometric Sprite Automap & Minimap System (diablo2-web)
Architecture
- 1.13c Ground Truth Lookup & Weight Engine (
src/formats/automap-table.ts, re-exported atsrc/game/automap-table.ts):- Parses all 3,288 data rows of
data\global\excel\AutoMap.txtby column position (0..4and6, 8, 10, 12, avoiding the duplicate'Type2'header at indices 7 and 9) into a36 × 20bucket table indexed byAUTOMAP_LEVEL_NAMES(0..35, 1-to-1 withLvlTypes.txtIDs1..35) andAUTOMAP_TILE_NAMES(0..19,"fl".."fi"). - Implements
getAutomapCellIdmatching(Style, StartSequence..EndSequence)(Style == -1andStartSequence == -1as wildcards) and selecting a valid frame fromCel1..Cel4(!= -1) via SplitMix64 spatial hashing. - Enforces the
D2Client.dll0x6fba1528cell priority weight table (49(cellId, groupId)pairs across 18 priority groups0..17) when deduplicating cells at identical(xPixel, yPixel)coordinates so doors, exits, stairs, and warps are never overwritten by ordinary walls. - Applies all 1.13c special tile, object, monster, and pre-baked town chunk overrides (
0x6fd89360,0x6fb11d00,0x6fb10c40,0x6fb12760).
- Parses all 3,288 data rows of
- Offline UI & Level Asset Packing Pipeline (
scripts/pack-ui.ts,src/game/d2map.ts,scripts/pack-act-assets.ts):scripts/pack-ui.tsdecodes all 8 automap DC6 sprite sheets fromdata\global\ui\Automap\(MaxiMap.dc6,MaxiMapS.dc6,Act2Map.dc6,Act2MapS.dc6,Act4Map.dc6,Act4MapS.dc6,ExTnMap.dc6,ExTnMapS.dc6) and packs them intopublic/ui/andpublic/assets/ui/automap/with exact per-frame dimensions and offsets, updatingpublic/ui/manifest.jsonandsrc/ui/baked-ui-meta.ts.- Both live
buildIsoMapScene(src/game/d2map.ts) and offlinebakeDs1Variant(scripts/pack-act-assets.ts) generate the 4 ordered automap cell layers (floors,walls,objects,extras) with authentic coordinates (xPixel = (cellX - cellY) * 8,yPixel = (cellX + cellY) * 4 + (tileType >= 16 ? 24 : 0)for tiles;xPixel = trunc(xClient / 10) + 1,yPixel = trunc(yClient / 10) - 3for objects/NPCs) across all 136 levels.
- Runtime Isometric Automap & Minimap UI (
src/ui/minimap.ts,src/ui/hud-manager.ts,src/scene/act-scene.ts):- Renders the 4 automap sprite layers in strict order (
floors -> walls -> objects -> extras) usingscreenX = trunc((xPixel * 10) / nDivisor) - offsetX,screenY = trunc((yPixel * 10) / nDivisor) - offsetY. - Supports Corner Minimap mode (
bMinimap = 1,nDivisor = 20,*MapS.dc6sheets,279×225top-right/top-left viewport clipped viagl.scissorwith no non-authentic background box) and Full-Screen Automap mode (bMinimap = 0,nDivisor = 10,*Map.dc6sheets, arrow-key panning±8px,Homereset, and automatic±160pxhorizontal center shift when left/right HUD panels are open), toggled viaTabandV(plusF9fade andF10names). - Implements Room/chunk-based fog-of-war exploration reveal (
0x6fb12af0&0x6fb12580), 12-segment closed isometricXcrosshairs (0x6fb0f1c0,0x6fb82de8), dynamic portal icons (306/307/300), entrance/warp destination labels (Font6), and top-right right-aligned goldFont16HUD text (Level Name and Difficulty).
- Renders the 4 automap sprite layers in strict order (
Feature Inventory
Every feature from the Survey phase is assigned to a milestone below. No feature is left unassigned.
| # | Feature | Description | Milestone | Source |
|---|---|---|---|---|
| F1 | AutoMap.txt 36 Level Names & 20 Tile Names |
Exact AUTOMAP_LEVEL_NAMES (0..35, 1-to-1 with LvlTypes.txt 1..35 including "1 Monestary") and AUTOMAP_TILE_NAMES (0..19, "fl".."fi"). |
M1 | Survey 1 & 2 |
| F2 | AutoMap.txt 3,288-Row Parser & getAutomapCellId |
Parse all 3,288 rows of data\global\excel\AutoMap.txt by column index, build 36 × 20 buckets, match (Style, StartSequence..EndSequence) (-1 wildcards), and pick valid frame from Cel1..Cel4 (!= -1) via SplitMix64 hash. |
M1 | Survey 1 & 2 |
| F3 | 0x6fba1528 Cell Priority Weight Table & Deduplication |
Extract all 49 (cellId, groupId) pairs across 18 priority groups (0..17), implement getAutomapCellWeight, and deduplicate identical (xPixel, yPixel) cells so high-priority doors, exits, stairs, and warps are never overwritten by ordinary walls. |
M1 | Survey 1 |
| F4 | 1.13c Special Tile, Object & Monster Overrides | Apply hidden wall bit 0 skip (flags & 1), Rogue Campfire (Level 2, style=8, seq=0 -> 405), Act 2 Palace Cellar warp (Level 40, type=8, style=25, seq=0 -> 305), Objects.txt AutoMap column, Stash (267 -> 319), Cain (26 & monster IDs 146,254,265,266,473,532,540 -> 267), Portal (59/60 -> 306 or 300 in Level 4), Wirt (268 -> 309), Khalim Chests (405..407 -> 319), Compelling Orb (366), Arcane Waypoint (402 in levelId=74), and MonStats2.txt (!noMap && automapCel > 0). |
M1 | Survey 1 & 2 |
| F5 | Pre-Baked Town Base Map Chunk Generation (0x6fb12760) |
Generate fSaved=1 Act 2 Lut Gholein (levelId=40, Act2Map 4×5 = 20 frames), fSaved=2 Act 4 Pandemonium Fortress (levelId=103, Act4Map 2×2 = 4 frames), and fSaved=3 Act 5 Harrogath (levelId=109, ExTnMap 2×4 = 8 frames) into extras layer with exact 1.13c coordinates. |
M1 | Survey 1 |
| F6 | Offline Packing of All 8 Automap DC6 Sprite Sheets (scripts/pack-ui.ts) |
Pack MaxiMap.dc6, MaxiMapS.dc6, Act2Map.dc6, Act2MapS.dc6, Act4Map.dc6, Act4MapS.dc6, ExTnMap.dc6, ExTnMapS.dc6 into public/ui/ and public/assets/ui/automap/ with exact per-frame dimensions and offsets; update public/ui/manifest.json and src/ui/baked-ui-meta.ts. |
M1 | Survey 1 & 2 |
| F7 | Live & Offline 4-Layer Automap Generation Across All 136 Levels | Expose layerId (Levels.txt Layer) in src/game/acts.ts, generate { floors, walls, objects, extras } with xPixel = (cellX - cellY) * 8, yPixel = (cellX + cellY) * 4 + (tileType >= 16 ? 24 : 0) in src/game/d2map.ts and scripts/pack-act-assets.ts, fail fast on missing/invalid inputs, re-bake all 136 levels (npm run pack:data), and verify in scripts/verify-packs.ts. |
M2 | Survey 1 & 2 |
| F8 | 4-Layer Isometric Sprite Automap & Minimap Renderer (src/ui/minimap.ts) |
Load all 8 packed automap sprite sheets and render floors -> walls -> objects -> extras in strict order using screenX = trunc((xPixel * 10) / nDivisor) - offsetX, screenY = trunc((yPixel * 10) / nDivisor) - offsetY. |
M3 | Survey 1 & 3 |
| F9 | Corner Minimap (bMinimap=1) vs Full Automap (bMinimap=0), Viewport, Panel Shift & Hotkeys |
Corner Minimap (nDivisor=20, *MapS sheets, 279×225 top-right/top-left viewport clipped via gl.scissor, no background box) and Full Automap (nDivisor=10, *Map sheets, arrow-key panning ±8px, Home reset, ±160px center shift when left/right HUD panels open), toggled via Tab and V (plus F9 fade, F10 names). |
M3 | Survey 1 & 3 |
| F10 | Room/Chunk-Based Fog-of-War Exploration Reveal (0x6fb12af0 & 0x6fb12580) |
Reveal current room/chunk and adjacent same-level rooms when player movement octile distance satisfies `max( | dx | , |
| F11 | 12-Segment Isometric X Crosshair (0x6fb0f1c0, 0x6fb82de8) & Dynamic Portals |
Render 12-segment closed polygon (0x6fb82de8 scaled by 2, shifted (-1, +5) when bMinimap == 1) for player (RGB(0,0,255) / palette 0x97), party/allies (RGB(0,255,0) + Font6 label), mercenaries/summons (RGB(72,160,52) / RGB(244,244,244)), bosses/superuniques (RGB(255,0,0)), and active town portals (MaxiMap(S) frame 306/307/300). |
M3 | Survey 1 & 3 |
| F12 | Entrance/Warp Area Name Labels & Top-Right Gold Font16 HUD Text (0x6fb0f9f0) |
Render centered Font6 area name labels on revealed entrances/warps (and "Stash" on Stash), and top-right right-aligned gold Font16 HUD text (Level Name and Difficulty) at x = screenWidth - 16. |
M3 | Survey 1 & 3 |
| F13 | Full-Spectrum 136-Level Verification & Browser Audit (scripts/verify-automap.ts) |
Verify all 136 level IDs (Acts 1–5 towns including 40, 103, 109, wildernesses, dungeons) produce non-empty valid automap cells and render cleanly in headless Chrome in both bMinimap=1 and bMinimap=0 modes with zero console/page/WebGL errors, plus Tiers 1–5 E2E tests. |
M4 | Survey 1, 2 & 3 |
Milestones
| # | Name | Scope | Dependencies | Status |
|---|---|---|---|---|
| M1 | M1-R1-Automap-Table-And-UI-Sprites |
Implement src/formats/automap-table.ts (and src/game/automap-table.ts re-export), src/formats/automap-table.test.ts (testing all 3,288 rows, bucket lookup, 0x6fba1528 weight deduplication, 0x6fb82de8 crosshair geometry, and special tile/object/town overrides), and update scripts/pack-ui.ts to pack all 8 automap DC6 sprite sheets (MaxiMap(S), Act2Map(S), Act4Map(S), ExTnMap(S)) into public/ui/ and public/assets/ui/automap/. Sync .agents/teamwork/PROJECT.md to root PROJECT.md. |
none | DONE (src/formats/automap-table.ts, src/game/automap-table.ts, src/formats/automap-table.test.ts 11/11 passed, scripts/pack-ui.ts 8 sheets packed) |
| M2 | M2-R2-Map-Scene-Automap-Baking |
Update src/game/acts.ts (layerId), src/game/d2map.ts (buildIsoMapScene), scripts/pack-act-assets.ts (bakeDs1Variant), package.json (pack:data), and scripts/verify-packs.ts to generate and verify the 4 ordered automap cell layers (floors, walls, objects, extras) across all 136 levels in both live buildIsoMapScene and baked scene.json. |
M1 | DONE (all 365 scenes across 136 levels baked in samples/d2-packs, npm run verify:packs 4061/4061 passed) |
| M3 | M3-R3-Isometric-Automap-Minimap-UI |
Rewrite src/ui/minimap.ts and update src/ui/hud-manager.ts and src/scene/act-scene.ts to implement the authentic 1.13c 4-layer isometric sprite renderer, Corner Minimap (bMinimap=1, nDivisor=20, 279×225) & Full Automap (bMinimap=0, nDivisor=10, arrow-key panning, ±160px panel shift), Tab/V/F9/F10 hotkeys, room fog-of-war reveal, 12-segment crosshairs, portal icons, warp labels, top-right gold Font16 HUD text, and unit tests (tests/automap-renderer.test.ts). |
M1, M2 | DONE (src/ui/minimap.ts, src/ui/hud-manager.ts, src/scene/act-scene.ts, tests/automap-renderer.test.ts 13/13 passed, scripts/verify-automap.ts 1556/1556 passed) |
| E2E | E2E-Testing-Track |
Design and implement TEST_INFRA.md, 4-tier requirement-driven opaque-box E2E test suite (tests/e2e-automap-113c/tier1..tier4.test.ts), and 136-level + headless browser verification script (scripts/verify-automap.ts); publish TEST_READY.md. |
M1, M2, M3 | DONE (tests/e2e-automap-113c/tier1..4.test.ts 160/160 passed, TEST_INFRA.md, TEST_READY.md) |
| M4 | M4-Final-E2E-And-Tier5-Hardening |
Phase 1: Pass 100% of E2E tests (Tiers 1–4), npm run typecheck, npm test, npm run verify:packs, npm run verify:acts, npm run verify:all, and scripts/verify-automap.ts across all 136 levels. Phase 2: Tier 5 white-box adversarial coverage hardening (tests/e2e-automap-113c/tier5-adversarial-coverage.test.ts). |
M1, M2, M3, E2E | IN_PROGRESS |
Interface Contracts
1. src/formats/automap-table.ts (re-exported by src/game/automap-table.ts)
export type AutomapAtlasSlot = 0 | 1 | 2 | 3 // 0 = MaxiMap(S), 1 = Act2Map(S), 2 = Act4Map(S), 3 = ExTnMap(S)
export interface AutomapRow {
readonly rowIndex: number
readonly levelName: string
readonly tileName: string
readonly levelTypeId: number // 0..35
readonly tileType: number // 0..19
readonly style: number // -1 or 0..N
readonly startSequence: number // -1 or 0..N
readonly endSequence: number // -1 or 0..N
readonly cels: readonly number[]
readonly celCount: number
}
export interface AutomapTable {
readonly rows: readonly AutomapRow[]
readonly buckets: ReadonlyArray<ReadonlyArray<readonly AutomapRow[]>> // [36][20]
readonly levelRanges: ReadonlyArray<{ readonly start: number; readonly end: number }> // [36]
}
export interface AutomapCell {
readonly cellNo: number
readonly xPixel: number
readonly yPixel: number
readonly cellX: number
readonly cellY: number
readonly fSaved?: AutomapAtlasSlot
readonly weight?: number
}
export interface AutomapLayerData {
readonly layerId: number
readonly levelTypeId: number
readonly floors: readonly AutomapCell[]
readonly walls: readonly AutomapCell[]
readonly objects: readonly AutomapCell[]
readonly extras: readonly AutomapCell[]
}
export const AUTOMAP_LEVEL_NAMES: readonly string[] // length 36 (0..35)
export const AUTOMAP_TILE_NAMES: readonly string[] // length 20 (0..19)
export const AUTOMAP_CELL_WEIGHT_PAIRS: ReadonlyArray<readonly [number, number]> // 49 pairs across groups 0..17
export const AUTOMAP_CROSSHAIR_VERTICES: ReadonlyArray<readonly [number, number]> // 13 pairs from 0x6fb82de8
export const AUTOMAP_CROSSHAIR_SCALED_VERTICES: ReadonlyArray<readonly [number, number]> // 13 pairs scaled by 2
export function parseAutomapTable(raw: Uint8Array | string): AutomapTable
export function getAutomapLevelTypeId(levelNameOrLvlTypeName: string | number): number
export function getAutomapTileType(tileName: string): number
export function getAutomapCellId(
table: AutomapTable,
levelTypeId: number,
tileType: number,
style: number,
sequence: number,
cellX?: number,
cellY?: number,
seed?: number,
): number
export function getAutomapCellWeight(cellNo: number): number
export function computeAutomapTilePixelCoords(cellX: number, cellY: number, tileType: number): { xPixel: number; yPixel: number }
export function computeAutomapObjectPixelCoords(subX: number, subY: number): { xPixel: number; yPixel: number }
export function resolveTileAutomapCellId(
table: AutomapTable,
params: {
levelId: number
levelTypeId: number
tileType: number
style: number
sequence: number
flags?: number
cellX?: number
cellY?: number
seed?: number
},
): number
export function resolveObjectAutomapCellId(params: {
objectClassId: number
autoMapCol: number
levelId?: number
mode?: number
}): number
export function resolveMonsterAutomapCellId(params: {
monsterClassId: number
automapCel: number
noMap?: boolean
isAlive?: boolean
}): number
export function deduplicateAutomapCells(cells: readonly AutomapCell[]): AutomapCell[]
export function generateTownSavedExtras(
levelId: number,
roomOriginSubX?: number,
roomOriginSubY?: number,
roomSubWidth?: number,
): AutomapCell[]
export function getCrosshairSegments(
cx: number,
cy: number,
bMinimap: boolean,
): ReadonlyArray<{ readonly x0: number; readonly y0: number; readonly x1: number; readonly y1: number }>
2. Packed UI Atlases (scripts/pack-ui.ts ↔ src/ui/baked-ui-meta.ts & src/ui/minimap.ts)
scripts/pack-ui.tsoutputs all 8 automap DC6 sprite sheets (MaxiMap.dc6,MaxiMapS.dc6,Act2Map.dc6,Act2MapS.dc6,Act4Map.dc6,Act4MapS.dc6,ExTnMap.dc6,ExTnMapS.dc6) to:public/ui/automap-maxi.png,public/ui/automap-maxis.png,public/ui/automap-act2.png,public/ui/automap-act2s.png,public/ui/automap-act4.png,public/ui/automap-act4s.png,public/ui/automap-extn.png,public/ui/automap-extns.png(and recorded inpublic/ui/manifest.json+src/ui/baked-ui-meta.ts), ANDpublic/assets/ui/automap/MaxiMap.png,MaxiMapS.png,Act2Map.png,Act2MapS.png,Act4Map.png,Act4MapS.png,ExTnMap.png,ExTnMapS.png+public/assets/ui/automap/manifest.jsonwith exact per-frame{ width, height, offsetX, offsetY, x, y }metadata.
3. Scene & Runtime Automap Data (src/game/d2map.ts & scripts/pack-act-assets.ts ↔ src/scene/act-scene.ts & src/ui/minimap.ts)
- Both
IsoMapScene(src/game/d2map.ts) andscene.json(PackSceneJsoninscripts/pack-act-assets.tsandsrc/scene/act-scene.ts) include:automap: AutomapLayerData({ layerId, levelTypeId, floors, walls, objects, extras }).
src/ui/minimap.ts(Minimap) exposes:visible: boolean(toggled byTab)bMinimap: boolean(orisMinimap: boolean, defaulttrue= Corner MinimapnDivisor = 20;false= Full-Screen AutomapnDivisor = 10, toggled byV)fadeMode: 0 | 1 | 2(toggled byF9)showNames: boolean(toggled byF10)panX: number,panY: number(stepped±8pxby Arrow keys when Full-Screen Automap is active; reset byHome)reveal(level: MinimapLevel, subXOrCellX: number, subYOrCellY: number): void(implementing 1.13c room/chunk fog-of-war reveal with octile movement thresholdmax(|dx|,|dy|)*2 + min(|dx|,|dy|) >= 160sub-units / room transition)draw(...)&drawHudOverlay(ctx: CanvasRenderingContext2D, font: D2FontRenderer, ...)(rendering 4 sprite layers, 12-segment crosshairs, dynamic portals, warp/stash labels, and top-right goldFont16Level Name + Difficulty)
Code Layout
src/formats/automap-table.ts&src/game/automap-table.ts: 1.13cAutoMap.txtparser,36 × 20bucket lookup,0x6fba1528weight table & deduplication, special tile/object/monster/town overrides, and0x6fb82de8crosshair geometry.src/formats/automap-table.test.ts: Comprehensive unit test suite forautomap-table.ts.scripts/pack-ui.ts,public/ui/,public/assets/ui/automap/,src/ui/baked-ui-meta.ts: Offline UI packer and packed atlas metadata for all 8 automap DC6 sprite sheets.src/game/acts.ts,src/game/d2map.ts,scripts/pack-act-assets.ts,scripts/verify-packs.ts: Live and offline 4-layer automap cell generation and 136-levelscene.jsonbaking.src/ui/minimap.ts,src/ui/hud-manager.ts,src/scene/act-scene.ts: Authentic 1.13c isometric sprite Automap & Minimap renderer, input bindings (Tab,V,F9,F10, Arrow keys,Home), room reveal, 12-segment crosshairs, warp labels, and top-right HUD text.tests/automap-renderer.test.ts: Unit tests forsrc/ui/minimap.tsrenderer, room reveal, crosshairs, projection, and HUD/input integration.tests/e2e-automap-113c/&scripts/verify-automap.ts: 4-tier requirement-driven E2E test suite, Tier 5 adversarial coverage tests, and 136-level + headless Chrome CDP verification script.