Merge pull request 'fix(ui): align Private Stash header and gold text inside TradeStash.dc6 recessed boxes (#493)' (#494) from fix/stash-text-align into main
Merge pull request 'fix(ui): align Private Stash header and gold text inside TradeStash.dc6 recessed boxes (#493)' (#494) from fix/stash-text-align into main
This commit is contained in:
commit
d2358ede37
|
|
@ -47,6 +47,45 @@ export const STASH_CLOSE_BTN_BOUNDS = { x: 80 + 272, y: 60 + 388, w: 32, h: 32 }
|
|||
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`)
|
||||
* header recessed dark boxes (Issue #493):
|
||||
* - Top box (`relY = 24..38`, height 15px) is divided by a vertical bar at `relX = 94..96` into:
|
||||
* - Left icon socket: `relX = 74..93, relY = 24..38` (20x15px)
|
||||
* - Right title text recess: `relX = 97..247, relY = 24..38` (151x15px, center `relX = 172, relY = 31`, alphabetic baseline `oy + 35`)
|
||||
* - Bottom gold text recess: `relX = 75..246, relY = 49..65` (172x17px, center `relX = 160, relY = 57`, alphabetic baseline `oy + 61`)
|
||||
*/
|
||||
export const STASH_HEADER_ICON_SOCKET = {
|
||||
x: 80 + 74,
|
||||
y: 60 + 24,
|
||||
relX: 74,
|
||||
relY: 24,
|
||||
w: 20,
|
||||
h: 15,
|
||||
} as const
|
||||
|
||||
export const STASH_HEADER_TOP_RECESS = {
|
||||
x: 80 + 97,
|
||||
y: 60 + 24,
|
||||
relX: 97,
|
||||
relY: 24,
|
||||
w: 151,
|
||||
h: 15,
|
||||
textX: 80 + 172,
|
||||
textY: 60 + 35,
|
||||
} as const
|
||||
|
||||
export const STASH_HEADER_BOTTOM_RECESS = {
|
||||
x: 80 + 75,
|
||||
y: 60 + 49,
|
||||
relX: 75,
|
||||
relY: 49,
|
||||
w: 172,
|
||||
h: 17,
|
||||
textX: 80 + 160,
|
||||
textY: 60 + 61,
|
||||
} as const
|
||||
|
||||
/** `Inventory.txt` `Transmogrify Box Page 1` / `Transmogrify Box2` (`supertransmogrifier.dc6` 3x4 grid) */
|
||||
export const CUBE_PANEL_ORIGIN = { x: 80, y: 60, width: 320, height: 432, w: 320, h: 432 } as const
|
||||
export const CUBE_GRID_ORIGIN = { x: 80 + 117, y: 60 + 139, cols: 3, rows: 4, cellPx: 29 } as const
|
||||
|
|
@ -1916,12 +1955,12 @@ export class WorldPanelsHud {
|
|||
}
|
||||
} else if (kind === 'stash') {
|
||||
if (assets.stashBgImg) ctx.drawImage(assets.stashBgImg, ox, oy)
|
||||
font.drawText(ctx, '私人储物箱 (Private Stash)', ox + 160, oy + 26, {
|
||||
font: 'fontexocet10',
|
||||
font.drawText(ctx, '私人储物箱 (Private Stash)', ox + 172, oy + 35, {
|
||||
font: 'font8',
|
||||
color: 'gold',
|
||||
align: 'center',
|
||||
})
|
||||
font.drawText(ctx, `储物箱金币: ${this.stashGold.toLocaleString()}`, ox + 160, oy + 48, {
|
||||
font.drawText(ctx, `储物箱金币: ${this.stashGold.toLocaleString()}`, ox + 160, oy + 61, {
|
||||
font: 'font8',
|
||||
color: 'white',
|
||||
align: 'center',
|
||||
|
|
|
|||
|
|
@ -15,8 +15,12 @@ import {
|
|||
PLAYER_GOLD_CAP,
|
||||
STASH_DEPOSIT_BTN_BOUNDS,
|
||||
STASH_WITHDRAW_BTN_BOUNDS,
|
||||
STASH_HEADER_ICON_SOCKET,
|
||||
STASH_HEADER_TOP_RECESS,
|
||||
STASH_HEADER_BOTTOM_RECESS,
|
||||
WorldPanelsHud,
|
||||
} from '../src/ui/world-panels.ts'
|
||||
import type { D2FontRenderer } from '../src/ui/font.ts'
|
||||
import {
|
||||
InventoryPanel,
|
||||
type UiInventoryItem,
|
||||
|
|
@ -604,4 +608,123 @@ describe('Milestone M5 (Issue #150): Camp Town Stash Entities Across 5 Acts & 6x
|
|||
expect(hud.inventory.visible).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('8. Stash Header Recessed Boxes & Text Alignment Parity (Issue #493)', () => {
|
||||
it('defines exact TradeStash.dc6 recessed box bounds and centered baselines', () => {
|
||||
// Left square icon socket in top row: relX = 74..93, relY = 24..38 (20x15)
|
||||
expect(STASH_HEADER_ICON_SOCKET).toEqual({
|
||||
x: 80 + 74,
|
||||
y: 60 + 24,
|
||||
relX: 74,
|
||||
relY: 24,
|
||||
w: 20,
|
||||
h: 15,
|
||||
})
|
||||
|
||||
// Right title recess in top row (after relX = 94..96 divider): relX = 97..247, relY = 24..38 (151x15)
|
||||
expect(STASH_HEADER_TOP_RECESS).toEqual({
|
||||
x: 80 + 97,
|
||||
y: 60 + 24,
|
||||
relX: 97,
|
||||
relY: 24,
|
||||
w: 151,
|
||||
h: 15,
|
||||
textX: 80 + 172,
|
||||
textY: 60 + 35,
|
||||
})
|
||||
|
||||
// Full-width gold readout recess in bottom row: relX = 75..246, relY = 49..65 (172x17)
|
||||
expect(STASH_HEADER_BOTTOM_RECESS).toEqual({
|
||||
x: 80 + 75,
|
||||
y: 60 + 49,
|
||||
relX: 75,
|
||||
relY: 49,
|
||||
w: 172,
|
||||
h: 17,
|
||||
textX: 80 + 160,
|
||||
textY: 60 + 61,
|
||||
})
|
||||
})
|
||||
|
||||
it('renders Private Stash title and gold readout centered inside their respective recessed boxes without crossing dividers', () => {
|
||||
const panels = new WorldPanelsHud()
|
||||
panels.stashGold = 2_500_000
|
||||
|
||||
const drawTextCalls: Array<{
|
||||
text: string
|
||||
x: number
|
||||
y: number
|
||||
opts?: { font?: string; color?: string; align?: string }
|
||||
}> = []
|
||||
|
||||
const mockFont = {
|
||||
drawText: (_ctx: CanvasRenderingContext2D, text: string, x: number, y: number, opts?: any) => {
|
||||
drawTextCalls.push({ text, x, y, opts })
|
||||
},
|
||||
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,
|
||||
)
|
||||
|
||||
const titleCall = drawTextCalls.find(c => c.text.includes('私人储物箱'))
|
||||
const goldCall = drawTextCalls.find(c => c.text.includes('储物箱金币'))
|
||||
|
||||
expect(titleCall).toBeDefined()
|
||||
expect(goldCall).toBeDefined()
|
||||
|
||||
// 1. Top title must be centered in the right compartment (relX = 97..247 -> center ox + 172 = 252)
|
||||
// and vertically centered in relY = 24..38 (alphabetic baseline oy + 35 = 95)
|
||||
expect(titleCall!.x).toBe(STASH_HEADER_TOP_RECESS.textX)
|
||||
expect(titleCall!.y).toBe(STASH_HEADER_TOP_RECESS.textY)
|
||||
expect(titleCall!.opts?.font).toBe('font8')
|
||||
expect(titleCall!.opts?.color).toBe('gold')
|
||||
expect(titleCall!.opts?.align).toBe('center')
|
||||
|
||||
// Verify title glyph vertical span [y - 9, y + 1] stays strictly inside top recess [oy + 24, oy + 38]
|
||||
const topRecessTop = STASH_HEADER_TOP_RECESS.y
|
||||
const topRecessBottom = STASH_HEADER_TOP_RECESS.y + STASH_HEADER_TOP_RECESS.h - 1
|
||||
expect(titleCall!.y - 9).toBeGreaterThanOrEqual(topRecessTop)
|
||||
expect(titleCall!.y + 1).toBeLessThanOrEqual(topRecessBottom)
|
||||
|
||||
// Verify title horizontal span (~130px in font8) stays strictly inside [ox + 97, ox + 247] (never crossing relX = 94..96 divider)
|
||||
const approxTitleWidth = 130
|
||||
expect(titleCall!.x - approxTitleWidth / 2).toBeGreaterThan(STASH_HEADER_TOP_RECESS.x)
|
||||
expect(titleCall!.x + approxTitleWidth / 2).toBeLessThan(STASH_HEADER_TOP_RECESS.x + STASH_HEADER_TOP_RECESS.w)
|
||||
|
||||
// 2. Bottom gold text must be centered in the bottom recess (relX = 75..246 -> center ox + 160 = 240)
|
||||
// and vertically centered in relY = 49..65 (alphabetic baseline oy + 61 = 121)
|
||||
expect(goldCall!.x).toBe(STASH_HEADER_BOTTOM_RECESS.textX)
|
||||
expect(goldCall!.y).toBe(STASH_HEADER_BOTTOM_RECESS.textY)
|
||||
expect(goldCall!.opts?.font).toBe('font8')
|
||||
expect(goldCall!.opts?.color).toBe('white')
|
||||
expect(goldCall!.opts?.align).toBe('center')
|
||||
|
||||
// Verify gold glyph vertical span [y - 9, y + 1] stays strictly inside bottom recess [oy + 49, oy + 65]
|
||||
const bottomRecessTop = STASH_HEADER_BOTTOM_RECESS.y
|
||||
const bottomRecessBottom = STASH_HEADER_BOTTOM_RECESS.y + STASH_HEADER_BOTTOM_RECESS.h - 1
|
||||
expect(goldCall!.y - 9).toBeGreaterThanOrEqual(bottomRecessTop)
|
||||
expect(goldCall!.y + 1).toBeLessThanOrEqual(bottomRecessBottom)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue