fix(ui): restore authentic D2 v1.13c NPC dialog popup menu (dialog.cpp + npcmenu.cpp + boxpieces.dc6) #489

Closed
opened 2026-09-27 05:31:41 +00:00 by troytt · 0 comments
Owner

Bug Description

Clicking an NPC in town currently displays a custom placeholder CSS/Canvas rectangle popup menu (drawNpcMenu in src/ui/world-panels.ts) rather than the authentic Diablo II v1.13c NPC dialog menu:

  • Reference screenshot: https://screenshot-v2.corp.google.com/3ot2t65n1dan0
  • Current defects:
    1. Uses a plain ctx.strokeRect gold rectangle (#9c824c) and a horizontal divider line beneath the NPC name instead of the 22-piece carved stone border (data\global\ui\menu\boxpieces.dc6) drawn by D2Client.dll 0x6fb6e4c0.
    2. Uses hardcoded w = 180, rowH = 26, headerH = 30 instead of the dynamic Font16 text measurement and line spacing from DIALOG_Create / DIALOG_ComputeLayout (0x6fb53580 / 0x6fb532d0: width = maxLineWidth + 20, height = sum(lineStepY) + 15, header lineStepY = 21, option lineStepY = 15).
    3. Uses a gray background bar (rgba(156, 130, 76, 0.28)) on hover instead of the D2Client.dll nSelectionStyle = 1 ([esi+0x5c] == 1 set in NPCMENU_CreateMainMenu 0x6faf8c21) blue text highlight (color = 3 / #6969ff) with default selection on the first selectable option (nSelectedLine = 1).
    4. Appends fake bilingual English parentheticals (交谈 (Talk), 交易 (Trade), 赌博 (Gamble), 离开 (Cancel)) instead of authentic .tbl localized strings (3381 "talk", 3396 "trade", 3334 "trade/repair", 3398 "gamble", 3397 "hire", 4020 "Identify Items", 4142 "cancel", etc.).

1.13c Binary Ground Truth (D2Client.dll dialog.cpp & npcmenu.cpp)

  1. Stone Border Frame (DATA\GLOBAL\UI\menu\boxpieces.dc6, 0x6fb6e4c0):
    • DIALOG_Draw (0x6fb53840) fills the interior with D2GFX_DrawRectangle(left, top, left + width, top + height, 0, 1) (dark translucent fill, no horizontal separator line) and calls 0x6fb6e4c0(&rc) where rc = { left, top, right: left + width - 1, bottom: top + height - 1 }:
      • Top edge: x = rc.left + 12 .. rc.right - 13 step 12, frame 2 + (++i % 6) at (x, rc.top + 12)
      • Bottom edge: x = rc.left + 13 .. rc.right - 13 step 12, frame 16 + (++i % 6) at (x, rc.bottom + 10)
      • Left edge: y = rc.top + 22 .. rc.bottom - 1 step 12, frame 10 + (++i % 3) at (rc.left - 4, y)
      • Right edge: y = rc.top + 24 .. rc.bottom - 1 step 12, frame 13 + (++i % 3) at (rc.right - 6, y)
      • Corners: TL frame 0 at (rc.left, rc.top + 12), TR frame 1 at (rc.right - 11, rc.top + 12), BL frame 8 at (rc.left, rc.bottom + 2), BR frame 9 at (rc.right - 11, rc.bottom + 2)
  2. Dialog Layout & Typography (0x6fb53580, 0x6fb536f0, 0x6fb53912):
    • Font: Font16 (D2Win_SetFont(1))
    • Header line: color = 4 (Gold), lineStepY = 21, bSelectable = 0, centered (relX = floor((width - textWidth + 1) / 2) + 1)
    • Option lines: color = 0 (White), lineStepY = 15, bSelectable = 1, centered; when i == nSelectedLine and nSelectionStyle == 1, color = 3 (Blue). First option (index = 1) is selected initially (0x6fb53829).
    • Hit-testing (0x6fb534b0): mouseX > left + 15 && mouseX < left + width - 15 && mouseY > rowY - 11 && mouseY < rowY + 4 where rowY = top + accumY + lineStepY.
  3. Offline Asset Pipeline Alignment:
    • Extract data\global\ui\menu\boxpieces.dc6 (22 frames) and data\global\ui\CURSOR\focus16.dc6 (7 frames) in scripts/pack-ui-assets.ts into public/ui/ and src/ui/baked-ui-meta.ts.
## Bug Description Clicking an NPC in town currently displays a custom placeholder CSS/Canvas rectangle popup menu (`drawNpcMenu` in `src/ui/world-panels.ts`) rather than the authentic Diablo II v1.13c NPC dialog menu: - Reference screenshot: https://screenshot-v2.corp.google.com/3ot2t65n1dan0 - Current defects: 1. Uses a plain `ctx.strokeRect` gold rectangle (`#9c824c`) and a horizontal divider line beneath the NPC name instead of the 22-piece carved stone border (`data\global\ui\menu\boxpieces.dc6`) drawn by `D2Client.dll` `0x6fb6e4c0`. 2. Uses hardcoded `w = 180`, `rowH = 26`, `headerH = 30` instead of the dynamic `Font16` text measurement and line spacing from `DIALOG_Create` / `DIALOG_ComputeLayout` (`0x6fb53580` / `0x6fb532d0`: `width = maxLineWidth + 20`, `height = sum(lineStepY) + 15`, header `lineStepY = 21`, option `lineStepY = 15`). 3. Uses a gray background bar (`rgba(156, 130, 76, 0.28)`) on hover instead of the `D2Client.dll` `nSelectionStyle = 1` (`[esi+0x5c] == 1` set in `NPCMENU_CreateMainMenu` `0x6faf8c21`) blue text highlight (`color = 3` / `#6969ff`) with default selection on the first selectable option (`nSelectedLine = 1`). 4. Appends fake bilingual English parentheticals (`交谈 (Talk)`, `交易 (Trade)`, `赌博 (Gamble)`, `离开 (Cancel)`) instead of authentic `.tbl` localized strings (`3381` `"talk"`, `3396` `"trade"`, `3334` `"trade/repair"`, `3398` `"gamble"`, `3397` `"hire"`, `4020` `"Identify Items"`, `4142` `"cancel"`, etc.). ## 1.13c Binary Ground Truth (`D2Client.dll` `dialog.cpp` & `npcmenu.cpp`) 1. **Stone Border Frame (`DATA\GLOBAL\UI\menu\boxpieces.dc6`, `0x6fb6e4c0`)**: - `DIALOG_Draw` (`0x6fb53840`) fills the interior with `D2GFX_DrawRectangle(left, top, left + width, top + height, 0, 1)` (dark translucent fill, no horizontal separator line) and calls `0x6fb6e4c0(&rc)` where `rc = { left, top, right: left + width - 1, bottom: top + height - 1 }`: - Top edge: `x = rc.left + 12 .. rc.right - 13` step `12`, frame `2 + (++i % 6)` at `(x, rc.top + 12)` - Bottom edge: `x = rc.left + 13 .. rc.right - 13` step `12`, frame `16 + (++i % 6)` at `(x, rc.bottom + 10)` - Left edge: `y = rc.top + 22 .. rc.bottom - 1` step `12`, frame `10 + (++i % 3)` at `(rc.left - 4, y)` - Right edge: `y = rc.top + 24 .. rc.bottom - 1` step `12`, frame `13 + (++i % 3)` at `(rc.right - 6, y)` - Corners: TL frame `0` at `(rc.left, rc.top + 12)`, TR frame `1` at `(rc.right - 11, rc.top + 12)`, BL frame `8` at `(rc.left, rc.bottom + 2)`, BR frame `9` at `(rc.right - 11, rc.bottom + 2)` 2. **Dialog Layout & Typography (`0x6fb53580`, `0x6fb536f0`, `0x6fb53912`)**: - Font: `Font16` (`D2Win_SetFont(1)`) - Header line: `color = 4` (Gold), `lineStepY = 21`, `bSelectable = 0`, centered (`relX = floor((width - textWidth + 1) / 2) + 1`) - Option lines: `color = 0` (White), `lineStepY = 15`, `bSelectable = 1`, centered; when `i == nSelectedLine` and `nSelectionStyle == 1`, `color = 3` (Blue). First option (`index = 1`) is selected initially (`0x6fb53829`). - Hit-testing (`0x6fb534b0`): `mouseX > left + 15 && mouseX < left + width - 15 && mouseY > rowY - 11 && mouseY < rowY + 4` where `rowY = top + accumY + lineStepY`. 3. **Offline Asset Pipeline Alignment**: - Extract `data\global\ui\menu\boxpieces.dc6` (22 frames) and `data\global\ui\CURSOR\focus16.dc6` (7 frames) in `scripts/pack-ui-assets.ts` into `public/ui/` and `src/ui/baked-ui-meta.ts`.
Sign in to join this conversation.
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#489
No description provided.