fix(ui): restore authentic D2 v1.13c NPC dialog popup menu (Fixes #489) #495

Merged
troytt merged 1 commits from fix/issue-489-npc-menu-ui into main 2026-09-27 06:41:34 +00:00
Owner

Summary

Fixes #489 by restoring the 100% authentic Diablo II v1.13c NPC dialog popup menu (D2Client.dll dialog.cpp + npcmenu.cpp + boxpieces.dc6).

Root Cause & 1.13c Binary Ground Truth (D2Client.dll)

  1. Stone Border Frame (0x6fb6e4c0, DATA\GLOBAL\UI\menu\boxpieces.dc6):
    • Replaced the custom placeholder CSS/Canvas rectangle (ctx.strokeRect('#9c824c')), horizontal header divider line, and gray hover highlight bar (rgba(64, 50, 28, 0.75)) in drawNpcMenu with drawStoneBoxFrame (0x6fb6e4c0), which fills the interior with dark translucent background (left + 1, top, width - 2, height - 2) and tiles all 22 14x15 carved stone pieces from boxpieces.dc6 (top frames 2..7, bottom frames 16..21, left frames 10..12, right frames 13..15, and corners 0, 1, 8, 9, each blitted at (x, y - 15)).
  2. Dynamic Font16 Layout & Open-Interval Hit-Testing (0x6fb53580, 0x6fb534b0, 0x6faf6880):
    • Implemented computeNpcDialogLayout (0x6fb53580): width = maxLineWidth + 20, height = 21 + options.length * 15 + 15, left = anchorX - trunc(width / 2), top = anchorY - 21, clamped within [10, 790] horizontally and [10, 542] vertically.
    • Implemented hitTestNpcDialogOption (0x6fb534b0): open-interval hit-testing logicalX > left + 15 && logicalX < left + width - 15 && logicalY > rowY - 11 && logicalY < rowY + 4, preserving the current selection when the cursor moves outside selectable lines (0x6fb533de).
    • Updated interactWithNpc (src/scene/act-scene.ts) to project the -150 world-pixel head offset (0x6faf6880: 0x6faf68de: add edi, -150; cmp eax, 20) above the NPC's feet so the popup menu sits cleanly above the NPC sprite without covering it, and suppressed duplicate DOM #dialog / #status / overhead #labels while the HUD menu is open.
  3. Typography, Selection Highlight & .tbl Strings (0x6faf8c50, 0x6fb53912, 0x6fb53988):
    • Header line rendered in Font16 Gold (color = 4), option lines in Font16 Blue (color = 3) when selected and White (color = 0) when unselected, with option 0 selected initially (hoveredOptionIdx: 0).
    • Removed non-authentic bilingual suffixes ((Talk), (Trade/Repair), (Cancel)) in favor of canonical .tbl localized strings (3381 交談, 4020 辨視物品, 3334 交易/修理, 3396 交易, 3398 賭博, 4142 取消).
  4. Offline UI Asset Pack Pipeline (scripts/pack-ui-assets.ts, scripts/pack-ui.ts):
    • Extracted data/global/ui/menu/boxpieces.dc6 (public/ui/boxpieces.png, 308x15, 22 frames) and data/global/ui/CURSOR/focus16.dc6 (public/ui/focus16.png, 160x20, 8 frames) and registered them in public/ui/manifest.json and src/ui/baked-ui-meta.ts.

Verification

  • npx vitest run tests/vendor-panel-ui.test.ts tests/input-controls.test.ts tests/challenger-m2-stress.test.ts: 54/54 passed (including MPQ byte-for-byte parity tests for boxpieces.png and focus16.png, computeNpcDialogLayout, drawStoneBoxFrame, hitTestNpcDialogOption, and drawNpcMenu).
  • npx tsx scripts/verify-vendor-ui.ts: Verified in headless Chromium on Charsi, Akara, and Gheed.
## Summary Fixes #489 by restoring the 100% authentic Diablo II v1.13c NPC dialog popup menu (`D2Client.dll` `dialog.cpp` + `npcmenu.cpp` + `boxpieces.dc6`). ### Root Cause & 1.13c Binary Ground Truth (`D2Client.dll`) 1. **Stone Border Frame (`0x6fb6e4c0`, `DATA\GLOBAL\UI\menu\boxpieces.dc6`)**: - Replaced the custom placeholder CSS/Canvas rectangle (`ctx.strokeRect('#9c824c')`), horizontal header divider line, and gray hover highlight bar (`rgba(64, 50, 28, 0.75)`) in `drawNpcMenu` with `drawStoneBoxFrame` (`0x6fb6e4c0`), which fills the interior with dark translucent background `(left + 1, top, width - 2, height - 2)` and tiles all 22 `14x15` carved stone pieces from `boxpieces.dc6` (top frames `2..7`, bottom frames `16..21`, left frames `10..12`, right frames `13..15`, and corners `0, 1, 8, 9`, each blitted at `(x, y - 15)`). 2. **Dynamic `Font16` Layout & Open-Interval Hit-Testing (`0x6fb53580`, `0x6fb534b0`, `0x6faf6880`)**: - Implemented `computeNpcDialogLayout` (`0x6fb53580`): `width = maxLineWidth + 20`, `height = 21 + options.length * 15 + 15`, `left = anchorX - trunc(width / 2)`, `top = anchorY - 21`, clamped within `[10, 790]` horizontally and `[10, 542]` vertically. - Implemented `hitTestNpcDialogOption` (`0x6fb534b0`): open-interval hit-testing `logicalX > left + 15 && logicalX < left + width - 15 && logicalY > rowY - 11 && logicalY < rowY + 4`, preserving the current selection when the cursor moves outside selectable lines (`0x6fb533de`). - Updated `interactWithNpc` (`src/scene/act-scene.ts`) to project the `-150` world-pixel head offset (`0x6faf6880`: `0x6faf68de: add edi, -150; cmp eax, 20`) above the NPC's feet so the popup menu sits cleanly above the NPC sprite without covering it, and suppressed duplicate DOM `#dialog` / `#status` / overhead `#labels` while the HUD menu is open. 3. **Typography, Selection Highlight & `.tbl` Strings (`0x6faf8c50`, `0x6fb53912`, `0x6fb53988`)**: - Header line rendered in `Font16` Gold (`color = 4`), option lines in `Font16` Blue (`color = 3`) when selected and White (`color = 0`) when unselected, with option `0` selected initially (`hoveredOptionIdx: 0`). - Removed non-authentic bilingual suffixes (`(Talk)`, `(Trade/Repair)`, `(Cancel)`) in favor of canonical `.tbl` localized strings (`3381` `交談`, `4020` `辨視物品`, `3334` `交易/修理`, `3396` `交易`, `3398` `賭博`, `4142` `取消`). 4. **Offline UI Asset Pack Pipeline (`scripts/pack-ui-assets.ts`, `scripts/pack-ui.ts`)**: - Extracted `data/global/ui/menu/boxpieces.dc6` (`public/ui/boxpieces.png`, `308x15`, 22 frames) and `data/global/ui/CURSOR/focus16.dc6` (`public/ui/focus16.png`, `160x20`, 8 frames) and registered them in `public/ui/manifest.json` and `src/ui/baked-ui-meta.ts`. ## Verification - `npx vitest run tests/vendor-panel-ui.test.ts tests/input-controls.test.ts tests/challenger-m2-stress.test.ts`: **54/54 passed** (including MPQ byte-for-byte parity tests for `boxpieces.png` and `focus16.png`, `computeNpcDialogLayout`, `drawStoneBoxFrame`, `hitTestNpcDialogOption`, and `drawNpcMenu`). - `npx tsx scripts/verify-vendor-ui.ts`: Verified in headless Chromium on Charsi, Akara, and Gheed.
troytt added 1 commit 2026-09-27 06:39:21 +00:00
troytt force-pushed fix/issue-489-npc-menu-ui from ce00574f83 to d998a0551b 2026-09-27 06:41:14 +00:00 Compare
troytt merged commit 57c57c2662 into main 2026-09-27 06:41:34 +00:00
troytt deleted branch fix/issue-489-npc-menu-ui 2026-09-27 06:41:34 +00:00
Sign in to join this conversation.
No reviewers
No Label
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: troytt/diablo2-web#495
No description provided.