fix(frontend): align button fonts (FontExocet10/FontRidiculous) and multiply engraving blend with D2Win.dll & D2Launch.dll
This commit is contained in:
parent
a100885552
commit
298c6f6b39
|
|
@ -228,7 +228,7 @@ export function createInitialFlowState(initialRoster: RosterCharacter[] = []): B
|
|||
|
||||
return {
|
||||
stage: 'main_menu',
|
||||
gateway: 'U.S. West',
|
||||
gateway: 'U.S. East',
|
||||
stageTicks: 0,
|
||||
globalTick: 0,
|
||||
|
||||
|
|
|
|||
|
|
@ -332,6 +332,7 @@ export class FrontendRenderer {
|
|||
align?: 'left' | 'center' | 'right'
|
||||
vAlign?: 'top' | 'middle'
|
||||
lineGap?: number
|
||||
blendMode?: GlobalCompositeOperation
|
||||
} = {},
|
||||
): void {
|
||||
const font = options.font ?? 'font16'
|
||||
|
|
@ -344,10 +345,15 @@ export class FrontendRenderer {
|
|||
|
||||
const lines = text.split('\n')
|
||||
const frameH = meta.glyphs[65]?.frameH || meta.lineHeight
|
||||
const stepY = (options.lineGap ?? Math.max(meta.lineHeight, Math.round(frameH * 0.78)))
|
||||
const stepY = options.lineGap ?? Math.max(meta.lineHeight, Math.round(frameH * 0.78))
|
||||
const totalBlockH = frameH + (lines.length - 1) * stepY
|
||||
let curY = vAlign === 'middle' ? Math.round(y - totalBlockH / 2) : Math.round(y)
|
||||
|
||||
if (options.blendMode) {
|
||||
this.ctx.save()
|
||||
this.ctx.globalCompositeOperation = options.blendMode
|
||||
}
|
||||
|
||||
for (const line of lines) {
|
||||
const lineW = this.measureBitmapText(line, font)
|
||||
let curX = Math.round(x)
|
||||
|
|
@ -365,6 +371,10 @@ export class FrontendRenderer {
|
|||
}
|
||||
curY += stepY
|
||||
}
|
||||
|
||||
if (options.blendMode) {
|
||||
this.ctx.restore()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -434,7 +444,12 @@ export class FrontendRenderer {
|
|||
}
|
||||
|
||||
/**
|
||||
* Draw a 2-state button (`state = 0` Up, `state = 1` Down) with `(-2, +2)` pressed label offset.
|
||||
* Draw a 2-state button (`state = 0` Up, `state = 1` Down) strictly following
|
||||
* `D2Win.dll` `Button_Draw` (`0x6f8efa80`) and `Button_DrawText` (`0x6f8ef8d0`):
|
||||
* - `h >= 35` (`0x23`): `fontexocet10` (`fontId = 9`), `yAdjust = 4`
|
||||
* - `h < 35`: `fontridiculous` (`fontId = 10`), `yAdjust = h === 32 ? 3 : h > 20 ? 2 : 1`
|
||||
* - `color = 0`, `blendMode = 'multiply'` (`D2Gdi` `blendMode = 4` `multiplyBlend[src][dst]`)
|
||||
* - Pressed offset `(-2, +2)` (`0x6f8ef94e sub ebx, 2; add edi, 2`)
|
||||
*/
|
||||
private drawButton(btn: UiButtonDef): void {
|
||||
const img = this.images.get(btn.sprite)
|
||||
|
|
@ -443,21 +458,56 @@ export class FrontendRenderer {
|
|||
const stateW = Math.floor(img.width / 2)
|
||||
const stateH = img.height
|
||||
const sx = isDown ? stateW : 0
|
||||
this.ctx.drawImage(img, sx, 0, stateW, stateH, btn.x, btn.y, btn.w, btn.h)
|
||||
if (btn.disabled) {
|
||||
this.ctx.save()
|
||||
this.ctx.filter = 'brightness(0.55)'
|
||||
this.ctx.drawImage(img, sx, 0, stateW, stateH, btn.x, btn.y, btn.w, btn.h)
|
||||
this.ctx.restore()
|
||||
} else {
|
||||
this.ctx.drawImage(img, sx, 0, stateW, stateH, btn.x, btn.y, btn.w, btn.h)
|
||||
}
|
||||
}
|
||||
if (btn.label) {
|
||||
const dx = isDown ? -2 : 0
|
||||
const dy = isDown ? 2 : 0
|
||||
let font = btn.font ?? 'font24'
|
||||
if (font === 'font30' && this.measureBitmapText(btn.label, 'font30') > btn.w - 30) {
|
||||
font = 'font24'
|
||||
const font: FrontendFontName = btn.font ?? (btn.h >= 35 ? 'fontexocet10' : 'fontridiculous')
|
||||
const yAdjust = btn.h >= 35 ? 4 : btn.h === 32 ? 3 : btn.h > 20 ? 2 : 1
|
||||
const meta = this.fontMetas[font]
|
||||
const lineHeight = meta?.lineHeight ?? (btn.h >= 35 ? 13 : 8)
|
||||
const frameH = meta?.glyphs[65]?.frameH ?? lineHeight
|
||||
const centerX = btn.x + Math.floor(btn.w / 2) + dx
|
||||
const color: FrontendColorIndex = btn.color ?? 0
|
||||
|
||||
if (btn.label.includes('\n')) {
|
||||
// Two-line button (`[esi+0x40] & 0x40` at 0x6f8ef954..0x6f8efa55)
|
||||
const lines = btn.label.split('\n')
|
||||
const third = Math.trunc((btn.h - lineHeight) / 3)
|
||||
const yBottom1 = btn.y + btn.h - 2 * third + yAdjust + dy
|
||||
const yBottom2 = btn.y + btn.h - third + yAdjust + dy
|
||||
this.drawBitmapText(lines[0] ?? '', centerX, yBottom1 - frameH, {
|
||||
font,
|
||||
color,
|
||||
align: 'center',
|
||||
vAlign: 'top',
|
||||
blendMode: 'multiply',
|
||||
})
|
||||
this.drawBitmapText(lines[1] ?? '', centerX, yBottom2 - frameH, {
|
||||
font,
|
||||
color,
|
||||
align: 'center',
|
||||
vAlign: 'top',
|
||||
blendMode: 'multiply',
|
||||
})
|
||||
} else {
|
||||
const yBottom = btn.y + btn.h - ((btn.h - lineHeight) >> 1) + yAdjust + dy
|
||||
this.drawBitmapText(btn.label, centerX, yBottom - frameH, {
|
||||
font,
|
||||
color,
|
||||
align: 'center',
|
||||
vAlign: 'top',
|
||||
blendMode: 'multiply',
|
||||
})
|
||||
}
|
||||
this.drawBitmapText(btn.label, btn.x + Math.floor(btn.w / 2) + dx, btn.y + Math.floor(btn.h / 2) + dy, {
|
||||
font,
|
||||
color: btn.disabled ? 5 : (btn.color ?? 4),
|
||||
align: 'center',
|
||||
vAlign: 'middle',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -512,17 +562,16 @@ export class FrontendRenderer {
|
|||
|
||||
switch (this.state.stage) {
|
||||
case 'main_menu':
|
||||
// Exact Expansion Main Menu coordinates from D2Launch.dll (0x6fa61190..0x6fa612d0)
|
||||
return [
|
||||
{
|
||||
id: 'single_player',
|
||||
x: 264,
|
||||
y: 290,
|
||||
y: 289,
|
||||
w: 272,
|
||||
h: 35,
|
||||
sprite: 'btn-wide.png',
|
||||
label: s.singlePlayer,
|
||||
font: 'font30',
|
||||
color: 4,
|
||||
onClick: () => {
|
||||
clickBattleNetButton(this.state)
|
||||
},
|
||||
|
|
@ -530,13 +579,11 @@ export class FrontendRenderer {
|
|||
{
|
||||
id: 'battle_net',
|
||||
x: 264,
|
||||
y: 332,
|
||||
y: 331,
|
||||
w: 272,
|
||||
h: 35,
|
||||
sprite: 'btn-wide.png',
|
||||
label: s.battleNet,
|
||||
font: 'font30',
|
||||
color: 4,
|
||||
onClick: () => {
|
||||
clickBattleNetButton(this.state)
|
||||
},
|
||||
|
|
@ -544,15 +591,13 @@ export class FrontendRenderer {
|
|||
{
|
||||
id: 'gateway',
|
||||
x: 264,
|
||||
y: 369,
|
||||
y: 366,
|
||||
w: 272,
|
||||
h: 25,
|
||||
sprite: 'btn-wide02.png',
|
||||
label: `${s.gatewayPrefix}${this.state.gateway.toUpperCase()}`,
|
||||
font: 'font16',
|
||||
color: 4,
|
||||
onClick: () => {
|
||||
const gateways = ['U.S. West', 'U.S. East', 'Asia', 'Europe']
|
||||
const gateways = ['U.S. East', 'U.S. West', 'Asia', 'Europe']
|
||||
const idx = gateways.indexOf(this.state.gateway)
|
||||
this.state.gateway = gateways[(idx + 1) % gateways.length]!
|
||||
},
|
||||
|
|
@ -560,13 +605,11 @@ export class FrontendRenderer {
|
|||
{
|
||||
id: 'other_multiplayer',
|
||||
x: 264,
|
||||
y: 400,
|
||||
y: 398,
|
||||
w: 272,
|
||||
h: 35,
|
||||
sprite: 'btn-wide.png',
|
||||
label: s.otherMultiplayer,
|
||||
font: 'font30',
|
||||
color: 4,
|
||||
onClick: () => {
|
||||
clickBattleNetButton(this.state)
|
||||
},
|
||||
|
|
@ -574,37 +617,31 @@ export class FrontendRenderer {
|
|||
{
|
||||
id: 'credits',
|
||||
x: 264,
|
||||
y: 505,
|
||||
y: 503,
|
||||
w: 135,
|
||||
h: 25,
|
||||
sprite: 'btn-short.png',
|
||||
label: s.credits,
|
||||
font: 'font16',
|
||||
color: 4,
|
||||
onClick: () => {},
|
||||
},
|
||||
{
|
||||
id: 'cinematics',
|
||||
x: 401,
|
||||
y: 505,
|
||||
x: 402,
|
||||
y: 503,
|
||||
w: 135,
|
||||
h: 25,
|
||||
sprite: 'btn-short.png',
|
||||
label: s.cinematics,
|
||||
font: 'font16',
|
||||
color: 4,
|
||||
onClick: () => {},
|
||||
},
|
||||
{
|
||||
id: 'exit_d2',
|
||||
x: 264,
|
||||
y: 535,
|
||||
y: 533,
|
||||
w: 272,
|
||||
h: 35,
|
||||
sprite: 'btn-3wide.png',
|
||||
label: s.exitDiablo2,
|
||||
font: 'font30',
|
||||
color: 4,
|
||||
onClick: () => {},
|
||||
},
|
||||
]
|
||||
|
|
@ -619,8 +656,6 @@ export class FrontendRenderer {
|
|||
h: 35,
|
||||
sprite: 'btn-medium.png',
|
||||
label: s.cancel,
|
||||
font: 'font24',
|
||||
color: 4,
|
||||
onClick: () => {
|
||||
cancelConnecting(this.state)
|
||||
},
|
||||
|
|
@ -637,8 +672,6 @@ export class FrontendRenderer {
|
|||
h: 35,
|
||||
sprite: 'btn-wide.png',
|
||||
label: s.logIn,
|
||||
font: 'font24',
|
||||
color: 4,
|
||||
onClick: () => {
|
||||
submitBattleNetLogin(this.state)
|
||||
},
|
||||
|
|
@ -651,8 +684,6 @@ export class FrontendRenderer {
|
|||
h: 35,
|
||||
sprite: 'btn-wide.png',
|
||||
label: s.accountSettings,
|
||||
font: 'font24',
|
||||
color: 4,
|
||||
onClick: () => {
|
||||
submitBattleNetLogin(this.state)
|
||||
},
|
||||
|
|
@ -665,8 +696,6 @@ export class FrontendRenderer {
|
|||
h: 35,
|
||||
sprite: 'btn-wide.png',
|
||||
label: s.createNewAccount,
|
||||
font: 'font24',
|
||||
color: 4,
|
||||
onClick: () => {
|
||||
submitBattleNetLogin(this.state)
|
||||
},
|
||||
|
|
@ -679,8 +708,6 @@ export class FrontendRenderer {
|
|||
h: 35,
|
||||
sprite: 'btn-medium.png',
|
||||
label: s.cancel,
|
||||
font: 'font24',
|
||||
color: 4,
|
||||
onClick: () => {
|
||||
this.state.stage = 'main_menu'
|
||||
this.state.stageTicks = 0
|
||||
|
|
@ -699,8 +726,6 @@ export class FrontendRenderer {
|
|||
h: 35,
|
||||
sprite: 'btn-medium.png',
|
||||
label: s.exit,
|
||||
font: 'font24',
|
||||
color: 4,
|
||||
onClick: () => {
|
||||
if (this.state.roster.length > 0) {
|
||||
this.state.stage = 'char_select'
|
||||
|
|
@ -721,8 +746,6 @@ export class FrontendRenderer {
|
|||
h: 35,
|
||||
sprite: 'btn-medium.png',
|
||||
label: s.ok,
|
||||
font: 'font24',
|
||||
color: 4,
|
||||
disabled: !this.state.selectedClass,
|
||||
onClick: () => {
|
||||
submitCharacterCreate(this.state)
|
||||
|
|
@ -742,8 +765,6 @@ export class FrontendRenderer {
|
|||
h: 60,
|
||||
sprite: 'btn-tall.png',
|
||||
label: s.createNewCharacter,
|
||||
font: 'font16',
|
||||
color: 4,
|
||||
onClick: () => {
|
||||
enterCharCreate(this.state)
|
||||
},
|
||||
|
|
@ -756,8 +777,6 @@ export class FrontendRenderer {
|
|||
h: 60,
|
||||
sprite: 'btn-tall.png',
|
||||
label: s.convertToExpansion,
|
||||
font: 'font16',
|
||||
color: 4,
|
||||
disabled: !selectedChar || selectedChar.isExpansion,
|
||||
onClick: () => {
|
||||
if (selectedChar && !selectedChar.isExpansion) {
|
||||
|
|
@ -773,8 +792,6 @@ export class FrontendRenderer {
|
|||
h: 60,
|
||||
sprite: 'btn-tall.png',
|
||||
label: s.deleteCharacter,
|
||||
font: 'font16',
|
||||
color: 4,
|
||||
disabled: !selectedChar,
|
||||
onClick: () => {
|
||||
if (selectedChar) {
|
||||
|
|
@ -794,8 +811,6 @@ export class FrontendRenderer {
|
|||
h: 35,
|
||||
sprite: 'btn-medium.png',
|
||||
label: s.exit,
|
||||
font: 'font24',
|
||||
color: 4,
|
||||
onClick: () => {
|
||||
this.state.stage = 'main_menu'
|
||||
this.state.stageTicks = 0
|
||||
|
|
@ -810,8 +825,6 @@ export class FrontendRenderer {
|
|||
h: 35,
|
||||
sprite: 'btn-medium.png',
|
||||
label: s.ok,
|
||||
font: 'font24',
|
||||
color: 4,
|
||||
disabled: !hasChars,
|
||||
onClick: () => {
|
||||
confirmCharacterSelect(this.state)
|
||||
|
|
@ -832,8 +845,6 @@ export class FrontendRenderer {
|
|||
h: 20,
|
||||
sprite: 'btn-chatleft.png',
|
||||
label: L.send,
|
||||
font: 'font16',
|
||||
color: 4,
|
||||
onClick: () => {
|
||||
this.sendChatMessage()
|
||||
},
|
||||
|
|
@ -846,8 +857,6 @@ export class FrontendRenderer {
|
|||
h: 20,
|
||||
sprite: 'btn-chatleft.png',
|
||||
label: L.whisper,
|
||||
font: 'font16',
|
||||
color: 4,
|
||||
onClick: () => {},
|
||||
},
|
||||
{
|
||||
|
|
@ -858,8 +867,6 @@ export class FrontendRenderer {
|
|||
h: 20,
|
||||
sprite: 'btn-chatleft.png',
|
||||
label: L.help,
|
||||
font: 'font16',
|
||||
color: 4,
|
||||
onClick: () => {},
|
||||
},
|
||||
{
|
||||
|
|
@ -870,8 +877,6 @@ export class FrontendRenderer {
|
|||
h: 20,
|
||||
sprite: 'btn-chatleft.png',
|
||||
label: L.squelch,
|
||||
font: 'font16',
|
||||
color: 4,
|
||||
onClick: () => {},
|
||||
},
|
||||
{
|
||||
|
|
@ -882,8 +887,6 @@ export class FrontendRenderer {
|
|||
h: 20,
|
||||
sprite: 'btn-chatleft.png',
|
||||
label: L.unsquelch,
|
||||
font: 'font16',
|
||||
color: 4,
|
||||
onClick: () => {},
|
||||
},
|
||||
{
|
||||
|
|
@ -894,8 +897,6 @@ export class FrontendRenderer {
|
|||
h: 20,
|
||||
sprite: 'btn-chatleft.png',
|
||||
label: L.emote,
|
||||
font: 'font16',
|
||||
color: 4,
|
||||
onClick: () => {},
|
||||
},
|
||||
// Right Game Command Bar (D2Multi.dll 0x6f9e9e50..0x6f9e9f10)
|
||||
|
|
@ -907,8 +908,6 @@ export class FrontendRenderer {
|
|||
h: 20,
|
||||
sprite: 'btn-game.png',
|
||||
label: L.create,
|
||||
font: 'font16',
|
||||
color: 4,
|
||||
onClick: () => {
|
||||
this.state.lobbySubPanel = 'create'
|
||||
this.state.activeInput = 'gameName'
|
||||
|
|
@ -922,8 +921,6 @@ export class FrontendRenderer {
|
|||
h: 20,
|
||||
sprite: 'btn-game.png',
|
||||
label: L.join,
|
||||
font: 'font16',
|
||||
color: 4,
|
||||
onClick: () => {
|
||||
this.state.lobbySubPanel = 'create'
|
||||
},
|
||||
|
|
@ -936,8 +933,6 @@ export class FrontendRenderer {
|
|||
h: 20,
|
||||
sprite: 'btn-chatleft.png',
|
||||
label: L.channel,
|
||||
font: 'font16',
|
||||
color: 4,
|
||||
onClick: () => {},
|
||||
},
|
||||
{
|
||||
|
|
@ -948,8 +943,6 @@ export class FrontendRenderer {
|
|||
h: 20,
|
||||
sprite: 'btn-chatleft.png',
|
||||
label: L.ladder,
|
||||
font: 'font16',
|
||||
color: 4,
|
||||
onClick: () => {},
|
||||
},
|
||||
{
|
||||
|
|
@ -960,8 +953,6 @@ export class FrontendRenderer {
|
|||
h: 20,
|
||||
sprite: 'btn-chatleft.png',
|
||||
label: L.quit,
|
||||
font: 'font16',
|
||||
color: 4,
|
||||
onClick: () => {
|
||||
this.state.stage = 'char_select'
|
||||
this.state.stageTicks = 0
|
||||
|
|
@ -977,8 +968,6 @@ export class FrontendRenderer {
|
|||
h: 32,
|
||||
sprite: 'btn-create-cancel.png',
|
||||
label: s.cancel,
|
||||
font: 'font16',
|
||||
color: 4,
|
||||
onClick: () => {
|
||||
this.state.gameName = ''
|
||||
this.state.gamePassword = ''
|
||||
|
|
@ -992,8 +981,6 @@ export class FrontendRenderer {
|
|||
h: 32,
|
||||
sprite: 'btn-game.png',
|
||||
label: L.createGameBtn,
|
||||
font: 'font16',
|
||||
color: 4,
|
||||
onClick: () => {
|
||||
this.triggerCreateGame()
|
||||
},
|
||||
|
|
@ -1113,8 +1100,8 @@ export class FrontendRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
// Version string at bottom-right
|
||||
this.drawBitmapText('v 1.13c', 748, 578, { font: 'fontformal10', color: 0, align: 'left' })
|
||||
// Version string at bottom-left (`D2Launch.dll` 0x6fa64310: x=0, w=200, y_bottom=599, h=40, align=2, Font16, color=0)
|
||||
this.drawBitmapText('v 1.13c', 100, 560, { font: 'font16', color: 0, align: 'center' })
|
||||
}
|
||||
|
||||
private renderBnetLogin(): void {
|
||||
|
|
@ -1151,8 +1138,6 @@ export class FrontendRenderer {
|
|||
for (const btn of this.getActiveButtons()) {
|
||||
this.drawButton(btn)
|
||||
}
|
||||
|
||||
this.drawBitmapText('v 1.13c', 748, 578, { font: 'fontformal10', color: 0, align: 'left' })
|
||||
}
|
||||
|
||||
private renderCharCreate(): void {
|
||||
|
|
|
|||
Loading…
Reference in New Issue