'use strict'; // === BOSS DRAWING === function renderBoss(boss) { if (!boss) return; const px = boss.x * TILE; const py = boss.y * TILE; const isHit = boss.hitTimer > 0; ctx.save(); if (isHit) { ctx.globalAlpha = 0.5 + Math.sin(Date.now() * 0.02) * 0.5; } // Boss is drawn larger (2x2 tiles) const def = BOSS_TYPES.find(b => b.id === boss.id || b.name === boss.name); if (def && def.drawFunction && typeof window[def.drawFunction] === 'function') { window[def.drawFunction](ctx, px, py, boss, isHit); } else { drawGenericBoss(ctx, px, py, boss, isHit); } // Boss HP bar (wider, at top of screen) const barW = 200; const barH = 8; const barX = (CANVAS_W - barW) / 2; const barY = 4; const hpRatio = boss.hp / boss.maxHp; ctx.fillStyle = '#333333'; ctx.fillRect(barX, barY, barW, barH); ctx.fillStyle = hpRatio > 0.5 ? '#00FF00' : hpRatio > 0.25 ? '#FFAA00' : '#FF0000'; ctx.fillRect(barX, barY, barW * hpRatio, barH); ctx.strokeStyle = '#555'; ctx.lineWidth = 1; ctx.strokeRect(barX, barY, barW, barH); // Boss name ctx.fillStyle = '#FFD700'; ctx.font = 'bold 10px monospace'; ctx.textAlign = 'center'; ctx.fillText(boss.name, CANVAS_W / 2, barY + barH + 10); ctx.textAlign = 'left'; // Phase indicator if (boss.phases && boss.phase > 0) { ctx.fillStyle = '#FF4444'; ctx.font = '8px monospace'; ctx.textAlign = 'center'; ctx.fillText(`Phase ${boss.phase + 1}`, CANVAS_W / 2, barY + barH + 20); ctx.textAlign = 'left'; } ctx.restore(); } function drawGenericBoss(ctx, px, py, boss, isHit) { const size = TILE * 2; const color = isHit ? '#FFFFFF' : (boss.color || '#8B0000'); // Large body ctx.fillStyle = color; ctx.beginPath(); ctx.roundRect(px - TILE / 2, py - TILE / 2, size, size, 6); ctx.fill(); // Details ctx.fillStyle = boss.colorLight || '#CC2222'; ctx.beginPath(); ctx.roundRect(px - TILE / 2 + 4, py - TILE / 2 + 4, size - 8, size * 0.4, 4); ctx.fill(); // Eyes ctx.fillStyle = boss.eyeColor || '#FFD700'; ctx.beginPath(); ctx.arc(px - 6, py - 4, 4, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(px + 6, py - 4, 4, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#000'; ctx.beginPath(); ctx.arc(px - 6, py - 4, 2, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(px + 6, py - 4, 2, 0, Math.PI * 2); ctx.fill(); } // Specific boss draw functions function drawGleeok(ctx, px, py, boss, isHit) { const size = TILE * 2; const color = isHit ? '#FFFFFF' : (boss.color || '#2F4F4F'); const heads = boss.heads || 3; const aliveHeads = boss.aliveHeads || heads; // Body ctx.fillStyle = color; ctx.beginPath(); ctx.ellipse(px, py, size * 0.45, size * 0.35, 0, 0, Math.PI * 2); ctx.fill(); // Scales pattern ctx.fillStyle = boss.colorLight || '#3F5F5F'; for (let i = 0; i < 6; i++) { const angle = (i / 6) * Math.PI * 2; ctx.beginPath(); ctx.ellipse(px + Math.cos(angle) * 12, py + Math.sin(angle) * 8, 4, 3, angle, 0, Math.PI * 2); ctx.fill(); } // Heads for (let i = 0; i < heads; i++) { const angle = (i / heads) * Math.PI * 2 - Math.PI / 2; const hx = px + Math.cos(angle) * 24; const hy = py + Math.sin(angle) * 18 - 8; const alive = i < aliveHeads; if (!alive) { ctx.fillStyle = '#333'; ctx.beginPath(); ctx.arc(hx, hy, 5, 0, Math.PI * 2); ctx.fill(); continue; } const headBob = Math.sin(Date.now() * 0.005 + i) * 2; ctx.fillStyle = boss.headColor || '#006400'; ctx.beginPath(); ctx.ellipse(hx, hy + headBob, 6, 5, 0, 0, Math.PI * 2); ctx.fill(); // Eyes ctx.fillStyle = '#FFD700'; ctx.beginPath(); ctx.arc(hx - 2, hy + headBob, 1.5, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(hx + 2, hy + headBob, 1.5, 0, Math.PI * 2); ctx.fill(); // Fire breath if (boss.attacking && i < aliveHeads) { ctx.fillStyle = '#FF4500'; const dirX = Math.cos(angle); const dirY = Math.sin(angle); ctx.beginPath(); ctx.moveTo(hx + dirX * 6, hy + headBob + dirY * 6); ctx.lineTo(hx + dirX * 14, hy + headBob + dirY * 14); ctx.lineTo(hx + dirX * 6 + dirY * 3, hy + headBob + dirY * 6 - dirX * 3); ctx.closePath(); ctx.fill(); } } } function drawDarknut(ctx, px, py, boss, isHit) { const size = TILE * 2; const color = isHit ? '#FFFFFF' : (boss.color || '#708090'); const armorBroken = boss.armorBroken || 0; // Armor body ctx.fillStyle = color; ctx.fillRect(px - 10, py - 12, 20, 28); // Helmet ctx.fillStyle = isHit ? '#FFFFFF' : '#606060'; ctx.beginPath(); ctx.arc(px, py - 8, 8, 0, Math.PI * 2); ctx.fill(); // Visor ctx.fillStyle = boss.eyeColor || '#FF0000'; ctx.fillRect(px - 5, py - 10, 10, 3); // Armor plates (break with phases) ctx.fillStyle = isHit ? '#FFFFFF' : '#808080'; if (armorBroken < 1) { ctx.fillRect(px - 12, py - 4, 24, 6); // Chest plate } if (armorBroken < 2) { ctx.fillRect(px - 12, py + 8, 24, 6); // Waist plate } // Weapon (two-handed) ctx.fillStyle = '#C0C0C0'; ctx.fillRect(px + 12, py - 16, 3, 32); ctx.fillStyle = '#8B4513'; ctx.fillRect(px + 10, py + 6, 7, 4); // Speed increases with armor broken if (armorBroken > 0) { ctx.fillStyle = 'rgba(255,0,0,0.2)'; ctx.fillRect(px - 14, py - 14, 28, 32); } } function drawWizzrobe(ctx, px, py, boss, isHit) { const robeColor = isHit ? '#FFFFFF' : (boss.color || '#4B0082'); const teleporting = boss.teleporting || false; if (teleporting) { ctx.globalAlpha = 0.3 + Math.sin(Date.now() * 0.05) * 0.3; } // Robe ctx.fillStyle = robeColor; ctx.beginPath(); ctx.moveTo(px, py - 14); ctx.lineTo(px - 10, py + 16); ctx.lineTo(px + 10, py + 16); ctx.closePath(); ctx.fill(); // Hood ctx.beginPath(); ctx.arc(px, py - 10, 8, 0, Math.PI * 2); ctx.fill(); // Face opening ctx.fillStyle = '#FFD700'; ctx.beginPath(); ctx.arc(px, py - 10, 4, 0, Math.PI * 2); ctx.fill(); // Eyes ctx.fillStyle = '#FF00FF'; ctx.beginPath(); ctx.arc(px - 2, py - 11, 1.5, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(px + 2, py - 11, 1.5, 0, Math.PI * 2); ctx.fill(); // Magic orb if (boss.castSpell) { ctx.fillStyle = '#FF00FF'; ctx.globalAlpha = 0.7; ctx.beginPath(); ctx.arc(px + 14, py - 4, 4, 0, Math.PI * 2); ctx.fill(); ctx.globalAlpha = 1; } } // Register boss draw functions const BOSS_DRAW_FUNCTIONS = { gleeok: drawGleeok, darknut: drawDarknut, wizzrobe: drawWizzrobe, generic: drawGenericBoss, }; if (typeof module !== 'undefined' && module.exports) { module.exports = { renderBoss, drawGenericBoss, drawGleeok, drawDarknut, drawWizzrobe }; }