From bdb3b4145a4cab8c816fb1cd6c06cf29eb3cf52c Mon Sep 17 00:00:00 2001 From: troytt <47798984@qq.com> Date: Sat, 26 Sep 2026 11:45:37 +0000 Subject: [PATCH] fix(ui): restore 1.13c buysell.dc6 vendor panel, tabs, and repair buttons --- public/ui/manifest.json | 13 +- public/ui/vendor-bg.png | Bin 64901 -> 40764 bytes public/ui/vendor-tabs.png | Bin 0 -> 12951 bytes scripts/pack-ui-assets.ts | 24 +- scripts/pack-ui.ts | 25 +- scripts/verify-vendor-ui.ts | 131 +++++++- src/ui/baked-ui-meta.ts | 3 +- src/ui/hud-manager.ts | 25 +- src/ui/world-panels.ts | 564 +++++++++++++++++++++++----------- tests/vendor-panel-ui.test.ts | 474 ++++++++++++++++++++++++++++ 10 files changed, 1044 insertions(+), 215 deletions(-) create mode 100644 public/ui/vendor-tabs.png create mode 100644 tests/vendor-panel-ui.test.ts diff --git a/public/ui/manifest.json b/public/ui/manifest.json index eb956a8..8582bb0 100644 --- a/public/ui/manifest.json +++ b/public/ui/manifest.json @@ -1,6 +1,6 @@ { "version": "1.13c", - "decodedDc6Count": 680, + "decodedDc6Count": 681, "dc6DecodeFailures": 0, "atlasWidth": 1024, "atlasHeight": 3573, @@ -2503,9 +2503,9 @@ "skl": "flpskl", "skz": "flpskl", "hrb": "flphrb", - "cm1": "flpmss", - "cm2": "flptrch", - "cm3": "flpmss", + "cm1": "flpchm1", + "cm2": "flpchm2", + "cm3": "flpchm3", "rps": "flprps", "rpl": "flprpl", "bps": "flpbps", @@ -3211,7 +3211,7 @@ "flpci1": "flpci1", "Ormus' Robes": "flpqlt_blac", "flpqlt_blac": "flpqlt_blac", - "Gheed's Fortune": "flpmss", + "Gheed's Fortune": "flpchm3", "Stormlash": "flpfla_dgry", "flpfla_dgry": "flpfla_dgry", "Halaberd's Reign": "flpba5", @@ -3482,7 +3482,7 @@ "Sander's Superstition": "flpbwn", "gold": "flpgld", "Horadric Cube": "flpbox", - "gheeds": "flpmss", + "gheeds": "flpchm3", "anni": "flpmss", "torch": "flptrch", "flphax": "flphax", @@ -13134,6 +13134,7 @@ "stashBg": "/ui/stash-bg.png", "cubeBg": "/ui/cube-bg.png", "vendorBg": "/ui/vendor-bg.png", + "vendorTabs": "/ui/vendor-tabs.png", "popbelt": "/ui/popbelt.png", "minipanel": "/ui/minipanel.png", "minipanelBtns": "/ui/minipanel-btns.png", diff --git a/public/ui/vendor-bg.png b/public/ui/vendor-bg.png index a943034994364f84609a4ebac7b09212d0180338..679b3b1bc548ad2724039d363b0402ef5ca7dbbc 100644 GIT binary patch delta 40273 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 delta 64600 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%0D=XbyIQy+fkwKcM3gY5HPo;}>RUfkq{ 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 diff --git a/public/ui/vendor-tabs.png b/public/ui/vendor-tabs.png new file mode 100644 index 0000000000000000000000000000000000000000..f3953d824f4c24486713e92c7b12f1fa731739c4 GIT binary patch literal 12951 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 N002ovPDHLkV1l7FhmimP literal 0 HcmV?d00001 diff --git a/scripts/pack-ui-assets.ts b/scripts/pack-ui-assets.ts index 943c978..45ec9d9 100644 --- a/scripts/pack-ui-assets.ts +++ b/scripts/pack-ui-assets.ts @@ -280,7 +280,21 @@ async function main(): Promise { const cubeBg = await decodeUiDc6('data\\global\\ui\\Panel\\supertransmogrifier.dc6') savePng('cube-bg.png', 320, 432, stitch320x432Panel(cubeBg, 0), false) - const vendorBg = await decodeUiDc6('data\\global\\ui\\Panel\\NPCInv.dc6') + const vendorBg = await decodeUiDc6('data\\global\\ui\\Panel\\buysell.dc6') + const vbf = vendorBg.groups[0]?.frames + const expectedVendorBgFrames = [ + { w: 256, h: 256 }, + { w: 64, h: 256 }, + { w: 256, h: 176 }, + { w: 64, h: 176 }, + ] + if ( + !vbf || + vbf.length < 4 || + expectedVendorBgFrames.some((exp, i) => vbf[i]?.width !== exp.w || vbf[i]?.height !== exp.h) + ) { + throw new Error('Invalid buysell.dc6 frame geometry: expected 4 frames (256x256, 64x256, 256x176, 64x176)') + } savePng('vendor-bg.png', 320, 432, stitch320x432Panel(vendorBg, 0), false) // 6. Pop-up Belt, Mini-panel, Buttons, Cursors, and Equipment Silhouettes @@ -306,6 +320,14 @@ async function main(): Promise { const bsStrip = stitchHorizontalStrip(buySellBtn.groups[0]!.frames) savePng('buysellbtn.png', bsStrip.width, bsStrip.height, bsStrip.pixels) + const buySellTabs = await decodeUiDc6('data\\global\\ui\\Panel\\buyselltabs.dc6') + const bstFrames = buySellTabs.groups[0]?.frames + if (!bstFrames || bstFrames.length !== 8 || bstFrames.some(f => f.width !== 79 || f.height !== 31)) { + throw new Error('Invalid buyselltabs.dc6 frame geometry: expected 8 frames of 79x31') + } + const bstStrip = stitchHorizontalStrip(bstFrames) + savePng('vendor-tabs.png', bstStrip.width, bstStrip.height, bstStrip.pixels) + const levelBtn = await decodeUiDc6('data\\global\\ui\\Panel\\Level.dc6') const lvlStrip = stitchHorizontalStrip(levelBtn.groups[0]!.frames) savePng('level-btn.png', lvlStrip.width, lvlStrip.height, lvlStrip.pixels) diff --git a/scripts/pack-ui.ts b/scripts/pack-ui.ts index 413a472..e215a51 100644 --- a/scripts/pack-ui.ts +++ b/scripts/pack-ui.ts @@ -323,7 +323,21 @@ async function main(): Promise { const cubeBg = await decodeUiDc6('data/global/ui/Panel/supertransmogrifier.dc6') savePng('cube-bg.png', 320, 432, stitch320x432Panel(cubeBg, 0), false) - const vendorBg = await decodeUiDc6('data/global/ui/Panel/NPCInv.dc6') + const vendorBg = await decodeUiDc6('data/global/ui/Panel/buysell.dc6') + const vbf = vendorBg.groups[0]?.frames + const expectedVendorBgFrames = [ + { w: 256, h: 256 }, + { w: 64, h: 256 }, + { w: 256, h: 176 }, + { w: 64, h: 176 }, + ] + if ( + !vbf || + vbf.length < 4 || + expectedVendorBgFrames.some((exp, i) => vbf[i]?.width !== exp.w || vbf[i]?.height !== exp.h) + ) { + throw new Error('Invalid buysell.dc6 frame geometry: expected 4 frames (256x256, 64x256, 256x176, 64x176)') + } savePng('vendor-bg.png', 320, 432, stitch320x432Panel(vendorBg, 0), false) // 6. Pop-up Belt, Mini-panel, Buttons, Cursors @@ -349,6 +363,14 @@ async function main(): Promise { const bsStrip = stitchHorizontalStrip(buySellBtn.groups[0]!.frames) savePng('buysellbtn.png', bsStrip.width, bsStrip.height, bsStrip.pixels) + const buySellTabs = await decodeUiDc6('data/global/ui/Panel/buyselltabs.dc6') + const bstFrames = buySellTabs.groups[0]?.frames + if (!bstFrames || bstFrames.length !== 8 || bstFrames.some(f => f.width !== 79 || f.height !== 31)) { + throw new Error('Invalid buyselltabs.dc6 frame geometry: expected 8 frames of 79x31') + } + const bstStrip = stitchHorizontalStrip(bstFrames) + savePng('vendor-tabs.png', bstStrip.width, bstStrip.height, bstStrip.pixels) + const levelBtn = await decodeUiDc6('data/global/ui/Panel/Level.dc6') const lvlStrip = stitchHorizontalStrip(levelBtn.groups[0]!.frames) savePng('level-btn.png', lvlStrip.width, lvlStrip.height, lvlStrip.pixels) @@ -994,6 +1016,7 @@ async function main(): Promise { stashBg: '/ui/stash-bg.png', cubeBg: '/ui/cube-bg.png', vendorBg: '/ui/vendor-bg.png', + vendorTabs: '/ui/vendor-tabs.png', popbelt: '/ui/popbelt.png', minipanel: '/ui/minipanel.png', minipanelBtns: '/ui/minipanel-btns.png', diff --git a/scripts/verify-vendor-ui.ts b/scripts/verify-vendor-ui.ts index e4df10e..c7ea822 100644 --- a/scripts/verify-vendor-ui.ts +++ b/scripts/verify-vendor-ui.ts @@ -20,9 +20,10 @@ function resolveChromeBinary(): string { } async function main() { - const outputDir = - process.env.VENDOR_AUDIT_OUT ?? - '/usr/local/google/home/taodao/.gemini/jetski/brain/ba5784fe-f165-4cff-aa7e-c3cfdf22c420' + const outputDir = process.env.VENDOR_AUDIT_OUT + if (!outputDir) { + throw new Error('VENDOR_AUDIT_OUT environment variable is required') + } mkdirSync(outputDir, { recursive: true }) ensurePacksAvailable() @@ -128,9 +129,64 @@ async function main() { await sleep(200) } - // 1. Open Charsi NPC Menu + // 1. Exhaustive audit of all 16 trade NPCs and 6 gamble NPCs + const fullAudit = await evalJs(`(() => { + const hud = window.__d2webHudInstance; + const wp = hud.worldPanels; + hud.charSheet.attrs.level = 30; + const tradeNpcs = [ + 'Akara', 'Charsi', 'Gheed', + 'Fara', 'Drognan', 'Elzix', 'Lysander', + 'Ormus', 'Hratli', 'Asheara', 'Alkor', + 'Halbu', 'Jamella', + 'Larzuk', 'Malah', 'Anya', + ]; + const gambleNpcs = ['Gheed', 'Elzix', 'Alkor', 'Jamella', 'Anya', 'Nihlathak']; + const tradeResults = []; + for (const name of tradeNpcs) { + const ok = hud.openVendorSession(name, 'trade', 0); + if (!ok) throw new Error('Failed to open trade vendor: ' + name); + hud.render(); + const frames = [0, 1, 2, 3].map(slot => wp.getVendorButtonFrame(slot)); + tradeResults.push({ + name, + hcIdx: wp.activeVendorDescriptor.hcIdx, + canRepair: wp.activeVendorDescriptor.canRepair, + layout: wp.getActiveVendorButtonLayout(), + frames, + activeTab: wp.activeVendorTab, + itemsCount: wp.getActiveVendorPlacements().length, + }); + } + const gambleResults = []; + for (const name of gambleNpcs) { + const ok = hud.openVendorSession(name, 'gamble', 1); + if (!ok) throw new Error('Failed to open gamble vendor: ' + name); + hud.render(); + const frames = [0, 1, 2, 3].map(slot => wp.getVendorButtonFrame(slot)); + gambleResults.push({ + name, + hcIdx: wp.activeVendorDescriptor.hcIdx, + layout: wp.getActiveVendorButtonLayout(), + frames, + itemsCount: wp.getActiveVendorPlacements().length, + }); + } + return { tradeCount: tradeResults.length, gambleCount: gambleResults.length, tradeResults, gambleResults }; + })()`) + console.log( + `Audited ${fullAudit.tradeCount} trade NPCs and ${fullAudit.gambleCount} gamble NPCs:`, + JSON.stringify({ + blacksmiths: fullAudit.tradeResults.filter((r: any) => r.canRepair).map((r: any) => `${r.name}:${r.frames.join(',')}`), + traders: fullAudit.tradeResults.filter((r: any) => !r.canRepair).map((r: any) => `${r.name}:${r.frames.join(',')}`), + gamblers: fullAudit.gambleResults.map((r: any) => `${r.name}:${r.frames.join(',')}`), + }), + ) + + // 2. Open Charsi NPC Menu const menuInfo = await evalJs(`(() => { const hud = window.__d2webHudInstance; + hud.closeAllPanels(); hud.openNpcMenuByName('Charsi', 400, 220); hud.render(); return { @@ -139,16 +195,17 @@ async function main() { }; })()`) console.log('NPC Menu:', JSON.stringify(menuInfo)) - await sleep(250) + await sleep(200) await takeScreenshot('vendor_npc_menu_charsi') - // 2. Open Charsi Trade/Repair 4-tab 10x10 Vendor UI + // 3. Open Charsi Trade/Repair 4-tab 10x10 Vendor UI (buysell.dc6 + buyselltabs.dc6 + [2,4,6,18] buttons) const charsiInfo = await evalJs(`(() => { const hud = window.__d2webHudInstance; hud.charSheet.attrs.level = 24; hud.openVendorSession('Charsi', 'trade', 0); const wp = hud.worldPanels; - wp.activeVendorTab = 'armor'; + wp.selectVendorTab('armor'); + wp.hoveredVendorButtonSlot = null; const armorItems = wp.vendorTabPlacements.armor; const docking = hud.getDockingLayout(); const deltaLeft = -docking.marginW; @@ -156,8 +213,8 @@ async function main() { const first = armorItems[0]; wp.hoveredVendorItem = { item: first.item, - x: 80 + 17 + first.col * 29 + 14, - y: 60 + 64 + first.row * 29 + 14, + x: 80 + 16 + first.col * 29 + 14, + y: 60 + 63 + first.row * 29 + 14, col: first.col, row: first.row, }; @@ -171,22 +228,67 @@ async function main() { return { leftPanel: hud.leftPanel, rightPanel: hud.rightPanel, + buttonLayout: wp.getActiveVendorButtonLayout(), armorCount: wp.vendorTabPlacements.armor.length, weapons1Count: wp.vendorTabPlacements.weapons1.length, + weapons2Count: wp.vendorTabPlacements.weapons2.length, miscCount: wp.vendorTabPlacements.misc.length, firstItem: armorItems[0] ? { nameZh: armorItems[0].item.nameZh, buyCost: armorItems[0].item.buyCost } : null, }; })()`) console.log('Charsi Trade UI:', JSON.stringify(charsiInfo)) - await sleep(250) + await sleep(200) await takeScreenshot('vendor_trade_charsi') - // 3. Open Gheed Gamble UI + // 4. Charsi Repair Latched + Repair All Hover Tooltip + const charsiRepairInfo = await evalJs(`(() => { + const hud = window.__d2webHudInstance; + const wp = hud.worldPanels; + wp.hoveredVendorItem = null; + hud.inventory.hoveredItem = null; + // Damage an equipped item to produce a non-zero Repair All cost + const equippedList = Object.values(hud.inventory.equipped).filter(Boolean); + if (equippedList.length > 0 && equippedList[0].durability) { + equippedList[0].durability.current = Math.max(1, Math.floor(equippedList[0].durability.max / 2)); + } + wp.vendorTradeState = 'repair'; + wp.hoveredVendorButtonSlot = 3; + hud.render(); + return { + vendorTradeState: wp.vendorTradeState, + repairFrame: wp.getVendorButtonFrame(2), + repairAllCost: wp.getRepairAllCost(hud.inventory), + }; + })()`) + console.log('Charsi Repair UI:', JSON.stringify(charsiRepairInfo)) + await sleep(200) + await takeScreenshot('vendor_repair_charsi') + + // 5. Akara Trader UI ([Buy, Sell, Blank, Close] + Close hover) + const akaraInfo = await evalJs(`(() => { + const hud = window.__d2webHudInstance; + hud.openVendorSession('Akara', 'trade', 0); + const wp = hud.worldPanels; + wp.hoveredVendorItem = null; + hud.inventory.hoveredItem = null; + wp.hoveredVendorButtonSlot = 3; + hud.render(); + return { + buttonLayout: wp.getActiveVendorButtonLayout(), + activeTab: wp.activeVendorTab, + }; + })()`) + console.log('Akara Trade UI:', JSON.stringify(akaraInfo)) + await sleep(200) + await takeScreenshot('vendor_trade_akara') + + // 6. Open Gheed Gamble UI const gheedInfo = await evalJs(`(() => { const hud = window.__d2webHudInstance; hud.charSheet.attrs.level = 60; hud.openVendorSession('Gheed', 'gamble', 1); const wp = hud.worldPanels; + wp.hoveredVendorButtonSlot = null; const gambleItems = wp.gamblePlacements; const docking = hud.getDockingLayout(); const deltaLeft = -docking.marginW; @@ -194,8 +296,8 @@ async function main() { const first = gambleItems[0]; wp.hoveredVendorItem = { item: first.item, - x: 80 + 17 + first.col * 29 + 14, - y: 60 + 64 + first.row * 29 + 14, + x: 80 + 16 + first.col * 29 + 14, + y: 60 + 63 + first.row * 29 + 14, col: first.col, row: first.row, }; @@ -207,12 +309,13 @@ async function main() { } hud.render(); return { + buttonLayout: wp.getActiveVendorButtonLayout(), gambleCount: gambleItems.length, firstGamble: gambleItems[0] ? { nameZh: gambleItems[0].item.nameZh, buyCost: gambleItems[0].item.buyCost } : null, }; })()`) console.log('Gheed Gamble UI:', JSON.stringify(gheedInfo)) - await sleep(250) + await sleep(200) await takeScreenshot('vendor_gamble_gheed') } finally { ws?.close() diff --git a/src/ui/baked-ui-meta.ts b/src/ui/baked-ui-meta.ts index d4148e7..ddc13b9 100644 --- a/src/ui/baked-ui-meta.ts +++ b/src/ui/baked-ui-meta.ts @@ -83,7 +83,7 @@ export const ACT_QUEST_ASSET_MAP: Readonly { this.cursor.handleMouseUp() + this.worldPanels.releaseMomentaryVendorButton() }) this.hudCanvas.addEventListener('contextmenu', (e) => { @@ -875,7 +880,7 @@ export class HudManager { } } - // Vendor Repair or Quick-Sell from Player Inventory + // Vendor Repair or Sell/Quick-Sell from Player Inventory if (this.leftPanel === 'vendor') { if ( rightX >= INV_GRID_ORIGIN.x && @@ -890,12 +895,12 @@ export class HudManager { ) if (hitIdx !== -1) { const hit = this.inventory.gridItems[hitIdx]! - if (this.worldPanels.repairCursorMode) { + if (this.worldPanels.vendorTradeState === 'repair') { this.worldPanels.repairSingleUiItem(hit.item, this.inventory) this.syncPublishedState() return } - if (e.shiftKey || e.button === 2) { + if (this.worldPanels.vendorTradeState === 'sell' || e.shiftKey || e.button === 2) { const res = this.worldPanels.sellToActiveVendor(hit.item, this.inventory) if (res.ok) { this.inventory.gridItems.splice(hitIdx, 1) @@ -905,7 +910,7 @@ export class HudManager { return } } - } else if (this.worldPanels.repairCursorMode && this.inventory.hoveredItem) { + } else if (this.worldPanels.vendorTradeState === 'repair' && this.inventory.hoveredItem) { this.worldPanels.repairSingleUiItem(this.inventory.hoveredItem.item, this.inventory) this.syncPublishedState() return @@ -1110,12 +1115,14 @@ export class HudManager { stashBgImg: this.images.get('stashBg') ?? null, cubeBgImg: this.images.get('cubeBg') ?? null, vendorBgImg: this.images.get('vendorBg') ?? null, + vendorTabsImg: this.images.get('vendorTabs') ?? null, buySellBtnImg: this.images.get('buySellBtn') ?? null, questsAtlasImg: this.images.get('questsAtlas') ?? null, itemsAtlasImg: this.images.get('itemsAtlas') ?? null, waypointTabsImg: this.images.get('waypointTabs') ?? null, waypointIconsImg: this.images.get('waypointIcons') ?? null, questTabsImg: this.images.get('questTabs') ?? null, + inventory: this.inventory, }, this.font, ) diff --git a/src/ui/world-panels.ts b/src/ui/world-panels.ts index 02bf6fa..6e9bd7d 100644 --- a/src/ui/world-panels.ts +++ b/src/ui/world-panels.ts @@ -3,7 +3,7 @@ * 1. Quest Log Panel (`Menu\questbackground.dc6`, hotkey `Q`) * 2. Waypoint Teleport Panel (`Menu\waygatebackground.dc6`, hotkey `V` or clicking scene Waypoint) * 3. Private Stash (`Panel\TradeStash.dc6`, 6x8 grid) & Horadric Cube (`Panel\supertransmogrifier.dc6`, 3x4 grid) - * 4. Town NPC Stone Interaction Menu (`npcmenu.cpp`) & Vendor Panel (`Panel\NPCInv.dc6`) + * 4. Town NPC Stone Interaction Menu (`npcmenu.cpp`) & Vendor Panel (`Panel\buysell.dc6`, `Panel\buyselltabs.dc6`, `Panel\buysellbtn.dc6`) * 5. Monster Target Top Plaque (`text.cpp`), Item Hover Tooltip (`showitems.cpp`), * Ground `Alt` Item Labels, Area Entry Gothic Banner (`Font30`), and Escape Menu (`menupanel.dc6`). */ @@ -34,16 +34,129 @@ export const STASH_DEPOSIT_BTN_BOUNDS = { x: 80 + 68, y: 60 + 386, w: 76, h: 26 export const STASH_WITHDRAW_BTN_BOUNDS = { x: 80 + 154, y: 60 + 386, w: 76, h: 26 } as const export const VENDOR_PANEL_ORIGIN = { x: 80, y: 60, width: 320, height: 432, w: 320, h: 432 } as const -export const VENDOR_GRID_ORIGIN = { x: 80 + 17, y: 60 + 64, cols: 10, rows: 10, cellPx: 29 } as const -export const VENDOR_CLOSE_BTN_BOUNDS = { x: 80 + 272, y: 60 + 388, w: 32, h: 32 } as const -export const VENDOR_REPAIR_BTN_BOUNDS = { x: 80 + 18, y: 60 + 386, w: 76, h: 26 } as const -export const VENDOR_REPAIR_ALL_BTN_BOUNDS = { x: 80 + 102, y: 60 + 386, w: 96, h: 26 } as const +/** `Inventory.txt` row `Monster2`: gridLeft=96 (`80 + 16`), gridTop=123 (`60 + 63`), 10x10 cells of 29x29px */ +export const VENDOR_GRID_ORIGIN = { x: 80 + 16, y: 60 + 63, cols: 10, rows: 10, cellPx: 29 } as const -export const VENDOR_TABS: readonly { readonly id: VendorTabId; readonly labelZh: string; readonly labelEn: string }[] = [ - { id: 'armor', labelZh: '护甲', labelEn: 'Armor' }, - { id: 'weapons1', labelZh: '武器 I', labelEn: 'Weapons I' }, - { id: 'weapons2', labelZh: '武器 II', labelEn: 'Weapons II' }, - { id: 'misc', labelZh: '杂物', labelEn: 'Misc' }, +/** + * D2Client.dll vendor button geometry (`0x6fb45df1`, `0x6fb4c27a`, `0x6fb3f260`): + * - Mode-3 button table X offsets (relative to panel X0=80): `{116, 169, 221, 273}` + * - Sprite draw position: `x = X0 + tableX - 1` (`115, 168, 220, 272` relative), `y = panelTop + 385` (`445` absolute) + * - Hit-test open intervals: `relX ∈ (tableX, tableX + 45)`, `relY ∈ (371, 415)` + */ +export const VENDOR_BUTTON_TABLE_X = [116, 169, 221, 273] as const +export const VENDOR_BUTTON_DRAW_Y = 385 +export const VENDOR_BUTTON_HIT_Y_MIN = 371 +export const VENDOR_BUTTON_HIT_Y_MAX = 415 +export const VENDOR_BUTTON_HIT_W = 45 + +export type VendorButtonKind = 'blank' | 'buy' | 'sell' | 'repair' | 'repairAll' | 'close' +export type VendorTradeState = 'idle' | 'buy' | 'sell' | 'repair' + +export interface VendorButtonSpec { + readonly kind: VendorButtonKind + readonly enabled: boolean + readonly baseFrame: number + readonly pressedFrame: number + readonly strIdx: number + readonly labelZh: string + readonly labelEn: string + readonly paramA: number +} + +export const VENDOR_BUTTON_SPECS: Readonly> = { + blank: { + kind: 'blank', + enabled: false, + baseFrame: 0, + pressedFrame: 1, + strIdx: 0, + labelZh: '', + labelEn: '', + paramA: 1, + }, + buy: { + kind: 'buy', + enabled: true, + baseFrame: 2, + pressedFrame: 3, + strIdx: 3335, + labelZh: '買', + labelEn: 'Buy', + paramA: 2, + }, + sell: { + kind: 'sell', + enabled: true, + baseFrame: 4, + pressedFrame: 5, + strIdx: 3336, + labelZh: '賣', + labelEn: 'Sell', + paramA: 3, + }, + repair: { + kind: 'repair', + enabled: true, + baseFrame: 6, + pressedFrame: 7, + strIdx: 3338, + labelZh: '修復', + labelEn: 'Repair', + paramA: 4, + }, + repairAll: { + kind: 'repairAll', + enabled: true, + baseFrame: 18, + pressedFrame: 19, + strIdx: 10095, + labelZh: '修復所有裝備', + labelEn: 'Repair all equipment', + paramA: 1, + }, + close: { + kind: 'close', + enabled: true, + baseFrame: 10, + pressedFrame: 11, + strIdx: 4144, + labelZh: '關閉', + labelEn: 'Close', + paramA: 9, + }, +} + +/** D2Client.dll `0x6fb3f430` jump tables (`0x6fb3f5d4`, `0x6fb3f5e0`, `0x6fb3f618`) */ +export const BLACKSMITH_CLASS_IDS: ReadonlySet = new Set([154, 178, 253, 257, 511]) +export const TRADER_CLASS_IDS: ReadonlySet = new Set([147, 148, 177, 199, 202, 252, 254, 255, 405, 512, 513]) + +export function resolveVendorButtonLayout( + hcIdx: number, + mode: 'trade' | 'gamble', +): readonly [VendorButtonKind, VendorButtonKind, VendorButtonKind, VendorButtonKind] { + if (mode === 'gamble') return ['buy', 'sell', 'blank', 'close'] + if (BLACKSMITH_CLASS_IDS.has(hcIdx)) return ['buy', 'sell', 'repair', 'repairAll'] + if (TRADER_CLASS_IDS.has(hcIdx)) return ['buy', 'sell', 'blank', 'close'] + throw new Error(`D2Client 0x6fb3f430 assigns no trade buttons to MonStats class ${hcIdx}`) +} + +/** D2Client.dll tab table at `0x6fb91178` (stride 18) & `buyselltabs.dc6` (`0x6fb45e5e–0x6fb45f5d`) */ +export const VENDOR_TABS: readonly { + readonly id: VendorTabId + readonly index: 0 | 1 | 2 | 3 + readonly xOffset: number + readonly width: number + readonly height: number + readonly centerX: number + readonly textY: number + readonly strIdx: number + readonly labelZh: string + readonly labelEn: string +}[] = [ + { id: 'armor', index: 0, xOffset: 0, width: 79, height: 31, centerX: 42, textY: 20, strIdx: 4036, labelZh: '裝甲', labelEn: 'Armor' }, + { id: 'weapons1', index: 1, xOffset: 80, width: 79, height: 31, centerX: 121, textY: 20, strIdx: 4037, labelZh: '武器', labelEn: 'Weapons' }, + { id: 'weapons2', index: 2, xOffset: 160, width: 79, height: 31, centerX: 201, textY: 20, strIdx: 4037, labelZh: '武器', labelEn: 'Weapons' }, + { id: 'misc', index: 3, xOffset: 240, width: 79, height: 31, centerX: 281, textY: 20, strIdx: 4039, labelZh: '其他', labelEn: 'Misc' }, ] as const export type NpcMenuActionId = 'talk' | 'trade' | 'gamble' | 'identify' | 'cancel' @@ -226,7 +339,10 @@ export class WorldPanelsHud { activeVendorTab: VendorTabId = 'armor' vendorDifficulty: 0 | 1 | 2 = 0 vendorCharLevel = 85 - repairCursorMode = false + vendorTradeState: VendorTradeState = 'idle' + hoveredVendorButtonSlot: 0 | 1 | 2 | 3 | null = null + pressedMomentarySlot: 0 | 1 | 2 | 3 | null = null + activePlayerInventory: InventoryPanel | null = null vendorStatusMsg: { text: string; color: D2ColorCode; untilMs: number } | null = null hoveredVendorItem: { item: UiInventoryItem; x: number; y: number; col: number; row: number } | null = null vendorTabPlacements: Record = { @@ -237,6 +353,97 @@ export class WorldPanelsHud { } gamblePlacements: GridPlacement[] = [] + get repairCursorMode(): boolean { + return this.vendorTradeState === 'repair' + } + + set repairCursorMode(val: boolean) { + this.vendorTradeState = val ? 'repair' : 'idle' + } + + releaseMomentaryVendorButton(): void { + this.pressedMomentarySlot = null + } + + isVendorTabPresent(tabId: VendorTabId): boolean { + if (this.vendorMode === 'gamble') { + return tabId === 'armor' + } + return this.vendorTabPlacements[tabId].length > 0 + } + + /** + * D2Client.dll `0x6fb3f710` (`0x6fb3f7a1–0x6fb3f873`): + * If requested tab is empty, cycle forward `(idx + 1) % 4` up to 4 tries to find a non-empty tab. + */ + selectVendorTab(requestedTab: VendorTabId): void { + if (this.vendorMode === 'gamble') { + this.activeVendorTab = 'armor' + return + } + const startIdx = VENDOR_TABS.findIndex(t => t.id === requestedTab) + if (startIdx === -1) { + throw new Error(`Invalid vendor tab: ${requestedTab}`) + } + for (let step = 0; step < 4; step++) { + const idx = (startIdx + step) % 4 + const candidate = VENDOR_TABS[idx]! + if (this.vendorTabPlacements[candidate.id].length > 0) { + this.activeVendorTab = candidate.id + return + } + } + this.activeVendorTab = VENDOR_TABS[startIdx]!.id + } + + /** + * D2Client.dll `0x6fb4c27a` (mousedown) & `0x6fb3f260` (hover): + * Open intervals `relX ∈ (tableX, tableX + 45)`, `relY ∈ (371, 415)`, enabled slots only. + */ + hitTestVendorButtonSlot(logicalX: number, logicalY: number): 0 | 1 | 2 | 3 | null { + if (!this.activeVendorDescriptor) return null + const relX = logicalX - VENDOR_PANEL_ORIGIN.x + const relY = logicalY - VENDOR_PANEL_ORIGIN.y + if (relY <= VENDOR_BUTTON_HIT_Y_MIN || relY >= VENDOR_BUTTON_HIT_Y_MAX) return null + const layout = resolveVendorButtonLayout(this.activeVendorDescriptor.hcIdx, this.vendorMode) + for (let slot = 0 as 0 | 1 | 2 | 3; slot < 4; slot = (slot + 1) as 0 | 1 | 2 | 3) { + const kind = layout[slot]! + const spec = VENDOR_BUTTON_SPECS[kind] + if (!spec.enabled) continue + const tableX = VENDOR_BUTTON_TABLE_X[slot]! + if (relX > tableX && relX < tableX + VENDOR_BUTTON_HIT_W) { + return slot + } + } + return null + } + + getActiveVendorButtonLayout(): readonly [VendorButtonKind, VendorButtonKind, VendorButtonKind, VendorButtonKind] { + if (!this.activeVendorDescriptor) { + throw new Error('Cannot resolve vendor button layout without active vendor descriptor') + } + return resolveVendorButtonLayout(this.activeVendorDescriptor.hcIdx, this.vendorMode) + } + + getVendorButtonFrame( + slot: 0 | 1 | 2 | 3, + layout?: readonly [VendorButtonKind, VendorButtonKind, VendorButtonKind, VendorButtonKind], + ): number { + if (!layout && !this.activeVendorDescriptor) { + throw new Error('Cannot resolve vendor button frame without active vendor descriptor') + } + const resolvedLayout = + layout ?? resolveVendorButtonLayout(this.activeVendorDescriptor!.hcIdx, this.vendorMode) + const kind = resolvedLayout[slot]! + const spec = VENDOR_BUTTON_SPECS[kind] + if (!spec.enabled) return spec.baseFrame + const isPressed = + kind === 'buy' || kind === 'sell' || kind === 'repair' + ? this.vendorTradeState === kind + : this.pressedMomentarySlot === slot + return isPressed ? spec.pressedFrame : spec.baseFrame + } + showVendorStatus(text: string, color: D2ColorCode = 'gold', nowMs = performance.now()): void { this.vendorStatusMsg = { text, color, untilMs: nowMs + 2800 } } @@ -301,11 +508,16 @@ export class WorldPanelsHud { : getTownNpcDescriptorByName(descriptorOrName) if (!descriptor || descriptor.vendorId === null) return false + // Fail-fast validation against D2Client.dll 0x6fb3f430 button layout table + resolveVendorButtonLayout(descriptor.hcIdx, mode) + this.activeVendorDescriptor = descriptor this.vendorMode = mode this.vendorCharLevel = Math.max(1, cLvl) this.vendorDifficulty = difficulty - this.repairCursorMode = false + this.vendorTradeState = 'idle' + this.hoveredVendorButtonSlot = null + this.pressedMomentarySlot = null this.hoveredVendorItem = null const tables = getEmbeddedDropTables() @@ -334,9 +546,8 @@ export class WorldPanelsHud { }) } - // Select first non-empty tab (defaulting to armor, or misc if armor is empty) - const firstNonEmpty = tabIds.find(t => this.vendorTabPlacements[t].length > 0) ?? 'misc' - this.activeVendorTab = firstNonEmpty + // D2Client.dll 0x6fb411a0 -> 0x6fb3f710(0): start at tab 0 ('armor') and cycle to first non-empty tab + this.selectVendorTab('armor') return true } @@ -477,6 +688,9 @@ export class WorldPanelsHud { if (!hit.item.permStore) { placements.splice(hitIdx, 1) + if (this.vendorMode === 'trade' && placements.length === 0) { + this.selectVendorTab(this.activeVendorTab) + } break } } @@ -536,6 +750,9 @@ export class WorldPanelsHud { permStore: false, } tabPlacements.push({ item: buybackUi, col: freeSlot.col, row: freeSlot.row }) + if (this.vendorTabPlacements[this.activeVendorTab].length === 0) { + this.selectVendorTab(targetTab) + } } } @@ -576,30 +793,55 @@ export class WorldPanelsHud { return { ok: true, cost } } + private collectRepairablePlayerItems(playerInventory: InventoryPanel): { item: UiInventoryItem; cost: number }[] { + const allItems: UiInventoryItem[] = [ + ...(Object.values(playerInventory.equipped).filter(Boolean) as UiInventoryItem[]), + ...playerInventory.gridItems.map(p => p.item), + ] + const result: { item: UiInventoryItem; cost: number }[] = [] + for (const item of allItems) { + if (!item.durability || item.durability.current >= item.durability.max || item.ethereal) continue + const cost = this.getPlayerItemRepairPrice(item) + if (cost <= 0) continue + result.push({ item, cost }) + } + return result + } + + /** + * D2Client.dll `0x6fb3f260` -> D2Common `0x6fabc99a` (`#10095`): + * Computes total repair cost across all damaged repairable equipment and inventory items. + */ + getRepairAllCost(playerInventory?: InventoryPanel | null): number { + if (!this.activeVendorDescriptor?.canRepair) return 0 + const inv = playerInventory ?? this.activePlayerInventory + if (!inv) return 0 + let total = 0 + for (const entry of this.collectRepairablePlayerItems(inv)) { + total += entry.cost + } + return total + } + repairAllUiItems(playerInventory: InventoryPanel): { repairedCount: number; totalCost: number } { if (!this.activeVendorDescriptor?.canRepair) { return { repairedCount: 0, totalCost: 0 } } let repairedCount = 0 let totalCost = 0 - const allItems: UiInventoryItem[] = [ - ...(Object.values(playerInventory.equipped).filter(Boolean) as UiInventoryItem[]), - ...playerInventory.gridItems.map(p => p.item), - ] - for (const item of allItems) { - if (!item.durability || item.durability.current >= item.durability.max || item.ethereal) continue - const cost = this.getPlayerItemRepairPrice(item) - if (cost <= 0) continue + for (const { item, cost } of this.collectRepairablePlayerItems(playerInventory)) { + if (!item.durability) continue if (playerInventory.gold < cost) break + const maxDur = item.durability.max playerInventory.gold -= cost totalCost += cost repairedCount++ ;(item as { durability: { current: number; max: number } }).durability = { - current: item.durability.max, - max: item.durability.max, + current: maxDur, + max: maxDur, } if (item.rawItem) { - item.rawItem.durability = item.durability.max + item.rawItem.durability = maxDur } } if (repairedCount > 0) { @@ -878,6 +1120,9 @@ export class WorldPanelsHud { handleMouseMove(logicalX: number, logicalY: number): void { this.hoveredStashItem = null this.hoveredVendorItem = null + this.hoveredVendorButtonSlot = this.activeVendorDescriptor + ? this.hitTestVendorButtonSlot(logicalX, logicalY) + : null this.hoveredWaypointIdx = null const ox = 80 @@ -1028,13 +1273,16 @@ export class WorldPanelsHud { const ox = 80 const oy = 60 - // Close button (`x = ox + 272, y = oy + 388` or `ox + 18, oy + 385` for non-vendor panels) + // Generic Close button (`x = ox + 272, y = oy + 388` or `ox + 18, oy + 385`) for non-vendor panels only. + // Vendor panels manage their 4 button slots via D2Client.dll 0x6fb3f430 / 0x6fb4c27a (where slot 3 is Repair All on blacksmiths). if ( + kind !== 'vendor' && logicalY >= oy + 380 && logicalY <= oy + 422 && - (logicalX >= ox + 260 || (kind !== 'vendor' && logicalX <= ox + 58)) + (logicalX >= ox + 260 || logicalX <= ox + 58) ) { - this.repairCursorMode = false + this.vendorTradeState = 'idle' + this.pressedMomentarySlot = null callbacks.onClose() return true } @@ -1148,44 +1396,48 @@ export class WorldPanelsHud { return true } } else if (kind === 'vendor') { - // 1. Vendor 4 Tabs (`armor`, `weapons1`, `weapons2`, `misc`) at `oy + 34 .. oy + 62` - if (this.vendorMode === 'trade' && logicalY >= oy + 32 && logicalY <= oy + 62 && logicalX >= ox + 16 && logicalX <= ox + 306) { - const tabIdx = Math.min(3, Math.max(0, Math.floor((logicalX - (ox + 16)) / 72))) - const chosen = VENDOR_TABS[tabIdx] - if (chosen) { + if (!this.activeVendorDescriptor || this.activeVendorDescriptor.vendorId === null) { + throw new Error('Cannot handle vendor click without an active vendor descriptor') + } + const relX = logicalX - ox + const relY = logicalY - oy + + // 1. D2Client.dll `0x6fb4c0e5–0x6fb4c233`: Tab click (`relY ∈ [1, 31]`, 80px bands `<=80, <=160, <=240, <=320`) + if (this.vendorMode === 'trade' && relY >= 1 && relY <= 31 && relX >= 0 && relX <= 320) { + const tabIdx = relX <= 80 ? 0 : relX <= 160 ? 1 : relX <= 240 ? 2 : 3 + const chosen = VENDOR_TABS[tabIdx]! + if (this.isVendorTabPresent(chosen.id) && chosen.id !== this.activeVendorTab) { this.activeVendorTab = chosen.id this.hoveredVendorItem = null } return true } - // 2. Repair & Repair All buttons (if active vendor canRepair) - if (this.activeVendorDescriptor?.canRepair && this.vendorMode === 'trade') { - if ( - logicalX >= VENDOR_REPAIR_BTN_BOUNDS.x && - logicalX <= VENDOR_REPAIR_BTN_BOUNDS.x + VENDOR_REPAIR_BTN_BOUNDS.w && - logicalY >= VENDOR_REPAIR_BTN_BOUNDS.y && - logicalY <= VENDOR_REPAIR_BTN_BOUNDS.y + VENDOR_REPAIR_BTN_BOUNDS.h - ) { - this.repairCursorMode = !this.repairCursorMode - this.showVendorStatus( - this.repairCursorMode ? '修理模式:点击右侧装备进行修理' : '已退出修理模式', - this.repairCursorMode ? 'gold' : 'tan', - ) - return true - } - - if ( - logicalX >= VENDOR_REPAIR_ALL_BTN_BOUNDS.x && - logicalX <= VENDOR_REPAIR_ALL_BTN_BOUNDS.x + VENDOR_REPAIR_ALL_BTN_BOUNDS.w && - logicalY >= VENDOR_REPAIR_ALL_BTN_BOUNDS.y && - logicalY <= VENDOR_REPAIR_ALL_BTN_BOUNDS.y + VENDOR_REPAIR_ALL_BTN_BOUNDS.h - ) { - if (callbacks.inventory) { - this.repairAllUiItems(callbacks.inventory) + // 2. D2Client.dll `0x6fb4c27a–0x6fb4c3ee`: Button mousedown hit-test & mutual-exclusion state machine + const hitSlot = this.hitTestVendorButtonSlot(logicalX, logicalY) + if (hitSlot !== null) { + const layout = resolveVendorButtonLayout(this.activeVendorDescriptor.hcIdx, this.vendorMode) + const btnKind = layout[hitSlot]! + if (btnKind === 'buy' || btnKind === 'sell' || btnKind === 'repair') { + this.pressedMomentarySlot = null + this.vendorTradeState = this.vendorTradeState === btnKind ? 'idle' : btnKind + } else if (btnKind === 'repairAll') { + this.vendorTradeState = 'idle' + this.pressedMomentarySlot = hitSlot + const inv = callbacks.inventory ?? this.activePlayerInventory + if (inv) { + this.repairAllUiItems(inv) } - return true + } else if (btnKind === 'close') { + this.vendorTradeState = 'idle' + this.pressedMomentarySlot = hitSlot + callbacks.onClose() } + return true + } + if (relY > VENDOR_BUTTON_HIT_Y_MIN && relY < VENDOR_BUTTON_HIT_Y_MAX && relX > VENDOR_BUTTON_TABLE_X[0] && relX < VENDOR_BUTTON_TABLE_X[3] + VENDOR_BUTTON_HIT_W) { + // Clicked disabled blank slot (`0x6fb3d300` no-op) + return true } // 3. Vendor 10x10 Grid (`VENDOR_GRID_ORIGIN`) @@ -1198,20 +1450,23 @@ export class WorldPanelsHud { const col = Math.floor((logicalX - VENDOR_GRID_ORIGIN.x) / VENDOR_GRID_ORIGIN.cellPx) const row = Math.floor((logicalY - VENDOR_GRID_ORIGIN.y) / VENDOR_GRID_ORIGIN.cellPx) - if (callbacks.inventory) { + const inv = callbacks.inventory ?? this.activePlayerInventory + if (inv) { // If player is holding an item on cursor and clicks inside Vendor grid -> Sell it! - if (callbacks.inventory.cursorItem) { - const held = callbacks.inventory.cursorItem - const res = this.sellToActiveVendor(held, callbacks.inventory) + if (inv.cursorItem) { + const held = inv.cursorItem + const res = this.sellToActiveVendor(held, inv) if (res.ok) { - callbacks.inventory.cursorItem = null + inv.cursorItem = null } return true } - // Otherwise buy the clicked item from the vendor! - this.buyFromActiveVendor(col, row, callbacks.inventory, Boolean(callbacks.isShiftClick)) - this.handleMouseMove(logicalX, logicalY) + // Left-click buy works in 'idle' or 'buy' state; do not accidentally buy when in 'sell' or 'repair' state + if (this.vendorTradeState === 'idle' || this.vendorTradeState === 'buy') { + this.buyFromActiveVendor(col, row, inv, Boolean(callbacks.isShiftClick)) + this.handleMouseMove(logicalX, logicalY) + } } return true } @@ -1229,12 +1484,14 @@ export class WorldPanelsHud { stashBgImg: HTMLImageElement | null cubeBgImg: HTMLImageElement | null vendorBgImg: HTMLImageElement | null + vendorTabsImg?: HTMLImageElement | null buySellBtnImg: HTMLImageElement | null questsAtlasImg?: HTMLImageElement | null itemsAtlasImg?: HTMLImageElement | null waypointTabsImg?: HTMLImageElement | null waypointIconsImg?: HTMLImageElement | null questTabsImg?: HTMLImageElement | null + inventory?: InventoryPanel | null }, font: D2FontRenderer, ): void { @@ -1470,68 +1727,17 @@ export class WorldPanelsHud { align: 'center', }) } else if (kind === 'vendor') { - if (!this.activeVendorDescriptor) { - this.openVendorForNpc('Akara', 'trade', this.vendorCharLevel, this.vendorDifficulty) + if (!this.activeVendorDescriptor || this.activeVendorDescriptor.vendorId === null) { + throw new Error('Cannot draw vendor panel without an active vendor descriptor') } - if (assets.vendorBgImg) { - ctx.drawImage(assets.vendorBgImg, ox, oy) - } else { - ctx.fillStyle = 'rgba(12, 10, 8, 0.96)' - ctx.fillRect(ox, oy, 320, 432) - ctx.strokeStyle = '#6c5838' - ctx.strokeRect(ox + 0.5, oy + 0.5, 319, 431) + if (!assets.vendorBgImg || !assets.vendorTabsImg || !assets.buySellBtnImg) { + throw new Error('Missing required baked UI assets for vendor panel (vendorBgImg, vendorTabsImg, buySellBtnImg)') } - const npcTitle = this.activeVendorDescriptor?.displayName ?? 'Akara' - const modeTitle = this.vendorMode === 'gamble' ? '赌博 (Gamble)' : '商店 (Trade)' - font.drawText(ctx, `${npcTitle} · ${modeTitle}`, ox + 160, oy + 22, { - font: 'fontexocet10', - color: 'gold', - align: 'center', - }) + // 1. D2Client.dll `0x6fb45c8a–0x6fb45d63`: `buysell.dc6` 320x432 stitched background + ctx.drawImage(assets.vendorBgImg, ox, oy) - // 4 Vendor Tabs (`护甲`, `武器 I`, `武器 II`, `杂物`) or Gamble Banner - if (this.vendorMode === 'trade') { - for (let i = 0; i < VENDOR_TABS.length; i++) { - const tab = VENDOR_TABS[i]! - const active = this.activeVendorTab === tab.id - const tx = ox + 17 + i * 72 - const ty = oy + 34 - ctx.fillStyle = active ? 'rgba(56, 44, 28, 0.95)' : 'rgba(20, 16, 12, 0.88)' - ctx.fillRect(tx, ty, 70, 26) - ctx.strokeStyle = active ? '#e8c26b' : '#584a34' - ctx.strokeRect(tx + 0.5, ty + 0.5, 69, 25) - font.drawText(ctx, tab.labelZh, tx + 35, ty + 17, { - font: 'font8', - color: active ? 'gold' : 'tan', - align: 'center', - }) - } - } else { - ctx.fillStyle = 'rgba(42, 32, 18, 0.92)' - ctx.fillRect(ox + 17, oy + 34, 289, 26) - ctx.strokeStyle = '#e8c26b' - ctx.strokeRect(ox + 17.5, oy + 34.5, 288, 25) - font.drawText(ctx, '未鉴定神秘赌博物品 · 点击直接购买鉴定', ox + 160, oy + 51, { - font: 'font8', - color: 'gold', - align: 'center', - }) - } - - // 10x10 Vendor Grid cell borders (`VENDOR_GRID_ORIGIN`) - for (let r = 0; r < VENDOR_GRID_ORIGIN.rows; r++) { - for (let c = 0; c < VENDOR_GRID_ORIGIN.cols; c++) { - const cx = VENDOR_GRID_ORIGIN.x + c * VENDOR_GRID_ORIGIN.cellPx - const cy = VENDOR_GRID_ORIGIN.y + r * VENDOR_GRID_ORIGIN.cellPx - ctx.fillStyle = 'rgba(10, 8, 6, 0.65)' - ctx.fillRect(cx, cy, VENDOR_GRID_ORIGIN.cellPx, VENDOR_GRID_ORIGIN.cellPx) - ctx.strokeStyle = 'rgba(68, 56, 40, 0.45)' - ctx.strokeRect(cx + 0.5, cy + 0.5, VENDOR_GRID_ORIGIN.cellPx - 1, VENDOR_GRID_ORIGIN.cellPx - 1) - } - } - - // Draw items in the active vendor 10x10 tab + // 2. D2Client.dll `0x6fb458a0`: Items on the active store page (`Inventory.txt` Monster2 grid at `ox+16, oy+63`) const atlas = assets.itemsAtlasImg ?? null const placements = this.getActiveVendorPlacements() for (const placed of placements) { @@ -1562,74 +1768,66 @@ export class WorldPanelsHud { } } - // Repair & Repair All buttons for blacksmiths - if (this.activeVendorDescriptor?.canRepair && this.vendorMode === 'trade') { - ctx.fillStyle = this.repairCursorMode ? 'rgba(64, 48, 20, 0.95)' : 'rgba(20, 16, 12, 0.9)' - ctx.fillRect( - VENDOR_REPAIR_BTN_BOUNDS.x, - VENDOR_REPAIR_BTN_BOUNDS.y, - VENDOR_REPAIR_BTN_BOUNDS.w, - VENDOR_REPAIR_BTN_BOUNDS.h, - ) - ctx.strokeStyle = this.repairCursorMode ? '#e8c26b' : '#8a7248' - ctx.strokeRect( - VENDOR_REPAIR_BTN_BOUNDS.x + 0.5, - VENDOR_REPAIR_BTN_BOUNDS.y + 0.5, - VENDOR_REPAIR_BTN_BOUNDS.w - 1, - VENDOR_REPAIR_BTN_BOUNDS.h - 1, - ) - font.drawText( - ctx, - '修理 (Repair)', - VENDOR_REPAIR_BTN_BOUNDS.x + VENDOR_REPAIR_BTN_BOUNDS.w / 2, - VENDOR_REPAIR_BTN_BOUNDS.y + 16, - { font: 'font8', color: this.repairCursorMode ? 'white' : 'gold', align: 'center' }, - ) - - ctx.fillStyle = 'rgba(20, 16, 12, 0.9)' - ctx.fillRect( - VENDOR_REPAIR_ALL_BTN_BOUNDS.x, - VENDOR_REPAIR_ALL_BTN_BOUNDS.y, - VENDOR_REPAIR_ALL_BTN_BOUNDS.w, - VENDOR_REPAIR_ALL_BTN_BOUNDS.h, - ) - ctx.strokeStyle = '#8a7248' - ctx.strokeRect( - VENDOR_REPAIR_ALL_BTN_BOUNDS.x + 0.5, - VENDOR_REPAIR_ALL_BTN_BOUNDS.y + 0.5, - VENDOR_REPAIR_ALL_BTN_BOUNDS.w - 1, - VENDOR_REPAIR_ALL_BTN_BOUNDS.h - 1, - ) - font.drawText( - ctx, - '全部修理 (All)', - VENDOR_REPAIR_ALL_BTN_BOUNDS.x + VENDOR_REPAIR_ALL_BTN_BOUNDS.w / 2, - VENDOR_REPAIR_ALL_BTN_BOUNDS.y + 16, - { font: 'font8', color: 'gold', align: 'center' }, - ) - } else { - font.drawText(ctx, '左键/右键点击购买 · 右键背包物品出售', ox + 130, oy + 403, { - font: 'font8', - color: 'tan', - align: 'center', - }) + // 3. D2Client.dll `0x6fb45df1–0x6fb45e51`: 4 button slots (`buysellbtn.dc6` at `ox + tableX - 1`, `oy + 385`) + const btnLayout = resolveVendorButtonLayout(this.activeVendorDescriptor.hcIdx, this.vendorMode) + for (let slot = 0 as 0 | 1 | 2 | 3; slot < 4; slot = (slot + 1) as 0 | 1 | 2 | 3) { + const frameIdx = this.getVendorButtonFrame(slot, btnLayout) + const drawX = ox + VENDOR_BUTTON_TABLE_X[slot]! - 1 + const drawY = oy + VENDOR_BUTTON_DRAW_Y + ctx.drawImage(assets.buySellBtnImg, frameIdx * 32, 0, 32, 32, drawX, drawY, 32, 32) } - // Status toast message at bottom of vendor panel + // 4. D2Client.dll `0x6fb45e5e–0x6fb45f5d`: `buyselltabs.dc6` present tabs + Font16 CHI `.tbl` labels (skipped in gamble mode) + for (const tab of VENDOR_TABS) { + if (!this.isVendorTabPresent(tab.id)) continue + const selected = this.activeVendorTab === tab.id + const frameIdx = selected ? tab.index : tab.index + 4 + ctx.drawImage(assets.vendorTabsImg, frameIdx * 79, 0, 79, 31, ox + tab.xOffset, oy, 79, 31) + if (this.vendorMode === 'trade') { + font.drawText(ctx, tab.labelZh, ox + tab.centerX, oy + tab.textY, { + font: 'font16', + color: selected ? 'gold' : 'white', + align: 'center', + }) + } + } + + // 5. Status toast text inside `buysell.dc6` bottom-left long box (`relX 17..200, relY 359..375`) if (this.vendorStatusMsg && performance.now() < this.vendorStatusMsg.untilMs) { - ctx.fillStyle = 'rgba(8, 6, 4, 0.92)' - ctx.fillRect(ox + 17, oy + 356, 289, 24) - ctx.strokeStyle = '#8a7248' - ctx.strokeRect(ox + 17.5, oy + 356.5, 288, 23) - font.drawText(ctx, this.vendorStatusMsg.text, ox + 160, oy + 372, { + font.drawText(ctx, this.vendorStatusMsg.text, ox + 109, oy + 371, { font: 'font8', color: this.vendorStatusMsg.color, align: 'center', }) } + + // 6. D2Client.dll `0x6fb3f260` -> D2Win #10085 (`0x6f8f33a0`): Left-anchored framed button hover text + if (this.hoveredVendorButtonSlot !== null) { + const slot = this.hoveredVendorButtonSlot + const btnKind = btnLayout[slot]! + const spec = VENDOR_BUTTON_SPECS[btnKind] + if (spec.enabled) { + const hoverText = + btnKind === 'repairAll' + ? `${spec.labelZh}:${this.getRepairAllCost(assets.inventory)}` + : spec.labelZh + const padX = 6 + const boxW = Math.max(24, font.measureText(hoverText, 'font16') + padX * 2) + const boxH = 22 + const boxX = ox + VENDOR_BUTTON_TABLE_X[slot]! + const boxY = oy + 380 - boxH + ctx.fillStyle = 'rgba(0, 0, 0, 0.85)' + ctx.fillRect(boxX, boxY, boxW, boxH) + font.drawText(ctx, hoverText, boxX + padX, boxY + 17, { + font: 'font16', + color: 'white', + align: 'left', + }) + } + } } - if (assets.buySellBtnImg) { + if (kind !== 'vendor' && assets.buySellBtnImg) { ctx.drawImage(assets.buySellBtnImg, 10 * 32, 0, 32, 32, ox + 272, oy + 388, 32, 32) } } diff --git a/tests/vendor-panel-ui.test.ts b/tests/vendor-panel-ui.test.ts new file mode 100644 index 0000000..9be4363 --- /dev/null +++ b/tests/vendor-panel-ui.test.ts @@ -0,0 +1,474 @@ +import { existsSync, readFileSync } from 'node:fs' +import { resolve } from 'node:path' +import { describe, expect, it, vi } from 'vitest' +import { encodeIndexedPng } from '../scripts/png.ts' +import { decodeDc6 } from '../src/formats/dc6.ts' +import { decodePl2 } from '../src/formats/pl2.ts' +import type { SpriteFrame } from '../src/formats/sprite.ts' +import { TOWN_NPC_DESCRIPTORS } from '../src/game/npc-table.ts' +import { MpqArchive } from '../src/mpq/archive.ts' +import { fileSource } from '../src/mpq/file-source.ts' +import { MountedArchives } from '../src/mpq/mount.ts' +import { BAKED_UI_MANIFEST } from '../src/ui/baked-ui-meta.ts' +import type { D2ColorCode, D2FontRenderer, FontSizeName } from '../src/ui/font.ts' +import { InventoryPanel, type UiInventoryItem } from '../src/ui/inventory.ts' +import { + BLACKSMITH_CLASS_IDS, + TRADER_CLASS_IDS, + VENDOR_BUTTON_DRAW_Y, + VENDOR_BUTTON_HIT_W, + VENDOR_BUTTON_HIT_Y_MAX, + VENDOR_BUTTON_HIT_Y_MIN, + VENDOR_BUTTON_SPECS, + VENDOR_BUTTON_TABLE_X, + VENDOR_GRID_ORIGIN, + VENDOR_TABS, + WorldPanelsHud, + resolveVendorButtonLayout, +} from '../src/ui/world-panels.ts' + +function readPngHeaderSize(buf: Uint8Array): { width: number; height: number } { + const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength) + return { + width: view.getUint32(16, false), + height: view.getUint32(20, false), + } +} + +function stitchIndexed( + width: number, + height: number, + placements: ReadonlyArray<{ frame: SpriteFrame; x: number; y: number }>, +): Uint8Array { + const out = new Uint8Array(width * height) + for (const { frame, x: dstX, y: dstY } of placements) { + for (let sy = 0; sy < frame.height; sy++) { + const ty = dstY + sy + if (ty < 0 || ty >= height) continue + for (let sx = 0; sx < frame.width; sx++) { + const tx = dstX + sx + if (tx < 0 || tx >= width) continue + const srcIdx = sy * frame.width + sx + if (frame.mask[srcIdx] === 0) continue + const color = frame.indices[srcIdx] ?? 0 + if (color !== 0) { + out[ty * width + tx] = color + } + } + } + } + return out +} + +function createFakeImage(width: number, height: number, tag: string): HTMLImageElement { + return { width, height, tag } as unknown as HTMLImageElement +} + +function createFakeVendorAssets() { + return { + borderLeftImg: createFakeImage(400, 553, 'borderLeft'), + questBgImg: null, + waypointBgImg: null, + stashBgImg: null, + cubeBgImg: null, + vendorBgImg: createFakeImage(320, 432, 'vendorBg'), + vendorTabsImg: createFakeImage(632, 31, 'vendorTabs'), + buySellBtnImg: createFakeImage(736, 32, 'buySellBtn'), + itemsAtlasImg: createFakeImage(2048, 2048, 'itemsAtlas'), + } +} + +interface DrawImageCall { + tag: string + args: number[] +} + +interface FontCall { + type: 'drawText' | 'drawRightText' | 'drawFramedTooltip' + text: string + x: number + y: number + size: FontSizeName + color: D2ColorCode | undefined +} + +function createMockCanvasAndFont() { + const drawImageCalls: DrawImageCall[] = [] + const fillRectCalls: number[][] = [] + const strokeRectCalls: number[][] = [] + const fontCalls: FontCall[] = [] + + const ctx = { + save: vi.fn(), + restore: vi.fn(), + drawImage: vi.fn((img: { tag?: string }, ...nums: number[]) => { + drawImageCalls.push({ tag: img?.tag ?? 'unknown', args: nums }) + }), + fillRect: vi.fn((...nums: number[]) => { + fillRectCalls.push(nums) + }), + strokeRect: vi.fn((...nums: number[]) => { + strokeRectCalls.push(nums) + }), + beginPath: vi.fn(), + moveTo: vi.fn(), + lineTo: vi.fn(), + stroke: vi.fn(), + fillText: vi.fn(), + fillStyle: '', + strokeStyle: '', + lineWidth: 1, + } as unknown as CanvasRenderingContext2D + + const font = { + loaded: true, + measureText: vi.fn((text: string) => text.length * 10), + drawText: vi.fn( + ( + _ctx: CanvasRenderingContext2D, + text: string, + x: number, + y: number, + opts?: { font?: FontSizeName; color?: D2ColorCode; align?: 'left' | 'center' | 'right' }, + ) => { + fontCalls.push({ + type: 'drawText', + text, + x, + y, + size: opts?.font ?? 'font16', + color: opts?.color ?? 'white', + }) + }, + ), + drawRightText: vi.fn( + ( + _ctx: CanvasRenderingContext2D, + text: string, + rightX: number, + y: number, + size: FontSizeName = 'font16', + color: D2ColorCode = 'white', + ) => { + fontCalls.push({ type: 'drawRightText', text, x: rightX, y, size, color }) + }, + ), + drawFramedTooltip: vi.fn( + ( + _ctx: CanvasRenderingContext2D, + lines: Array<{ text: string; color?: D2ColorCode; size?: FontSizeName }>, + centerX: number, + bottomY: number, + ) => { + fontCalls.push({ + type: 'drawFramedTooltip', + text: lines.map(l => l.text).join('\n'), + x: centerX, + y: bottomY, + size: lines[0]?.size ?? 'font16', + color: lines[0]?.color, + }) + }, + ), + } as unknown as D2FontRenderer + + return { ctx, font, drawImageCalls, fillRectCalls, strokeRectCalls, fontCalls } +} + +describe('Diablo II 1.13c Vendor / BuySell Panel UI Parity', () => { + it('verifies Monster2 10x10 grid origin and baked UI manifest entries', () => { + // Inventory.txt Monster2: gridLeft = 96 (80 + 16), gridTop = 123 (60 + 63), 10x10, 29px + expect(VENDOR_GRID_ORIGIN).toEqual({ + x: 96, + y: 123, + cols: 10, + rows: 10, + cellPx: 29, + }) + expect(BAKED_UI_MANIFEST.images.vendorBg).toBe('/ui/vendor-bg.png') + expect(BAKED_UI_MANIFEST.images.vendorTabs).toBe('/ui/vendor-tabs.png') + expect(BAKED_UI_MANIFEST.images.buySellBtn).toBe('/ui/buysellbtn.png') + }) + + it('verifies baked vendor-bg.png, vendor-tabs.png, and buysellbtn.png dimensions and MPQ byte parity', async () => { + const publicUiDir = resolve(process.cwd(), 'public/ui') + const vendorBgBytes = new Uint8Array(readFileSync(resolve(publicUiDir, 'vendor-bg.png'))) + const vendorTabsBytes = new Uint8Array(readFileSync(resolve(publicUiDir, 'vendor-tabs.png'))) + const buySellBtnBytes = new Uint8Array(readFileSync(resolve(publicUiDir, 'buysellbtn.png'))) + + expect(readPngHeaderSize(vendorBgBytes)).toEqual({ width: 320, height: 432 }) + expect(readPngHeaderSize(vendorTabsBytes)).toEqual({ width: 632, height: 31 }) + expect(readPngHeaderSize(buySellBtnBytes)).toEqual({ width: 736, height: 32 }) + + // Verify byte-exact PNG parity against freshly decoded MPQ buysell.dc6 and buyselltabs.dc6 + const d2dataPath = resolve(process.cwd(), 'samples/d2/d2data.mpq') + if (existsSync(d2dataPath)) { + const archives = new MountedArchives() + archives.add('d2data.mpq', await MpqArchive.open(await fileSource(d2dataPath))) + const pl2 = decodePl2(await archives.read('data/global/palette/ACT1/pal.pl2')) + + const buysellSheet = decodeDc6(await archives.read('data/global/ui/Panel/buysell.dc6')) + const bf = buysellSheet.groups[0]!.frames + expect(bf).toHaveLength(4) + const expectedBgPng = encodeIndexedPng({ + width: 320, + height: 432, + pixels: stitchIndexed(320, 432, [ + { frame: bf[0]!, x: 0, y: 0 }, + { frame: bf[1]!, x: 256, y: 0 }, + { frame: bf[2]!, x: 0, y: 256 }, + { frame: bf[3]!, x: 256, y: 256 }, + ]), + palette: pl2.rgb, + }) + expect(Buffer.compare(Buffer.from(vendorBgBytes), Buffer.from(expectedBgPng))).toBe(0) + + const tabsSheet = decodeDc6(await archives.read('data/global/ui/Panel/buyselltabs.dc6')) + const tf = tabsSheet.groups[0]!.frames + expect(tf).toHaveLength(8) + const expectedTabsPng = encodeIndexedPng({ + width: 632, + height: 31, + pixels: stitchIndexed( + 632, + 31, + tf.slice(0, 8).map((frame, i) => ({ frame, x: i * 79, y: 0 })), + ), + palette: pl2.rgb, + transparentIndex: 0, + }) + expect(Buffer.compare(Buffer.from(vendorTabsBytes), Buffer.from(expectedTabsPng))).toBe(0) + } + }) + + it('verifies D2Client.dll 0x6fb3f430 4-button layout across all 16 trade NPCs, 6 gamble NPCs, and non-vendor NPCs', () => { + const tradeNpcs = TOWN_NPC_DESCRIPTORS.filter(d => d.canTrade) + const repairNpcs = TOWN_NPC_DESCRIPTORS.filter(d => d.canRepair) + const gambleNpcs = TOWN_NPC_DESCRIPTORS.filter(d => d.canGamble) + const nonVendorNpcs = TOWN_NPC_DESCRIPTORS.filter(d => !d.canTrade && !d.canGamble) + + expect(tradeNpcs).toHaveLength(16) + expect(repairNpcs).toHaveLength(5) + expect(gambleNpcs).toHaveLength(6) + + for (const desc of tradeNpcs) { + const layout = resolveVendorButtonLayout(desc.hcIdx, 'trade') + if (desc.canRepair) { + expect(BLACKSMITH_CLASS_IDS.has(desc.hcIdx)).toBe(true) + expect(layout).toEqual(['buy', 'sell', 'repair', 'repairAll']) + } else { + expect(TRADER_CLASS_IDS.has(desc.hcIdx)).toBe(true) + expect(layout).toEqual(['buy', 'sell', 'blank', 'close']) + } + } + + // Gamble mode overrides blacksmith/trader check first per 0x6fb3f430 + for (const desc of gambleNpcs) { + expect(resolveVendorButtonLayout(desc.hcIdx, 'gamble')).toEqual(['buy', 'sell', 'blank', 'close']) + } + + for (const desc of nonVendorNpcs) { + expect(() => resolveVendorButtonLayout(desc.hcIdx, 'trade')).toThrow( + /D2Client 0x6fb3f430 assigns no trade buttons to MonStats class/, + ) + } + }) + + it('draws Charsi blacksmith panel with [2, 4, 6, 18] button frames, present tabs, CHI labels, and no extra close button', () => { + const wp = new WorldPanelsHud() + const inv = new InventoryPanel() + wp.activePlayerInventory = inv + expect(wp.openVendorForNpc('Charsi', 'trade', 24, 0)).toBe(true) + + const { ctx, font, drawImageCalls, strokeRectCalls, fontCalls } = createMockCanvasAndFont() + const assets = { ...createFakeVendorAssets(), inventory: inv } + + wp.drawLeftDockPanel(ctx, 'vendor', assets, font) + + // 1. vendorBg drawn at (80, 60) + const bgCalls = drawImageCalls.filter(c => c.tag === 'vendorBg') + expect(bgCalls).toEqual([{ tag: 'vendorBg', args: [80, 60] }]) + + // 2. Exactly 4 buySellBtn calls (no 5th generic Close button at the end of drawLeftDockPanel!) + const btnCalls = drawImageCalls.filter(c => c.tag === 'buySellBtn') + expect(btnCalls).toHaveLength(4) + expect(btnCalls.map(c => c.args)).toEqual([ + [2 * 32, 0, 32, 32, 80 + 115, 60 + VENDOR_BUTTON_DRAW_Y, 32, 32], + [4 * 32, 0, 32, 32, 80 + 168, 60 + VENDOR_BUTTON_DRAW_Y, 32, 32], + [6 * 32, 0, 32, 32, 80 + 220, 60 + VENDOR_BUTTON_DRAW_Y, 32, 32], + [18 * 32, 0, 32, 32, 80 + 272, 60 + VENDOR_BUTTON_DRAW_Y, 32, 32], + ]) + + // 3. Present tabs drawn from vendorTabsImg with frames 0 (selected) and 5..7 (unselected) + const tabCalls = drawImageCalls.filter(c => c.tag === 'vendorTabs') + expect(tabCalls.length).toBeGreaterThanOrEqual(3) + expect(tabCalls[0]!.args).toEqual([0 * 79, 0, 79, 31, 80 + 0, 60, 79, 31]) + + // 4. Tab labels use CHI .tbl strings ('裝甲', '武器', '其他') in font16 and NO fake strokeRects or vendor title banner + expect(strokeRectCalls).toHaveLength(0) + const tabLabels = fontCalls.filter(c => c.type === 'drawText').map(c => c.text) + expect(tabLabels).toContain('裝甲') + expect(tabLabels).toContain('武器') + expect(tabLabels).toContain('其他') + expect(tabLabels.some(t => t.includes('恰西') || t.includes('Charsi'))).toBe(false) + }) + + it('draws Akara trader panel with [2, 4, 0, 10] button frames and omits empty tabs', () => { + const wp = new WorldPanelsHud() + const inv = new InventoryPanel() + wp.activePlayerInventory = inv + expect(wp.openVendorForNpc('Akara', 'trade', 12, 0)).toBe(true) + + // Force armor tab empty to verify 0x6fb45e5e-0x6fb45f5d tab presence guard + wp.vendorTabPlacements.armor = [] + wp.selectVendorTab('armor') + expect(wp.activeVendorTab).not.toBe('armor') + + const { ctx, font, drawImageCalls, fontCalls } = createMockCanvasAndFont() + wp.drawLeftDockPanel(ctx, 'vendor', { ...createFakeVendorAssets(), inventory: inv }, font) + + const btnCalls = drawImageCalls.filter(c => c.tag === 'buySellBtn') + expect(btnCalls).toHaveLength(4) + expect(btnCalls.map(c => c.args[0]! / 32)).toEqual([2, 4, 0, 10]) + + // Empty 'armor' tab must not draw its tab sprite or '裝甲' label + const tabLabels = fontCalls.filter(c => c.type === 'drawText').map(c => c.text) + expect(tabLabels).not.toContain('裝甲') + }) + + it('draws Gamble panel with only tab 0 sprite and no tab text labels', () => { + const wp = new WorldPanelsHud() + const inv = new InventoryPanel() + wp.activePlayerInventory = inv + expect(wp.openVendorForNpc('Gheed', 'gamble', 30, 0)).toBe(true) + + const { ctx, font, drawImageCalls, fontCalls } = createMockCanvasAndFont() + wp.drawLeftDockPanel(ctx, 'vendor', { ...createFakeVendorAssets(), inventory: inv }, font) + + const btnCalls = drawImageCalls.filter(c => c.tag === 'buySellBtn') + expect(btnCalls.map(c => c.args[0]! / 32)).toEqual([2, 4, 0, 10]) + + const tabCalls = drawImageCalls.filter(c => c.tag === 'vendorTabs') + expect(tabCalls).toEqual([{ tag: 'vendorTabs', args: [0, 0, 79, 31, 80, 60, 79, 31] }]) + expect(fontCalls.filter(c => c.type === 'drawText')).toHaveLength(0) + }) + + it('enforces open-interval button hit-testing, latching Buy/Sell/Repair states, Repair All cost tooltip, and Close', () => { + const wp = new WorldPanelsHud() + const inv = new InventoryPanel() + inv.gold = 50000 + wp.activePlayerInventory = inv + expect(wp.openVendorForNpc('Charsi', 'trade', 24, 0)).toBe(true) + + // Damage an equipped item so Repair All has a non-zero cost + const helm: UiInventoryItem = { + id: 'damaged-cap', + code: 'cap', + nameEn: 'Cap', + nameZh: '帽子', + quality: 'normal', + invFile: 'invcap', + invWidth: 2, + invHeight: 2, + equipSlots: ['head'], + identified: true, + durability: { current: 4, max: 12 }, + } + inv.equipped.head = helm + const expectedCost = wp.getRepairAllCost(inv) + expect(expectedCost).toBeGreaterThan(0) + + // Open-interval boundary check: relX = tableX (116) or tableX + 45 (161) must NOT hit + expect(wp.hitTestVendorButtonSlot(80 + 116, 60 + 390)).toBeNull() + expect(wp.hitTestVendorButtonSlot(80 + 116 + VENDOR_BUTTON_HIT_W, 60 + 390)).toBeNull() + expect(wp.hitTestVendorButtonSlot(80 + 130, 60 + VENDOR_BUTTON_HIT_Y_MIN)).toBeNull() + expect(wp.hitTestVendorButtonSlot(80 + 130, 60 + VENDOR_BUTTON_HIT_Y_MAX)).toBeNull() + expect(wp.hitTestVendorButtonSlot(80 + 130, 60 + 390)).toBe(0) + + let closed = false + const callbacks = { + onClose: () => { + closed = true + }, + onWaypointTeleport: () => {}, + inventory: inv, + } + + // Click slot 0 (Buy) -> latches 'buy' (frame 3) + wp.handleLeftDockClick('vendor', 80 + VENDOR_BUTTON_TABLE_X[0] + 20, 60 + 390, callbacks) + expect(wp.vendorTradeState).toBe('buy') + expect(wp.getVendorButtonFrame(0)).toBe(3) + + // Click slot 1 (Sell) -> switches to 'sell' (frame 5) + wp.handleLeftDockClick('vendor', 80 + VENDOR_BUTTON_TABLE_X[1] + 20, 60 + 390, callbacks) + expect(wp.vendorTradeState).toBe('sell') + expect(wp.getVendorButtonFrame(1)).toBe(5) + + // Click slot 2 (Repair) -> switches to 'repair' (frame 7) + wp.handleLeftDockClick('vendor', 80 + VENDOR_BUTTON_TABLE_X[2] + 20, 60 + 390, callbacks) + expect(wp.vendorTradeState).toBe('repair') + expect(wp.getVendorButtonFrame(2)).toBe(7) + expect(wp.repairCursorMode).toBe(true) + + // Hover slot 3 (Repair All) -> verifies left-anchored framed hover text "修復所有裝備:${cost}" + wp.handleMouseMove(80 + VENDOR_BUTTON_TABLE_X[3] + 20, 60 + 390) + expect(wp.hoveredVendorButtonSlot).toBe(3) + const { ctx, font, fontCalls } = createMockCanvasAndFont() + wp.drawLeftDockPanel(ctx, 'vendor', { ...createFakeVendorAssets(), inventory: inv }, font) + expect( + fontCalls.some( + c => + c.type === 'drawText' && + c.text === `修復所有裝備:${expectedCost}` && + c.x === 80 + VENDOR_BUTTON_TABLE_X[3] + 6, + ), + ).toBe(true) + + // Click slot 3 (Repair All on Charsi) -> repairs item, clears state to 'idle', and does NOT close panel! + wp.handleLeftDockClick('vendor', 80 + VENDOR_BUTTON_TABLE_X[3] + 20, 60 + 390, callbacks) + expect(closed).toBe(false) + expect(wp.vendorTradeState).toBe('idle') + expect(wp.pressedMomentarySlot).toBe(3) + expect(helm.durability?.current).toBe(12) + wp.releaseMomentaryVendorButton() + expect(wp.pressedMomentarySlot).toBeNull() + + // Switch to Akara and click slot 3 (Close) -> closes panel, slot 2 (blank) is ignored + wp.openVendorForNpc('Akara', 'trade', 24, 0) + expect(wp.hitTestVendorButtonSlot(80 + VENDOR_BUTTON_TABLE_X[2] + 20, 60 + 390)).toBeNull() + wp.handleLeftDockClick('vendor', 80 + VENDOR_BUTTON_TABLE_X[3] + 20, 60 + 390, callbacks) + expect(closed).toBe(true) + }) + + it('auto-cycles to the next non-empty tab when buying the last item on the active tab', () => { + const wp = new WorldPanelsHud() + const inv = new InventoryPanel() + inv.gold = 999999 + wp.activePlayerInventory = inv + expect(wp.openVendorForNpc('Charsi', 'trade', 24, 0)).toBe(true) + expect(wp.activeVendorTab).toBe('armor') + + // Leave only 1 non-permStore item on the armor tab and buy it + const nonPerm = wp.vendorTabPlacements.armor.find(p => !p.item.rawItem?.permStoreItem) + expect(nonPerm).toBeDefined() + wp.vendorTabPlacements.armor = [nonPerm!] + + const res = wp.buyFromActiveVendor(nonPerm!.col, nonPerm!.row, inv, false) + expect(res.ok).toBe(true) + expect(wp.vendorTabPlacements.armor).toHaveLength(0) + expect(wp.activeVendorTab).toBe('weapons1') + }) + + it('verifies VENDOR_TABS and VENDOR_BUTTON_SPECS match D2Client.dll 1.13c constants', () => { + expect(VENDOR_TABS.map(t => ({ xOffset: t.xOffset, width: t.width, height: t.height, centerX: t.centerX, labelZh: t.labelZh }))).toEqual([ + { xOffset: 0, width: 79, height: 31, centerX: 42, labelZh: '裝甲' }, + { xOffset: 80, width: 79, height: 31, centerX: 121, labelZh: '武器' }, + { xOffset: 160, width: 79, height: 31, centerX: 201, labelZh: '武器' }, + { xOffset: 240, width: 79, height: 31, centerX: 281, labelZh: '其他' }, + ]) + expect(VENDOR_BUTTON_SPECS.buy.labelZh).toBe('買') + expect(VENDOR_BUTTON_SPECS.sell.labelZh).toBe('賣') + expect(VENDOR_BUTTON_SPECS.repair.labelZh).toBe('修復') + expect(VENDOR_BUTTON_SPECS.close.labelZh).toBe('關閉') + }) +})