fix(ui): remove non-authentic deposit/withdraw buttons over stash bottom keyhole area

This commit is contained in:
troytt 2026-09-28 01:27:10 +00:00
parent b6fc2512d4
commit a06777be07
3 changed files with 41 additions and 116 deletions

View File

@ -60,8 +60,6 @@ export const STASH_GRID_ORIGIN = { x: 80 + 74, y: 60 + 82, cols: 6, rows: 8, cel
export const STASH_GOLD_CAP = 2_500_000
export const PLAYER_GOLD_CAP = 2_500_000
export const STASH_CLOSE_BTN_BOUNDS = { x: 80 + 272, y: 60 + 388, w: 32, h: 32 } as const
export const STASH_DEPOSIT_BTN_BOUNDS = { x: 80 + 68, y: 60 + 386, w: 76, h: 26 } as const
export const STASH_WITHDRAW_BTN_BOUNDS = { x: 80 + 154, y: 60 + 386, w: 76, h: 26 } as const
/**
* `Panel\TradeStash.dc6` (`public/ui/stash-bg.png`, 320x432 anchored at `ox = 80, oy = 60`)
@ -1675,8 +1673,7 @@ export class WorldPanelsHud {
withdrawGold(amount: number, playerInventory: { gold: number; maxGold?: number }): number {
if (amount <= 0) return 0
const maxInvGold = playerInventory.maxGold ?? PLAYER_GOLD_CAP
const available = Math.max(0, Math.min(amount, this.stashGold, maxInvGold - playerInventory.gold))
const available = Math.max(0, Math.min(amount, this.stashGold, PLAYER_GOLD_CAP - playerInventory.gold))
if (available <= 0) return 0
this.stashGold -= available
playerInventory.gold += available
@ -2018,39 +2015,7 @@ export class WorldPanelsHud {
return true
}
} else if (kind === 'stash') {
// 1. Gold deposit button
if (
logicalX >= STASH_DEPOSIT_BTN_BOUNDS.x &&
logicalX <= STASH_DEPOSIT_BTN_BOUNDS.x + STASH_DEPOSIT_BTN_BOUNDS.w &&
logicalY >= STASH_DEPOSIT_BTN_BOUNDS.y &&
logicalY <= STASH_DEPOSIT_BTN_BOUNDS.y + STASH_DEPOSIT_BTN_BOUNDS.h
) {
if (!callbacks.isRightClick && callbacks.inventory) {
const depositAmt = Math.min(50_000, callbacks.inventory.gold, STASH_GOLD_CAP - this.stashGold)
if (depositAmt > 0) {
this.depositGold(depositAmt, callbacks.inventory)
}
}
return true
}
// 2. Gold withdraw button
if (
logicalX >= STASH_WITHDRAW_BTN_BOUNDS.x &&
logicalX <= STASH_WITHDRAW_BTN_BOUNDS.x + STASH_WITHDRAW_BTN_BOUNDS.w &&
logicalY >= STASH_WITHDRAW_BTN_BOUNDS.y &&
logicalY <= STASH_WITHDRAW_BTN_BOUNDS.y + STASH_WITHDRAW_BTN_BOUNDS.h
) {
if (!callbacks.isRightClick && callbacks.inventory) {
const withdrawAmt = Math.min(50_000, this.stashGold, PLAYER_GOLD_CAP - callbacks.inventory.gold)
if (withdrawAmt > 0) {
this.withdrawGold(withdrawAmt, callbacks.inventory)
}
}
return true
}
// 3. Stash 6x8 Grid
// Stash 6x8 Grid
if (
logicalX >= STASH_GRID_ORIGIN.x &&
logicalX < STASH_GRID_ORIGIN.x + STASH_GRID_ORIGIN.cols * STASH_GRID_ORIGIN.cellPx &&
@ -2383,60 +2348,6 @@ export class WorldPanelsHud {
ctx.fillRect(gx + 2, gy + 2, gw - 4, gh - 4)
}
}
// Deposit button
ctx.fillStyle = 'rgba(20, 16, 12, 0.9)'
ctx.fillRect(
STASH_DEPOSIT_BTN_BOUNDS.x,
STASH_DEPOSIT_BTN_BOUNDS.y,
STASH_DEPOSIT_BTN_BOUNDS.w,
STASH_DEPOSIT_BTN_BOUNDS.h,
)
ctx.strokeStyle = '#8a7248'
ctx.strokeRect(
STASH_DEPOSIT_BTN_BOUNDS.x + 0.5,
STASH_DEPOSIT_BTN_BOUNDS.y + 0.5,
STASH_DEPOSIT_BTN_BOUNDS.w - 1,
STASH_DEPOSIT_BTN_BOUNDS.h - 1,
)
font.drawText(
ctx,
'存入',
STASH_DEPOSIT_BTN_BOUNDS.x + STASH_DEPOSIT_BTN_BOUNDS.w / 2,
STASH_DEPOSIT_BTN_BOUNDS.y + 16,
{
font: 'font8',
color: 'gold',
align: 'center',
},
)
// Withdraw button
ctx.fillStyle = 'rgba(20, 16, 12, 0.9)'
ctx.fillRect(
STASH_WITHDRAW_BTN_BOUNDS.x,
STASH_WITHDRAW_BTN_BOUNDS.y,
STASH_WITHDRAW_BTN_BOUNDS.w,
STASH_WITHDRAW_BTN_BOUNDS.h,
)
ctx.strokeStyle = '#8a7248'
ctx.strokeRect(
STASH_WITHDRAW_BTN_BOUNDS.x + 0.5,
STASH_WITHDRAW_BTN_BOUNDS.y + 0.5,
STASH_WITHDRAW_BTN_BOUNDS.w - 1,
STASH_WITHDRAW_BTN_BOUNDS.h - 1,
)
font.drawText(
ctx,
'取出',
STASH_WITHDRAW_BTN_BOUNDS.x + STASH_WITHDRAW_BTN_BOUNDS.w / 2,
STASH_WITHDRAW_BTN_BOUNDS.y + 16,
{
font: 'font8',
color: 'gold',
align: 'center',
},
)
} else if (kind === 'cube') {
if (assets.cubeBgImg) ctx.drawImage(assets.cubeBgImg, ox, oy)
font.drawText(ctx, `赫拉迪克方块 (已合成 ${this.cubeTransmuteCount} 次)`, ox + 160, oy + 32, {

View File

@ -9,8 +9,6 @@ import {
WorldPanelsHud,
STASH_GOLD_CAP,
PLAYER_GOLD_CAP,
STASH_DEPOSIT_BTN_BOUNDS,
STASH_WITHDRAW_BTN_BOUNDS,
} from '../src/ui/world-panels.ts'
import { HudManager } from '../src/ui/hud-manager.ts'
import { GameEngine } from '../src/game/engine.ts'
@ -584,7 +582,7 @@ describe('Challenger M5 Empirical Stress Test: Town Stash Entities, Interaction
expect(panels.stashGold).toBe(50_000)
})
it('handles simulated UI button clicks for deposit and withdraw', () => {
it('does not intercept bottom keyhole ornament clicks as non-authentic deposit/withdraw buttons', () => {
inv.gold = 100_000
panels.stashGold = 200_000
@ -594,19 +592,12 @@ describe('Challenger M5 Empirical Stress Test: Town Stash Entities, Interaction
inventory: inv as any,
}
// Click deposit button (center of STASH_DEPOSIT_BTN_BOUNDS)
const depX = STASH_DEPOSIT_BTN_BOUNDS.x + 10
const depY = STASH_DEPOSIT_BTN_BOUNDS.y + 10
const handledDep = panels.handleLeftDockClick('stash', depX, depY, clickCallbacks)
expect(handledDep).toBe(true)
expect(panels.stashGold).toBe(250_000) // Default batch is 50,000
expect(inv.gold).toBe(50_000)
// Click bottom keyhole area where non-authentic deposit/withdraw buttons used to be
panels.handleLeftDockClick('stash', 80 + 84, 60 + 388, clickCallbacks)
expect(panels.stashGold).toBe(200_000)
expect(inv.gold).toBe(100_000)
// Click withdraw button (center of STASH_WITHDRAW_BTN_BOUNDS)
const wX = STASH_WITHDRAW_BTN_BOUNDS.x + 10
const wY = STASH_WITHDRAW_BTN_BOUNDS.y + 10
const handledW = panels.handleLeftDockClick('stash', wX, wY, clickCallbacks)
expect(handledW).toBe(true)
panels.handleLeftDockClick('stash', 80 + 168, 60 + 388, clickCallbacks)
expect(panels.stashGold).toBe(200_000)
expect(inv.gold).toBe(100_000)
})

View File

@ -13,8 +13,6 @@ import {
STASH_GRID_ORIGIN,
STASH_GOLD_CAP,
PLAYER_GOLD_CAP,
STASH_DEPOSIT_BTN_BOUNDS,
STASH_WITHDRAW_BTN_BOUNDS,
STASH_HEADER_ICON_SOCKET,
STASH_HEADER_TOP_RECESS,
STASH_HEADER_BOTTOM_RECESS,
@ -155,15 +153,40 @@ describe('Milestone M5 (Issue #150): Camp Town Stash Entities Across 5 Acts & 6x
expect(STASH_GRID_ORIGIN.y + STASH_GRID_ORIGIN.rows * STASH_GRID_ORIGIN.cellPx).toBe(374)
})
it('defines deposit and withdraw button bounds inside stash panel', () => {
expect(STASH_DEPOSIT_BTN_BOUNDS.x).toBeGreaterThanOrEqual(STASH_PANEL_ORIGIN.x)
expect(STASH_DEPOSIT_BTN_BOUNDS.x + STASH_DEPOSIT_BTN_BOUNDS.w).toBeLessThanOrEqual(
STASH_PANEL_ORIGIN.x + STASH_PANEL_ORIGIN.w,
)
expect(STASH_WITHDRAW_BTN_BOUNDS.x).toBeGreaterThanOrEqual(STASH_PANEL_ORIGIN.x)
expect(STASH_WITHDRAW_BTN_BOUNDS.x + STASH_WITHDRAW_BTN_BOUNDS.w).toBeLessThanOrEqual(
STASH_PANEL_ORIGIN.x + STASH_PANEL_ORIGIN.w,
it('does not render non-authentic deposit or withdraw buttons over bottom keyhole area', () => {
const panels = new WorldPanelsHud()
const drawTextCalls: string[] = []
const mockFont = {
drawText: (_ctx: CanvasRenderingContext2D, text: string) => {
drawTextCalls.push(text)
},
measureText: (text: string) => text.length * 6.5,
} as unknown as D2FontRenderer
const mockCtx = {
drawImage: () => {},
fillRect: () => {},
strokeRect: () => {},
fillStyle: '',
strokeStyle: '',
} as unknown as CanvasRenderingContext2D
panels.drawLeftDockPanel(
mockCtx,
'stash',
{
questBgImg: null,
waypointBgImg: null,
borderLeftImg: null,
stashBgImg: null,
cubeBgImg: null,
vendorBgImg: null,
buySellBtnImg: null,
},
mockFont,
)
expect(drawTextCalls).not.toContain('存入')
expect(drawTextCalls).not.toContain('取出')
})
it('confirms stash background graphic asset exists in public/ui/stash-bg.png', () => {