/** * TankSkinRenderer.js * Shared tank-skin drawing primitives used by BOTH the SkinScene preview * and the in-game Tank.render() path. This is the SINGLE SOURCE OF TRUTH * for how each skin looks. * * Coordinate contract for every _tankXxx(ctx, bc, tc, kc, t) function: * • ctx is already translated to the tank center and rotated so that * the barrel points toward negative-Y (i.e. "up" on screen). * • All drawings use a DESIGN UNIT of s ≈ 12 pixels (matches the * SkinScene preview). In-game callers should call ctx.scale(k, k) * beforehand where k = this.halfSize / DESIGN_HALF_SIZE to make the * skin match the actual collision box. * • bc / tc / kc are body / turret / track colors. * • t is a seconds-based animation timer (optional for static skins). */ const DESIGN_HALF_SIZE = 12; // matches the "s" used in most _tankXxx funcs // --------------------------------------------------------------- // Rounded-rect helper (local copy so we don't depend on any scene) // --------------------------------------------------------------- function _roundRect(ctx, x, y, w, h, r) { ctx.beginPath(); ctx.moveTo(x + r, y); ctx.lineTo(x + w - r, y); ctx.arcTo(x + w, y, x + w, y + r, r); ctx.lineTo(x + w, y + h - r); ctx.arcTo(x + w, y + h, x + w - r, y + h, r); ctx.lineTo(x + r, y + h); ctx.arcTo(x, y + h, x, y + h - r, r); ctx.lineTo(x, y + r); ctx.arcTo(x, y, x + r, y, r); ctx.closePath(); } // ======== DEFAULT — Classic rounded tank ======== function _tankDefault(ctx, bc, tc, kc /* , t */) { const s = 12; ctx.fillStyle = kc; _roundRect(ctx, -s - 4, -s + 1, 4, s * 2 - 2, 2); ctx.fill(); _roundRect(ctx, s, -s + 1, 4, s * 2 - 2, 2); ctx.fill(); ctx.fillStyle = bc; _roundRect(ctx, -s, -s, s * 2, s * 2, 3); ctx.fill(); ctx.fillStyle = 'rgba(255,255,255,0.15)'; ctx.fillRect(-s + 2, -s + 2, s - 2, 3); ctx.fillStyle = tc; ctx.beginPath(); ctx.arc(0, 0, s * 0.4, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = tc; _roundRect(ctx, -2.5, -s - 8, 5, s - 2, 2); ctx.fill(); ctx.fillStyle = 'rgba(255,255,255,0.2)'; ctx.fillRect(-1.5, -s - 8, 3, 3); } // ======== ARCTIC ======== function _tankArctic(ctx, bc, tc, kc /* , t */) { const s = 12; ctx.fillStyle = kc; ctx.fillRect(-s - 5, -s, 5, s * 2); ctx.fillRect(s, -s, 5, s * 2); ctx.strokeStyle = 'rgba(255,255,255,0.25)'; ctx.lineWidth = 0.8; for (let ty = -s + 3; ty < s; ty += 4) { ctx.beginPath(); ctx.moveTo(-s - 5, ty); ctx.lineTo(-s, ty); ctx.stroke(); ctx.beginPath(); ctx.moveTo(s, ty); ctx.lineTo(s + 5, ty); ctx.stroke(); } ctx.fillStyle = bc; ctx.beginPath(); ctx.moveTo(0, -s - 2); ctx.lineTo(s - 1, -s + 4); ctx.lineTo(s - 1, s - 4); ctx.lineTo(0, s + 2); ctx.lineTo(-s + 1, s - 4); ctx.lineTo(-s + 1, -s + 4); ctx.closePath(); ctx.fill(); ctx.fillStyle = 'rgba(255,255,255,0.3)'; ctx.beginPath(); ctx.moveTo(-s + 3, -s + 4); ctx.lineTo(0, -s - 1); ctx.lineTo(2, -s + 5); ctx.closePath(); ctx.fill(); ctx.strokeStyle = 'rgba(255,255,255,0.5)'; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(0, -4); ctx.lineTo(0, 4); ctx.stroke(); ctx.beginPath(); ctx.moveTo(-4, 0); ctx.lineTo(4, 0); ctx.stroke(); ctx.beginPath(); ctx.moveTo(-3, -3); ctx.lineTo(3, 3); ctx.stroke(); ctx.beginPath(); ctx.moveTo(3, -3); ctx.lineTo(-3, 3); ctx.stroke(); ctx.fillStyle = tc; ctx.beginPath(); ctx.moveTo(0, -5); ctx.lineTo(5, 0); ctx.lineTo(0, 5); ctx.lineTo(-5, 0); ctx.closePath(); ctx.fill(); ctx.fillStyle = tc; ctx.beginPath(); ctx.moveTo(-3, -s + 3); ctx.lineTo(3, -s + 3); ctx.lineTo(2, -s - 9); ctx.lineTo(-2, -s - 9); ctx.closePath(); ctx.fill(); ctx.fillStyle = 'rgba(176,224,230,0.6)'; ctx.beginPath(); ctx.arc(0, -s - 9, 2, 0, Math.PI * 2); ctx.fill(); } // ======== INFERNO ======== function _tankInferno(ctx, bc, tc, kc /* , t */) { const s = 12; ctx.fillStyle = kc; ctx.fillRect(-s - 4, -s, 4, s * 2); ctx.fillRect(s, -s, 4, s * 2); for (let ty = -s + 2; ty < s - 2; ty += 5) { ctx.fillStyle = kc; ctx.beginPath(); ctx.moveTo(-s - 4, ty); ctx.lineTo(-s - 7, ty + 2.5); ctx.lineTo(-s - 4, ty + 5); ctx.fill(); ctx.beginPath(); ctx.moveTo(s + 4, ty); ctx.lineTo(s + 7, ty + 2.5); ctx.lineTo(s + 4, ty + 5); ctx.fill(); } ctx.fillStyle = bc; ctx.beginPath(); ctx.moveTo(-s + 2, -s - 1); ctx.lineTo(s - 2, -s - 1); ctx.lineTo(s + 1, -s + 4); ctx.lineTo(s + 1, s - 2); ctx.lineTo(s - 3, s + 1); ctx.lineTo(-s + 3, s + 1); ctx.lineTo(-s - 1, s - 2); ctx.lineTo(-s - 1, -s + 4); ctx.closePath(); ctx.fill(); ctx.fillStyle = 'rgba(255,165,0,0.5)'; ctx.beginPath(); ctx.moveTo(-s + 3, s); ctx.lineTo(-s + 6, 0); ctx.lineTo(-s + 3, -2); ctx.lineTo(-s + 1, s - 2); ctx.closePath(); ctx.fill(); ctx.beginPath(); ctx.moveTo(s - 3, s); ctx.lineTo(s - 6, 0); ctx.lineTo(s - 3, -2); ctx.lineTo(s + 1, s - 2); ctx.closePath(); ctx.fill(); ctx.fillStyle = 'rgba(255,69,0,0.3)'; ctx.beginPath(); ctx.arc(0, 0, 5, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = tc; ctx.beginPath(); ctx.moveTo(0, -5); ctx.lineTo(5, -2); ctx.lineTo(4, 4); ctx.lineTo(-4, 4); ctx.lineTo(-5, -2); ctx.closePath(); ctx.fill(); ctx.fillStyle = tc; _roundRect(ctx, -4, -s - 10, 3, s - 1, 1); ctx.fill(); _roundRect(ctx, 1, -s - 10, 3, s - 1, 1); ctx.fill(); ctx.fillStyle = 'rgba(255,100,0,0.7)'; ctx.beginPath(); ctx.arc(-2.5, -s - 10, 2, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(2.5, -s - 10, 2, 0, Math.PI * 2); ctx.fill(); } // ======== PHANTOM ======== function _tankPhantom(ctx, bc, tc, kc, t) { const s = 12; const tt = t || 0; const flicker = 0.6 + Math.sin(tt * 5) * 0.15; ctx.save(); ctx.globalAlpha = ctx.globalAlpha * flicker; ctx.fillStyle = kc; _roundRect(ctx, -s - 3, -s + 2, 3, s * 2 - 4, 1.5); ctx.fill(); _roundRect(ctx, s, -s + 2, 3, s * 2 - 4, 1.5); ctx.fill(); ctx.fillStyle = bc; ctx.beginPath(); ctx.ellipse(0, 0, s - 1, s + 1, 0, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = 'rgba(147,112,219,0.3)'; ctx.lineWidth = 1; ctx.beginPath(); ctx.ellipse(0, 0, s + 2, s + 4, 0, 0, Math.PI * 2); ctx.stroke(); ctx.strokeStyle = 'rgba(147,112,219,0.15)'; ctx.beginPath(); ctx.ellipse(0, 0, s + 5, s + 7, 0, 0, Math.PI * 2); ctx.stroke(); ctx.fillStyle = 'rgba(200,180,255,0.2)'; ctx.beginPath(); ctx.ellipse(0, -2, s * 0.5, s * 0.6, 0, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = tc; ctx.beginPath(); ctx.arc(0, 0, 5, Math.PI * 0.2, Math.PI * 1.8); ctx.closePath(); ctx.fill(); ctx.fillStyle = tc; ctx.fillRect(-1.5, -s - 10, 3, s); ctx.fillStyle = 'rgba(147,112,219,0.8)'; ctx.beginPath(); ctx.arc(0, -s - 11, 2.5, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = 'rgba(255,255,255,0.4)'; ctx.beginPath(); ctx.arc(0, -s - 11, 1, 0, Math.PI * 2); ctx.fill(); ctx.restore(); } // ======== JUNGLE ======== function _tankJungle(ctx, bc, tc, kc /* , t */) { const s = 13; ctx.fillStyle = kc; _roundRect(ctx, -s - 6, -s, 6, s * 2, 2); ctx.fill(); _roundRect(ctx, s, -s, 6, s * 2, 2); ctx.fill(); ctx.strokeStyle = 'rgba(0,100,0,0.4)'; ctx.lineWidth = 1; for (let ty = -s + 2; ty < s - 2; ty += 4) { ctx.beginPath(); ctx.moveTo(-s - 6, ty); ctx.lineTo(-s - 3, ty + 2); ctx.lineTo(-s - 6, ty + 4); ctx.stroke(); ctx.beginPath(); ctx.moveTo(s + 6, ty); ctx.lineTo(s + 3, ty + 2); ctx.lineTo(s + 6, ty + 4); ctx.stroke(); } ctx.fillStyle = bc; _roundRect(ctx, -s, -s, s * 2, s * 2, 2); ctx.fill(); ctx.fillStyle = 'rgba(0,80,0,0.35)'; ctx.beginPath(); ctx.arc(-4, -4, 5, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = 'rgba(34,60,20,0.3)'; ctx.beginPath(); ctx.arc(5, 3, 4, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = 'rgba(0,50,0,0.25)'; ctx.beginPath(); ctx.arc(-2, 6, 3.5, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = 'rgba(0,100,0,0.4)'; ctx.lineWidth = 0.8; ctx.beginPath(); ctx.moveTo(6, -8); ctx.quadraticCurveTo(10, -6, 7, -3); ctx.stroke(); ctx.beginPath(); ctx.moveTo(8, -7); ctx.quadraticCurveTo(11, -8, 9, -4); ctx.stroke(); ctx.fillStyle = tc; _roundRect(ctx, -5, -5, 10, 10, 3); ctx.fill(); ctx.fillStyle = 'rgba(0,50,0,0.3)'; ctx.beginPath(); ctx.arc(0, 0, 3, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = tc; _roundRect(ctx, -3, -s - 7, 6, s - 3, 2); ctx.fill(); ctx.fillStyle = kc; ctx.fillRect(-4, -s - 7, 8, 2); } // ======== NEON ======== function _tankNeon(ctx, bc, tc, kc, t) { const s = 12; const tt = t || 0; const glow = 0.6 + Math.sin(tt * 4) * 0.4; ctx.save(); ctx.shadowColor = bc; ctx.shadowBlur = 8 * glow; ctx.strokeStyle = kc; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(-s - 3, -s); ctx.lineTo(-s - 3, s); ctx.stroke(); ctx.beginPath(); ctx.moveTo(s + 3, -s); ctx.lineTo(s + 3, s); ctx.stroke(); ctx.fillStyle = kc; ctx.beginPath(); ctx.arc(-s - 3, -s, 2, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(-s - 3, s, 2, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(s + 3, -s, 2, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(s + 3, s, 2, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = bc; ctx.lineWidth = 1.5; ctx.strokeRect(-s, -s, s * 2, s * 2); ctx.lineWidth = 0.8; ctx.beginPath(); ctx.moveTo(-s, -s); ctx.lineTo(s, s); ctx.stroke(); ctx.beginPath(); ctx.moveTo(s, -s); ctx.lineTo(-s, s); ctx.stroke(); ctx.fillStyle = 'rgba(255,20,147,0.08)'; ctx.fillRect(-s, -s, s * 2, s * 2); ctx.strokeStyle = bc; ctx.lineWidth = 0.6; ctx.beginPath(); ctx.moveTo(-s, 0); ctx.lineTo(-3, 0); ctx.lineTo(-3, -5); ctx.stroke(); ctx.beginPath(); ctx.moveTo(s, 0); ctx.lineTo(3, 0); ctx.lineTo(3, 5); ctx.stroke(); ctx.strokeStyle = tc; ctx.lineWidth = 1.5; ctx.beginPath(); ctx.arc(0, 0, 5, 0, Math.PI * 2); ctx.stroke(); ctx.fillStyle = bc; ctx.beginPath(); ctx.arc(0, 0, 2, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = tc; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(0, -5); ctx.lineTo(0, -s - 10); ctx.stroke(); ctx.fillStyle = bc; ctx.beginPath(); ctx.arc(0, -s - 2, 1.5, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(0, -s - 6, 1.5, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(0, -s - 10, 2.5, 0, Math.PI * 2); ctx.fill(); ctx.restore(); } // ======== NEBULA ======== function _tankNebula(ctx, bc, tc, kc, t) { const s = 12; const tt = t || 0; const pulse = 0.7 + Math.sin(tt * 3) * 0.3; ctx.fillStyle = kc; _roundRect(ctx, -s - 4, -s + 1, 4, s * 2 - 2, 2); ctx.fill(); _roundRect(ctx, s, -s + 1, 4, s * 2 - 2, 2); ctx.fill(); ctx.strokeStyle = 'rgba(255,0,255,0.45)'; ctx.lineWidth = 0.8; ctx.beginPath(); ctx.moveTo(-s - 4, -s + 1); ctx.lineTo(-s - 4, s - 1); ctx.stroke(); ctx.beginPath(); ctx.moveTo(s + 4, -s + 1); ctx.lineTo(s + 4, s - 1); ctx.stroke(); ctx.fillStyle = bc; _roundRect(ctx, -s, -s, s * 2, s * 2, 5); ctx.fill(); ctx.fillStyle = 'rgba(180,0,255,0.15)'; ctx.beginPath(); ctx.moveTo(-s, -s); ctx.lineTo(s, -s); ctx.lineTo(-s, s); ctx.closePath(); ctx.fill(); ctx.save(); const stars = [ { x: -7, y: -8, size: 1.2, phase: 0 }, { x: 5, y: -6, size: 0.9, phase: 1.2 }, { x: -4, y: 3, size: 1.0, phase: 2.4 }, { x: 8, y: 5, size: 0.8, phase: 3.6 }, { x: -2, y: -3, size: 1.1, phase: 4.8 }, { x: 6, y: -1, size: 0.7, phase: 0.8 }, { x: -8, y: 6, size: 0.9, phase: 2.0 }, { x: 3, y: 7, size: 1.0, phase: 3.2 }, ]; for (const star of stars) { const twinkle = 0.4 + Math.sin(tt * 5 + star.phase) * 0.6; ctx.globalAlpha = Math.max(0, twinkle); ctx.fillStyle = '#FFFFFF'; ctx.beginPath(); ctx.arc(star.x, star.y, star.size, 0, Math.PI * 2); ctx.fill(); if (star.size > 0.9 && twinkle > 0.7) { ctx.strokeStyle = 'rgba(255,255,255,0.5)'; ctx.lineWidth = 0.4; ctx.beginPath(); ctx.moveTo(star.x - 2, star.y); ctx.lineTo(star.x + 2, star.y); ctx.stroke(); ctx.beginPath(); ctx.moveTo(star.x, star.y - 2); ctx.lineTo(star.x, star.y + 2); ctx.stroke(); } } ctx.restore(); ctx.save(); ctx.globalAlpha = 0.2; ctx.strokeStyle = '#FF00FF'; ctx.lineWidth = 1; ctx.beginPath(); for (let a = 0; a < Math.PI * 4; a += 0.15) { const r = 2 + a * 1.2; const sx = Math.cos(a + tt * 0.5) * r; const sy = Math.sin(a + tt * 0.5) * r; if (a === 0) ctx.moveTo(sx, sy); else ctx.lineTo(sx, sy); if (r > s - 2) break; } ctx.stroke(); ctx.restore(); ctx.save(); ctx.shadowColor = '#FF00FF'; ctx.shadowBlur = 6 * pulse; ctx.fillStyle = tc; ctx.beginPath(); ctx.arc(0, 0, 5.5, 0, Math.PI * 2); ctx.fill(); ctx.restore(); ctx.strokeStyle = 'rgba(255,255,255,0.3)'; ctx.lineWidth = 0.8; ctx.beginPath(); ctx.arc(0, 0, 3.5, 0, Math.PI * 2); ctx.stroke(); ctx.fillStyle = `rgba(255,200,255,${0.5 + pulse * 0.3})`; ctx.beginPath(); ctx.arc(0, 0, 2, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = 'rgba(255,255,255,0.7)'; ctx.beginPath(); ctx.arc(0, 0, 0.8, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = tc; ctx.beginPath(); ctx.moveTo(-3, -5); ctx.lineTo(3, -5); ctx.lineTo(2, -s - 9); ctx.lineTo(-2, -s - 9); ctx.closePath(); ctx.fill(); ctx.strokeStyle = `rgba(255,0,255,${pulse * 0.6})`; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(-3.5, -s); ctx.lineTo(3.5, -s); ctx.stroke(); ctx.beginPath(); ctx.moveTo(-3, -s - 4); ctx.lineTo(3, -s - 4); ctx.stroke(); ctx.save(); ctx.shadowColor = '#FF00FF'; ctx.shadowBlur = 8 * pulse; ctx.fillStyle = '#FF00FF'; ctx.beginPath(); ctx.arc(0, -s - 9, 2.8, 0, Math.PI * 2); ctx.fill(); ctx.restore(); ctx.fillStyle = 'rgba(255,255,255,0.8)'; ctx.beginPath(); ctx.arc(0, -s - 9, 1.2, 0, Math.PI * 2); ctx.fill(); } // ======== ROYAL ======== function _tankRoyal(ctx, bc, tc, kc /* , t */) { const s = 12; ctx.fillStyle = kc; _roundRect(ctx, -s - 5, -s + 1, 5, s * 2 - 2, 2); ctx.fill(); _roundRect(ctx, s, -s + 1, 5, s * 2 - 2, 2); ctx.fill(); ctx.strokeStyle = 'rgba(255,215,0,0.5)'; ctx.lineWidth = 0.8; ctx.beginPath(); ctx.moveTo(-s - 5, -s + 1); ctx.lineTo(-s - 5, s - 1); ctx.stroke(); ctx.beginPath(); ctx.moveTo(s + 5, -s + 1); ctx.lineTo(s + 5, s - 1); ctx.stroke(); ctx.fillStyle = bc; _roundRect(ctx, -s, -s, s * 2, s * 2, 5); ctx.fill(); ctx.fillStyle = tc; ctx.beginPath(); ctx.moveTo(-4, -5); ctx.lineTo(4, -5); ctx.lineTo(4, 1); ctx.quadraticCurveTo(4, 5, 0, 7); ctx.quadraticCurveTo(-4, 5, -4, 1); ctx.closePath(); ctx.fill(); ctx.fillStyle = 'rgba(255,215,0,0.6)'; ctx.beginPath(); ctx.moveTo(-2, -3); ctx.lineTo(2, -3); ctx.lineTo(2, 0); ctx.quadraticCurveTo(2, 3, 0, 4); ctx.quadraticCurveTo(-2, 3, -2, 0); ctx.closePath(); ctx.fill(); ctx.fillStyle = '#FFD700'; ctx.beginPath(); ctx.moveTo(-6, -s + 2); ctx.lineTo(-5, -s - 2); ctx.lineTo(-3, -s + 1); ctx.lineTo(0, -s - 4); ctx.lineTo(3, -s + 1); ctx.lineTo(5, -s - 2); ctx.lineTo(6, -s + 2); ctx.closePath(); ctx.fill(); ctx.fillStyle = '#FF0000'; ctx.beginPath(); ctx.arc(0, -s - 2, 1.2, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#0066FF'; ctx.beginPath(); ctx.arc(-4, -s, 0.8, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(4, -s, 0.8, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = tc; _roundRect(ctx, -2.5, -s - 10, 5, s - 4, 2); ctx.fill(); ctx.strokeStyle = '#FFD700'; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(-3, -s - 3); ctx.lineTo(3, -s - 3); ctx.stroke(); ctx.beginPath(); ctx.moveTo(-3, -s - 7); ctx.lineTo(3, -s - 7); ctx.stroke(); ctx.fillStyle = '#FFD700'; ctx.beginPath(); ctx.arc(0, -s - 10, 2.5, 0, Math.PI * 2); ctx.fill(); } // ======== SAKURA ======== function _tankSakura(ctx, bc, tc, kc, t) { const s = 12; const tt = t || 0; ctx.fillStyle = kc; _roundRect(ctx, -s - 4, -s + 1, 4, s * 2 - 2, 2); ctx.fill(); _roundRect(ctx, s, -s + 1, 4, s * 2 - 2, 2); ctx.fill(); ctx.strokeStyle = 'rgba(255,105,180,0.4)'; ctx.lineWidth = 0.7; ctx.beginPath(); ctx.moveTo(-s - 4, -s + 1); ctx.lineTo(-s - 4, s - 1); ctx.stroke(); ctx.beginPath(); ctx.moveTo(s + 4, -s + 1); ctx.lineTo(s + 4, s - 1); ctx.stroke(); ctx.fillStyle = bc; _roundRect(ctx, -s, -s, s * 2, s * 2, 6); ctx.fill(); ctx.fillStyle = 'rgba(255,255,255,0.18)'; ctx.beginPath(); ctx.moveTo(-s + 2, -s); ctx.lineTo(s - 2, -s); ctx.lineTo(-s + 2, -s + 8); ctx.closePath(); ctx.fill(); ctx.save(); ctx.globalAlpha = 0.6; const petals = [ { ox: -6, oy: -7, phase: 0 }, { ox: 7, oy: -4, phase: 1.5 }, { ox: -3, oy: 6, phase: 3 }, { ox: 5, oy: 5, phase: 4.5 }, ]; for (const p of petals) { const drift = Math.sin(tt * 2 + p.phase) * 1.5; const px = p.ox + drift; const py = p.oy; ctx.fillStyle = '#FF69B4'; ctx.beginPath(); for (let i = 0; i < 5; i++) { const angle = (i / 5) * Math.PI * 2 - Math.PI / 2; const pr = 2.2; const fx = px + Math.cos(angle) * pr; const fy = py + Math.sin(angle) * pr; ctx.moveTo(fx, fy); ctx.arc(fx, fy, 1.2, 0, Math.PI * 2); } ctx.fill(); ctx.fillStyle = '#FFE4E1'; ctx.beginPath(); ctx.arc(px, py, 0.8, 0, Math.PI * 2); ctx.fill(); } ctx.restore(); ctx.fillStyle = tc; ctx.beginPath(); ctx.arc(0, 0, 5.5, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = 'rgba(255,255,255,0.4)'; ctx.lineWidth = 0.8; ctx.beginPath(); ctx.arc(0, 0, 3.5, 0, Math.PI * 2); ctx.stroke(); ctx.fillStyle = '#FFE4E1'; ctx.beginPath(); ctx.arc(0, 0, 1.5, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = tc; ctx.beginPath(); ctx.moveTo(-3, -5); ctx.lineTo(3, -5); ctx.lineTo(2, -s - 9); ctx.lineTo(-2, -s - 9); ctx.closePath(); ctx.fill(); ctx.fillStyle = 'rgba(255,183,197,0.7)'; ctx.beginPath(); ctx.arc(0, -s - 9, 2.5, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = 'rgba(255,255,255,0.5)'; ctx.beginPath(); ctx.arc(0, -s - 9, 1.2, 0, Math.PI * 2); ctx.fill(); } // ======== THUNDER ======== function _tankThunder(ctx, bc, tc, kc, t) { const s = 12; const tt = t || 0; const flash = 0.7 + Math.sin(tt * 8) * 0.3; ctx.fillStyle = kc; _roundRect(ctx, -s - 5, -s, 5, s * 2, 2); ctx.fill(); _roundRect(ctx, s, -s, 5, s * 2, 2); ctx.fill(); ctx.save(); ctx.globalAlpha = flash * 0.6; ctx.strokeStyle = '#00BFFF'; ctx.lineWidth = 0.8; for (let ty = -s + 2; ty < s - 2; ty += 5) { ctx.beginPath(); ctx.moveTo(-s - 5, ty); ctx.lineTo(-s - 3, ty + 1.5); ctx.lineTo(-s - 5, ty + 3); ctx.stroke(); ctx.beginPath(); ctx.moveTo(s + 5, ty); ctx.lineTo(s + 3, ty + 1.5); ctx.lineTo(s + 5, ty + 3); ctx.stroke(); } ctx.restore(); ctx.fillStyle = bc; ctx.beginPath(); ctx.moveTo(-s + 1, -s - 2); ctx.lineTo(s - 1, -s - 2); ctx.lineTo(s + 2, -s + 3); ctx.lineTo(s + 2, s - 3); ctx.lineTo(s - 1, s + 2); ctx.lineTo(-s + 1, s + 2); ctx.lineTo(-s - 2, s - 3); ctx.lineTo(-s - 2, -s + 3); ctx.closePath(); ctx.fill(); ctx.fillStyle = 'rgba(0,191,255,0.5)'; ctx.beginPath(); ctx.moveTo(-1, -7); ctx.lineTo(3, -7); ctx.lineTo(0, -1); ctx.lineTo(4, -1); ctx.lineTo(-2, 8); ctx.lineTo(0, 2); ctx.lineTo(-3, 2); ctx.closePath(); ctx.fill(); ctx.save(); ctx.shadowColor = '#00BFFF'; ctx.shadowBlur = 6 * flash; ctx.strokeStyle = 'rgba(0,191,255,0.35)'; ctx.lineWidth = 1.5; ctx.beginPath(); ctx.moveTo(-s + 1, -s - 2); ctx.lineTo(s - 1, -s - 2); ctx.lineTo(s + 2, -s + 3); ctx.lineTo(s + 2, s - 3); ctx.lineTo(s - 1, s + 2); ctx.lineTo(-s + 1, s + 2); ctx.lineTo(-s - 2, s - 3); ctx.lineTo(-s - 2, -s + 3); ctx.closePath(); ctx.stroke(); ctx.fillStyle = tc; ctx.beginPath(); for (let i = 0; i < 6; i++) { const angle = (i / 6) * Math.PI * 2 - Math.PI / 6; const hx = Math.cos(angle) * 5.5; const hy = Math.sin(angle) * 5.5; if (i === 0) ctx.moveTo(hx, hy); else ctx.lineTo(hx, hy); } ctx.closePath(); ctx.fill(); ctx.fillStyle = `rgba(0,191,255,${0.4 + flash * 0.3})`; ctx.beginPath(); ctx.arc(0, 0, 3, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = `rgba(255,255,255,${0.5 + flash * 0.3})`; ctx.beginPath(); ctx.arc(0, 0, 1.5, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = tc; _roundRect(ctx, -2.5, -s - 10, 5, s - 3, 2); ctx.fill(); ctx.strokeStyle = `rgba(0,191,255,${flash * 0.8})`; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(-2.5, -s); ctx.lineTo(-5, -s - 3); ctx.lineTo(-2.5, -s - 5); ctx.lineTo(-5, -s - 8); ctx.stroke(); ctx.beginPath(); ctx.moveTo(2.5, -s - 1); ctx.lineTo(5, -s - 4); ctx.lineTo(2.5, -s - 6); ctx.lineTo(5, -s - 9); ctx.stroke(); ctx.fillStyle = '#00BFFF'; ctx.beginPath(); ctx.arc(0, -s - 10, 3, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = 'rgba(255,255,255,0.8)'; ctx.beginPath(); ctx.arc(0, -s - 10, 1.5, 0, Math.PI * 2); ctx.fill(); ctx.restore(); } // ======== DIAMOND 💎 ======== // Full crystalline tank — all-diamond body, gem-encrusted tracks, // hexagonal faceted turret, jewel barrel. Stays within the standard // [−s, +s] design footprint so it matches the size of every other skin. // Reference: clean icy-blue gem tank, faceted triangular surfaces, // sapphire track wheels, centered diamond rivet. function _tankDiamond(ctx, bc, tc, kc, t) { const s = 12; const tt = t || 0; const breathe = 0.5 + Math.sin(tt * 0.5) * 0.5; // very slow breathing const shimmer = 0.5 + Math.sin(tt * 0.7) * 0.5; // slow shimmer // Ice-blue palette (reference image): // light = crystal highlight, mid = surface, deep = shadow / outline const light = '#EAF6FF'; const midA = '#BCDFFF'; const midB = '#7DC4FF'; const deep = '#1E5B9E'; const edge = '#7DF9FF'; // ── 0. Very faint outer aura (doesn't enlarge footprint) ── ctx.save(); ctx.globalAlpha = 0.10 + breathe * 0.06; ctx.fillStyle = edge; ctx.beginPath(); ctx.arc(0, 0, s + 2, 0, Math.PI * 2); ctx.fill(); ctx.restore(); // ═══════════════════════════════════════════════════════════ // TRACKS (left & right) — sapphire-bead chains with hub wheels // ═══════════════════════════════════════════════════════════ const tW = 4; // track half-width const tx = -s - tW * 0.5; // left track center x const rx = s + tW * 0.5; // right track center x const trackTop = -s + 1; const trackBot = s - 1; // Track outer shell (dark steel) ctx.fillStyle = '#0E3562'; ctx.fillRect(tx - tW, trackTop - 0.5, tW * 2, trackBot - trackTop + 1); ctx.fillRect(rx - tW, trackTop - 0.5, tW * 2, trackBot - trackTop + 1); // Track top/bottom rounded caps ctx.beginPath(); ctx.arc(tx, trackTop, tW, Math.PI, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(tx, trackBot, tW, 0, Math.PI); ctx.fill(); ctx.beginPath(); ctx.arc(rx, trackTop, tW, Math.PI, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(rx, trackBot, tW, 0, Math.PI); ctx.fill(); // Gem beads along each track (small blue sapphires) const beadCount = 5; for (let i = 0; i < beadCount; i++) { const by = trackTop + 3 + (trackBot - trackTop - 6) * (i / (beadCount - 1)); for (const cx of [tx, rx]) { // Bead body (rhombus-ish) ctx.fillStyle = midB; ctx.beginPath(); ctx.moveTo(cx, by - 1.6); ctx.lineTo(cx + 2.2, by); ctx.lineTo(cx, by + 1.6); ctx.lineTo(cx - 2.2, by); ctx.closePath(); ctx.fill(); // Highlight ctx.fillStyle = light; ctx.beginPath(); ctx.moveTo(cx, by - 1.6); ctx.lineTo(cx + 0.8, by - 0.4); ctx.lineTo(cx - 0.8, by - 0.4); ctx.closePath(); ctx.fill(); // Dark edge bottom ctx.fillStyle = deep; ctx.beginPath(); ctx.moveTo(cx + 2.2, by); ctx.lineTo(cx, by + 1.6); ctx.lineTo(cx + 0.4, by + 0.2); ctx.closePath(); ctx.fill(); } } // Hub wheels (big sapphire wheels at each end) const hubR = 2.6; for (const cx of [tx, rx]) { for (const hy of [trackTop, trackBot]) { // Outer ring ctx.fillStyle = deep; ctx.beginPath(); ctx.arc(cx, hy, hubR + 0.6, 0, Math.PI * 2); ctx.fill(); // Gem core ctx.fillStyle = midB; ctx.beginPath(); ctx.arc(cx, hy, hubR, 0, Math.PI * 2); ctx.fill(); // Star-cut facets (4-point) ctx.strokeStyle = 'rgba(255,255,255,0.65)'; ctx.lineWidth = 0.5; ctx.beginPath(); ctx.moveTo(cx - hubR, hy); ctx.lineTo(cx + hubR, hy); ctx.stroke(); ctx.beginPath(); ctx.moveTo(cx, hy - hubR); ctx.lineTo(cx, hy + hubR); ctx.stroke(); // Inner highlight ctx.fillStyle = light; ctx.beginPath(); ctx.arc(cx - 0.6, hy - 0.6, 0.8, 0, Math.PI * 2); ctx.fill(); } } // ═══════════════════════════════════════════════════════════ // BODY — teardrop/bullet silhouette, faceted diamond surface // Width stays within ±s; height uses the full ±s footprint. // ═══════════════════════════════════════════════════════════ // Main body outline (rear wider, front slightly narrower — classic tank nose) const body = [ [-s + 2, -s + 1], // front-left [ s - 2, -s + 1], // front-right [ s, -s + 4], [ s, s - 4], [ s - 2, s - 1], [-s + 2, s - 1], [-s, s - 4], [-s, -s + 4], ]; // Base fill — soft ice-blue gradient approximation (radial-ish) ctx.fillStyle = midA; ctx.beginPath(); ctx.moveTo(body[0][0], body[0][1]); for (let i = 1; i < body.length; i++) ctx.lineTo(body[i][0], body[i][1]); ctx.closePath(); ctx.fill(); // Faceted triangles — all emanating from a single "highlight point" so the // crystal looks like it has a single light source, mimicking the reference. const hp = [-s + 4, -s + 3]; // highlight point (upper-left region) const facetRim = [ [-s + 2, -s + 1], [ s - 2, -s + 1], [ s, -s + 4], [ s, s - 4], [ s - 2, s - 1], [-s + 2, s - 1], [-s, s - 4], [-s, -s + 4], ]; // Triangles from highlight point to each rim edge for (let i = 0; i < facetRim.length; i++) { const a = facetRim[i]; const b = facetRim[(i + 1) % facetRim.length]; // Shade based on angle away from light point const mx = (a[0] + b[0]) * 0.5; const my = (a[1] + b[1]) * 0.5; const dx = mx - hp[0]; const dy = my - hp[1]; const dist = Math.sqrt(dx * dx + dy * dy); // Nearer to hp → brighter, farther → deeper blue const tBright = Math.max(0, 1 - dist / 22); const colA = tBright > 0.6 ? light : tBright > 0.3 ? midA : midB; ctx.fillStyle = colA; ctx.globalAlpha = 0.55; ctx.beginPath(); ctx.moveTo(hp[0], hp[1]); ctx.lineTo(a[0], a[1]); ctx.lineTo(b[0], b[1]); ctx.closePath(); ctx.fill(); } ctx.globalAlpha = 1; // Secondary facet lines — subtle geometric net ctx.strokeStyle = 'rgba(255,255,255,0.35)'; ctx.lineWidth = 0.4; // From highlight point to every rim vertex for (const p of facetRim) { ctx.beginPath(); ctx.moveTo(hp[0], hp[1]); ctx.lineTo(p[0], p[1]); ctx.stroke(); } // Rim outline (crisp inner) ctx.strokeStyle = 'rgba(255,255,255,0.55)'; ctx.lineWidth = 0.6; ctx.beginPath(); ctx.moveTo(body[0][0], body[0][1]); for (let i = 1; i < body.length; i++) ctx.lineTo(body[i][0], body[i][1]); ctx.closePath(); ctx.stroke(); // Deep blue body outline (crisp edge) ctx.strokeStyle = deep; ctx.lineWidth = 0.8; ctx.beginPath(); ctx.moveTo(body[0][0], body[0][1]); for (let i = 1; i < body.length; i++) ctx.lineTo(body[i][0], body[i][1]); ctx.closePath(); ctx.stroke(); // ── Centered "diamond rivet" — the signature small gem on the hull ── ctx.save(); ctx.fillStyle = midB; ctx.beginPath(); ctx.moveTo(0, -2.8); ctx.lineTo(2.4, 0); ctx.lineTo(0, 2.8); ctx.lineTo(-2.4, 0); ctx.closePath(); ctx.fill(); // Rivet facet lines ctx.strokeStyle = 'rgba(255,255,255,0.7)'; ctx.lineWidth = 0.5; ctx.beginPath(); ctx.moveTo(0, -2.8); ctx.lineTo(0, 2.8); ctx.stroke(); ctx.beginPath(); ctx.moveTo(-2.4, 0); ctx.lineTo(2.4, 0); ctx.stroke(); // Rivet highlight ctx.fillStyle = light; ctx.beginPath(); ctx.moveTo(0, -2.8); ctx.lineTo(0.9, -1); ctx.lineTo(-0.9, -1); ctx.closePath(); ctx.fill(); // Rivet dark outline ctx.strokeStyle = deep; ctx.lineWidth = 0.5; ctx.beginPath(); ctx.moveTo(0, -2.8); ctx.lineTo(2.4, 0); ctx.lineTo(0, 2.8); ctx.lineTo(-2.4, 0); ctx.closePath(); ctx.stroke(); ctx.restore(); // ═══════════════════════════════════════════════════════════ // TURRET — hexagonal gem prism (top view) sitting on the hull // ═══════════════════════════════════════════════════════════ const turretR = 6.5; const turretRy = 5.5; // slightly squashed vertically (perspective feel) // Turret base ring (darker blue outline for contrast with body) ctx.fillStyle = deep; ctx.beginPath(); for (let i = 0; i < 6; i++) { const ang = (i / 6) * Math.PI * 2 - Math.PI / 2; const hx = Math.cos(ang) * (turretR + 0.6); const hy = Math.sin(ang) * (turretRy + 0.6) - 1; if (i === 0) ctx.moveTo(hx, hy); else ctx.lineTo(hx, hy); } ctx.closePath(); ctx.fill(); // Turret body (6-sided gem) const turretPts = []; for (let i = 0; i < 6; i++) { const ang = (i / 6) * Math.PI * 2 - Math.PI / 2; turretPts.push([Math.cos(ang) * turretR, Math.sin(ang) * turretRy - 1]); } ctx.fillStyle = midA; ctx.beginPath(); ctx.moveTo(turretPts[0][0], turretPts[0][1]); for (let i = 1; i < 6; i++) ctx.lineTo(turretPts[i][0], turretPts[i][1]); ctx.closePath(); ctx.fill(); // Turret facets from center const tHp = [-1.8, -2.5]; for (let i = 0; i < 6; i++) { const a = turretPts[i]; const b = turretPts[(i + 1) % 6]; const mx = (a[0] + b[0]) * 0.5; const my = (a[1] + b[1]) * 0.5; const d = Math.sqrt((mx - tHp[0]) ** 2 + (my - tHp[1]) ** 2); const tBright = Math.max(0, 1 - d / 10); ctx.fillStyle = tBright > 0.55 ? light : tBright > 0.25 ? midA : midB; ctx.globalAlpha = 0.7; ctx.beginPath(); ctx.moveTo(tHp[0], tHp[1]); ctx.lineTo(a[0], a[1]); ctx.lineTo(b[0], b[1]); ctx.closePath(); ctx.fill(); } ctx.globalAlpha = 1; // Turret facet lines ctx.strokeStyle = 'rgba(255,255,255,0.55)'; ctx.lineWidth = 0.5; for (const p of turretPts) { ctx.beginPath(); ctx.moveTo(tHp[0], tHp[1]); ctx.lineTo(p[0], p[1]); ctx.stroke(); } // Turret crisp outline ctx.strokeStyle = deep; ctx.lineWidth = 0.7; ctx.beginPath(); ctx.moveTo(turretPts[0][0], turretPts[0][1]); for (let i = 1; i < 6; i++) ctx.lineTo(turretPts[i][0], turretPts[i][1]); ctx.closePath(); ctx.stroke(); // Turret crown highlight band ctx.fillStyle = `rgba(255,255,255,${0.25 + breathe * 0.12})`; ctx.beginPath(); ctx.ellipse(-1, -3.8, 3.2, 1.3, -0.3, 0, Math.PI * 2); ctx.fill(); // Turret center (small cut-gem apex) ctx.fillStyle = midB; ctx.beginPath(); ctx.arc(tHp[0], tHp[1], 1.2, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#FFFFFF'; ctx.beginPath(); ctx.arc(tHp[0] - 0.3, tHp[1] - 0.3, 0.5, 0, Math.PI * 2); ctx.fill(); // ═══════════════════════════════════════════════════════════ // BARREL — hex-prism gem cannon with metal band + glowing muzzle // ═══════════════════════════════════════════════════════════ const barrelBase = -3; // at turret edge const barrelTip = -s - 4; // doesn't exceed footprint much const barrelHalfW = 2.2; // Barrel body — faceted ctx.fillStyle = midA; ctx.beginPath(); ctx.moveTo(-barrelHalfW, barrelBase); ctx.lineTo( barrelHalfW, barrelBase); ctx.lineTo( barrelHalfW * 0.8, barrelTip); ctx.lineTo(-barrelHalfW * 0.8, barrelTip); ctx.closePath(); ctx.fill(); // Barrel left/right facet shade ctx.fillStyle = midB; ctx.globalAlpha = 0.7; ctx.beginPath(); ctx.moveTo( barrelHalfW, barrelBase); ctx.lineTo( barrelHalfW * 0.8, barrelTip); ctx.lineTo( 0, barrelTip); ctx.lineTo( 0, barrelBase); ctx.closePath(); ctx.fill(); ctx.globalAlpha = 1; // Barrel center highlight stripe ctx.strokeStyle = 'rgba(255,255,255,0.6)'; ctx.lineWidth = 0.5; ctx.beginPath(); ctx.moveTo(-0.6, barrelBase); ctx.lineTo(-0.5, barrelTip); ctx.stroke(); // Barrel outline ctx.strokeStyle = deep; ctx.lineWidth = 0.7; ctx.beginPath(); ctx.moveTo(-barrelHalfW, barrelBase); ctx.lineTo( barrelHalfW, barrelBase); ctx.lineTo( barrelHalfW * 0.8, barrelTip); ctx.lineTo(-barrelHalfW * 0.8, barrelTip); ctx.closePath(); ctx.stroke(); // Metal collar near turret (dark band) ctx.fillStyle = deep; ctx.fillRect(-barrelHalfW - 0.4, barrelBase - 0.4, (barrelHalfW + 0.4) * 2, 1.4); ctx.fillStyle = '#3E88C8'; ctx.fillRect(-barrelHalfW - 0.4, barrelBase - 0.1, (barrelHalfW + 0.4) * 2, 0.4); // Muzzle ring ctx.fillStyle = deep; ctx.fillRect(-barrelHalfW * 0.9, barrelTip - 0.8, barrelHalfW * 1.8, 1.2); // Glowing gem muzzle ctx.save(); ctx.shadowColor = edge; ctx.shadowBlur = 5 + breathe * 3; ctx.fillStyle = light; ctx.beginPath(); ctx.arc(0, barrelTip - 0.4, 1.4, 0, Math.PI * 2); ctx.fill(); ctx.restore(); ctx.fillStyle = '#FFFFFF'; ctx.beginPath(); ctx.arc(0, barrelTip - 0.4, 0.7, 0, Math.PI * 2); ctx.fill(); // ═══════════════════════════════════════════════════════════ // SPARKLES — 4 sparse 4-point stars, very slow twinkle // ═══════════════════════════════════════════════════════════ ctx.save(); const flares = [ { x: -s + 3, y: -s + 2, phase: 0, len: 2.2 }, { x: s - 4, y: -4, phase: 2.0, len: 1.8 }, { x: -4, y: s - 3, phase: 4.0, len: 2.0 }, { x: s - 2, y: s - 6, phase: 6.0, len: 1.6 }, ]; for (const fl of flares) { const tw = Math.max(0, Math.sin(tt * 0.6 + fl.phase)); if (tw > 0.65) { const a = (tw - 0.65) * 3; const col = `rgba(255,255,255,${Math.min(1, a)})`; ctx.strokeStyle = col; ctx.fillStyle = col; ctx.lineWidth = 0.5; ctx.beginPath(); ctx.moveTo(fl.x - fl.len, fl.y); ctx.lineTo(fl.x + fl.len, fl.y); ctx.stroke(); ctx.beginPath(); ctx.moveTo(fl.x, fl.y - fl.len); ctx.lineTo(fl.x, fl.y + fl.len); ctx.stroke(); ctx.beginPath(); ctx.arc(fl.x, fl.y, 0.6, 0, Math.PI * 2); ctx.fill(); } } ctx.restore(); // ── Very subtle shimmer band sliding across body (crystal gleam) ── ctx.save(); ctx.globalAlpha = 0.12 * shimmer; ctx.fillStyle = '#FFFFFF'; const gleamY = -s + (s * 2) * ((tt * 0.25) % 1); ctx.beginPath(); ctx.moveTo(-s + 2, gleamY); ctx.lineTo( s - 2, gleamY - 3); ctx.lineTo( s - 2, gleamY - 1); ctx.lineTo(-s + 2, gleamY + 2); ctx.closePath(); ctx.fill(); ctx.restore(); } // --------------------------------------------------------------- // Dispatcher — the ONE function every caller should use. // @param {CanvasRenderingContext2D} ctx — already translated+rotated to tank // @param {string} skinId — 'default', 'arctic', ... 'diamond' // @param {object|null} colors — { body, turret, track } (null = default) // @param {number} t — animation timer in seconds // --------------------------------------------------------------- const FALLBACK_COLORS = { body: '#FFD700', turret: '#B8860B', track: '#8B6914' }; function drawTankSkin(ctx, skinId, colors, t) { const c = colors || FALLBACK_COLORS; const bc = c.body || FALLBACK_COLORS.body; const tc = c.turret || FALLBACK_COLORS.turret; const kc = c.track || FALLBACK_COLORS.track; const tt = t || 0; switch (skinId) { case 'arctic': return _tankArctic(ctx, bc, tc, kc, tt); case 'inferno': return _tankInferno(ctx, bc, tc, kc, tt); case 'phantom': return _tankPhantom(ctx, bc, tc, kc, tt); case 'jungle': return _tankJungle(ctx, bc, tc, kc, tt); case 'neon': return _tankNeon(ctx, bc, tc, kc, tt); case 'nebula': return _tankNebula(ctx, bc, tc, kc, tt); case 'royal': return _tankRoyal(ctx, bc, tc, kc, tt); case 'sakura': return _tankSakura(ctx, bc, tc, kc, tt); case 'thunder': return _tankThunder(ctx, bc, tc, kc, tt); case 'diamond': return _tankDiamond(ctx, bc, tc, kc, tt); case 'default': default: return _tankDefault(ctx, bc, tc, kc, tt); } } module.exports = { drawTankSkin, DESIGN_HALF_SIZE, };