From a727343eb3ac7d9fd68d323be2026b47648dd826 Mon Sep 17 00:00:00 2001 From: troytt <47798984@qq.com> Date: Tue, 22 Sep 2026 03:30:22 +0000 Subject: [PATCH] feat(arena): add switchAndCastSkill and hero class avatar overlay to ?arena=1 --- src/scene/act-scene.ts | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/src/scene/act-scene.ts b/src/scene/act-scene.ts index d14d9d3..ff77266 100644 --- a/src/scene/act-scene.ts +++ b/src/scene/act-scene.ts @@ -3842,7 +3842,7 @@ export async function initBrowserArenaMode(): Promise { const { WorldArena } = await import('../game/engine/world-arena.ts') const registry = await getSharedDataRegistry('samples/d2') - const arena = new WorldArena({ + let arena = new WorldArena({ registry, classCode: classParam, skillId, @@ -3851,10 +3851,13 @@ export async function initBrowserArenaMode(): Promise { if (autocast) { arena.triggerCast() + arena.stepTicks(15) } const syncWindowArena = () => { - const snap = arena.getDebugState() + const snap = arena.getDebugState() as ReturnType & { + switchAndCastSkill?: (nextClassCode: string, nextSkillId: number, nextSlvl?: number) => unknown + } const origTrigger = snap.triggerCast const origStep = snap.stepTicks snap.triggerCast = (tx?: number, ty?: number) => { @@ -3868,6 +3871,19 @@ export async function initBrowserArenaMode(): Promise { syncWindowArena() renderArenaVisualOverlay(arena) } + snap.switchAndCastSkill = (nextClassCode: string, nextSkillId: number, nextSlvl = 20) => { + arena = new WorldArena({ + registry, + classCode: nextClassCode, + skillId: nextSkillId, + slvl: nextSlvl, + }) + arena.triggerCast() + arena.stepTicks(15) + syncWindowArena() + renderArenaVisualOverlay(arena) + return arena.getDebugState() + } ;(window as unknown as { __d2webArena?: unknown }).__d2webArena = snap } @@ -3892,13 +3908,27 @@ export async function initBrowserArenaMode(): Promise { const cx = w * 0.5 const cy = h * 0.52 - // 1. Draw Active Aura / Cast Ring around Hero + // 1. Draw Active Aura / Cast Ring & Class Hero Avatar around Hero + const classHue = ((dbg.skillId * 37) % 360) ctx.save() - ctx.strokeStyle = dbg.activeStates.length > 0 ? 'rgba(255, 210, 80, 0.85)' : 'rgba(110, 180, 255, 0.7)' - ctx.lineWidth = 3 + ctx.strokeStyle = dbg.activeStates.length > 0 ? 'rgba(255, 210, 80, 0.9)' : `hsla(${classHue}, 85%, 65%, 0.85)` + ctx.lineWidth = 3.5 ctx.beginPath() - ctx.ellipse(cx, cy + 16, 62, 32, 0, 0, Math.PI * 2) + ctx.ellipse(cx, cy + 16, 62 + (dbg.skillId % 18), 32 + (dbg.skillId % 9), 0, 0, Math.PI * 2) ctx.stroke() + + // Hero class figure + active weapon/spell glow + ctx.fillStyle = `hsla(${classHue}, 75%, 55%, 0.85)` + ctx.strokeStyle = '#fff3d1' + ctx.lineWidth = 2 + ctx.beginPath() + ctx.arc(cx, cy - 6, 18, 0, Math.PI * 2) + ctx.fill() + ctx.stroke() + ctx.fillStyle = '#ffe9a8' + ctx.font = 'bold 11px monospace' + ctx.textAlign = 'center' + ctx.fillText(`${dbg.classCode.toUpperCase()}:${dbg.charToken.toUpperCase()} [${dbg.playerAnimMode}:${dbg.playerAnimFrame}]`, cx, cy + 35) ctx.restore() // 2. Draw Target Dummies (`dummy-normal`, `dummy-undead`, `dummy-demon`, `dummy-immune`)