// Dr. Zomboss Multi-Phase Boss Fight & Zombot Mechanics window.BossSystem = class BossSystem { constructor(gameEngine) { this.engine = gameEngine; this.isActive = false; this.maxHealth = 6000; this.health = 6000; // Position & Visuals this.x = 670; this.y = 140; this.w = 210; this.h = 340; this.animTimer = 0; this.eyeColor = '#00e5ff'; // Attacks & Timers this.stompTimer = 8.0; this.ballTimer = 14.0; this.vanTimer = 22.0; this.summonTimer = 10.0; // Active boss projectiles (Ice balls, Fire balls, RV vans) this.hazards = []; // Stomp animation this.isStomping = false; this.stompTarget = null; this.stompAnimProgress = 0; } reset() { this.isActive = false; this.health = this.maxHealth; this.stompTimer = 8.0; this.ballTimer = 14.0; this.vanTimer = 22.0; this.summonTimer = 10.0; this.hazards = []; this.isStomping = false; this.stompTarget = null; } startFight() { this.isActive = true; this.health = this.maxHealth; this.hazards = []; this.stompTimer = 6.0; this.ballTimer = 12.0; this.vanTimer = 25.0; this.summonTimer = 8.0; } takeDamage(amount) { if (!this.isActive || this.health <= 0) return; this.health = Math.max(0, this.health - amount); if (this.health <= 0) { this.defeatBoss(); } } defeatBoss() { this.isActive = false; if (window.PvZAudio) { window.PvZAudio.playExplosion(); window.PvZAudio.playWin(); } // Huge reward if (this.engine && this.engine.economy) { this.engine.economy.addCoins(1000); } if (this.engine && this.engine.workshop) { this.engine.workshop.addMaterial('crystals', 5); this.engine.workshop.addMaterial('scrapMetal', 20); } if (this.engine && this.engine.leaderboards) { this.engine.leaderboards.submitScore('boss', 1, 'Побежден Зомбот!'); } // Explosion celebration for (let i = 0; i < 60; i++) { setTimeout(() => { if (this.engine && this.engine.combat) { const rx = this.x + Math.random() * this.w; const ry = this.y + Math.random() * this.h; this.engine.combat.createExplosionParticles(rx, ry); } }, i * 50); } setTimeout(() => { if (this.engine) { this.engine.triggerVictory(); } }, 2500); } update(dt, gameState) { if (!this.isActive) return; this.animTimer += dt; // Phase detection const hpRatio = this.health / this.maxHealth; const speedMult = hpRatio < 0.35 ? 1.5 : (hpRatio < 0.70 ? 1.25 : 1.0); this.eyeColor = hpRatio < 0.35 ? '#ff1744' : (hpRatio < 0.70 ? '#ff9800' : '#00e5ff'); // 1. Attack: Stomp (Phase 1+) this.stompTimer -= dt * speedMult; if (this.stompTimer <= 0) { this.stompTimer = 14.0; this.triggerStomp(gameState); } // 2. Attack: Ice Ball / Fire Ball (Phase 2+) if (hpRatio < 0.75) { this.ballTimer -= dt * speedMult; if (this.ballTimer <= 0) { this.ballTimer = 16.0; const ballType = Math.random() < 0.5 ? 'ice' : 'fire'; const row = Math.floor(Math.random() * gameState.grid.rows); this.spawnBallHazard(row, ballType, gameState); } } // 3. Attack: Van Throw (Phase 3 Enraged) if (hpRatio < 0.35) { this.vanTimer -= dt * speedMult; if (this.vanTimer <= 0) { this.vanTimer = 22.0; this.throwVan(gameState); } } // 4. Summons this.summonTimer -= dt * speedMult; if (this.summonTimer <= 0) { this.summonTimer = 12.0; const count = hpRatio < 0.35 ? 3 : 2; for (let i = 0; i < count; i++) { const types = ['conehead', 'buckethead', 'football', 'bungee', 'normal']; const t = types[Math.floor(Math.random() * types.length)]; const r = Math.floor(Math.random() * gameState.grid.rows); gameState.spawnZombie(r, t === 'bungee' ? 'buckethead' : t); // Bungee drop drops a durable zombie } } // 5. Update Hazards for (const h of this.hazards) { if (h.type === 'ice' || h.type === 'fire') { h.x -= h.speed * dt; // Crush plants in path for (const p of gameState.plants) { if (p.row === h.row && Math.abs(p.x - h.x) < 30 && !p.toRemove) { p.toRemove = true; gameState.grid.removePlant(p.col, p.row); gameState.combat.createSplash(p.x, p.y, h.type === 'ice' ? '#80deea' : '#ff5722'); } } // Jalapeno melts ice ball! if (h.type === 'ice') { for (const p of gameState.plants) { if (p.type === 'jalapeno' && p.row === h.row && Math.abs(p.x - h.x) < 50) { h.toRemove = true; gameState.combat.createExplosionParticles(h.x, h.y); } } } // Snowpea / Ice slows and extinguishes fireball if (h.type === 'fire') { for (const proj of gameState.projectiles) { if (proj.row === h.row && proj.slows && Math.abs(proj.x - h.x) < 30) { h.toRemove = true; proj.toRemove = true; gameState.combat.createSplash(h.x, h.y, '#80deea', 16); } } } if (h.x < 30) { h.toRemove = true; } } } this.hazards = this.hazards.filter(h => !h.toRemove); } triggerStomp(gameState) { const alivePlants = gameState.plants.filter(p => !p.toRemove); if (alivePlants.length === 0) return; const victim = alivePlants[Math.floor(Math.random() * alivePlants.length)]; if (victim) { victim.toRemove = true; gameState.grid.removePlant(victim.col, victim.row); gameState.combat.createExplosionParticles(victim.x, victim.y); if (window.PvZAudio) window.PvZAudio.playStrike(); } } spawnBallHazard(row, type, gameState) { const bounds = gameState.grid.getCellBounds(7, row); this.hazards.push({ type, row, x: this.x - 20, y: bounds.centerY, speed: 130, radius: 28, toRemove: false }); if (window.PvZAudio) window.PvZAudio.playHugeWave(); } throwVan(gameState) { // Pick 2x2 area to crush const targetCol = 2 + Math.floor(Math.random() * 3); const targetRow = Math.floor(Math.random() * (gameState.grid.rows - 1)); for (let r = targetRow; r <= targetRow + 1; r++) { for (let c = targetCol; c <= targetCol + 1; c++) { const p = gameState.grid.getPlant(c, r); if (p) { p.toRemove = true; gameState.grid.removePlant(c, r); } const b = gameState.grid.getCellBounds(c, r); gameState.combat.createExplosionParticles(b.centerX, b.centerY); } } if (window.PvZAudio) window.PvZAudio.playExplosion(); } draw(ctx) { if (!this.isActive) return; ctx.save(); // 1. Zombot Body & Legs const bob = Math.sin(this.animTimer * 2.5) * 5; // Shadow ctx.fillStyle = 'rgba(0, 0, 0, 0.4)'; ctx.beginPath(); ctx.ellipse(this.x + 95, this.y + 320, 90, 24, 0, 0, Math.PI * 2); ctx.fill(); // Robot Legs ctx.fillStyle = '#424242'; ctx.fillRect(this.x + 30, this.y + 180 + bob, 35, 140); ctx.fillRect(this.x + 125, this.y + 180 + bob, 35, 140); // Feet ctx.fillStyle = '#212121'; ctx.fillRect(this.x + 15, this.y + 300, 65, 24); ctx.fillRect(this.x + 110, this.y + 300, 65, 24); // Torso / Chassis ctx.fillStyle = '#616161'; ctx.beginPath(); ctx.roundRect(this.x + 15, this.y + 70 + bob, 160, 130, 16); ctx.fill(); ctx.strokeStyle = '#212121'; ctx.lineWidth = 4; ctx.stroke(); // Cockpit Glass & Dr. Zomboss ctx.fillStyle = 'rgba(33, 150, 243, 0.35)'; ctx.beginPath(); ctx.roundRect(this.x + 40, this.y + 15 + bob, 110, 65, 12); ctx.fill(); ctx.strokeStyle = '#0d47a1'; ctx.lineWidth = 3; ctx.stroke(); // Dr. Zomboss head inside ctx.fillStyle = '#81c784'; // zombie green skin ctx.beginPath(); ctx.arc(this.x + 95, this.y + 45 + bob, 22, 0, Math.PI * 2); ctx.fill(); // Zomboss giant brain ctx.fillStyle = '#f48fb1'; ctx.beginPath(); ctx.arc(this.x + 95, this.y + 34 + bob, 16, Math.PI, Math.PI * 2); ctx.fill(); // Zomboss red eyes ctx.fillStyle = '#d50000'; ctx.beginPath(); ctx.arc(this.x + 88, this.y + 45 + bob, 4, 0, Math.PI * 2); ctx.arc(this.x + 102, this.y + 45 + bob, 4, 0, Math.PI * 2); ctx.fill(); // Zombot Giant Robot Eyes ctx.fillStyle = this.eyeColor; ctx.shadowColor = this.eyeColor; ctx.shadowBlur = 12; ctx.beginPath(); ctx.arc(this.x + 60, this.y + 115 + bob, 14, 0, Math.PI * 2); ctx.arc(this.x + 130, this.y + 115 + bob, 14, 0, Math.PI * 2); ctx.fill(); ctx.shadowBlur = 0; // Zombot Metal Jaw & Grille ctx.fillStyle = '#212121'; ctx.fillRect(this.x + 45, this.y + 150 + bob, 100, 30); ctx.strokeStyle = '#757575'; ctx.lineWidth = 2; for (let i = 0; i < 6; i++) { ctx.beginPath(); ctx.moveTo(this.x + 52 + i * 16, this.y + 150 + bob); ctx.lineTo(this.x + 52 + i * 16, this.y + 180 + bob); ctx.stroke(); } // 2. Boss Health Bar (Top Center) const barW = 340; const barH = 22; const barX = 280; const barY = 16; ctx.fillStyle = '#212121'; ctx.beginPath(); ctx.roundRect(barX, barY, barW, barH, 5); ctx.fill(); ctx.strokeStyle = '#b71c1c'; ctx.lineWidth = 2.5; ctx.stroke(); const hpRatioBar = Math.max(0, this.health / this.maxHealth); ctx.fillStyle = hpRatioBar < 0.35 ? '#d50000' : (hpRatioBar < 0.70 ? '#ff9800' : '#4caf50'); ctx.beginPath(); ctx.roundRect(barX + 2, barY + 2, Math.max(0, (barW - 4) * hpRatioBar), barH - 4, 3); ctx.fill(); ctx.fillStyle = '#fff'; ctx.font = 'bold 12px sans-serif'; ctx.textAlign = 'center'; ctx.fillText(`👑 ДОКТОР ЗОМБОСС: ${Math.round(this.health)} / ${this.maxHealth} HP`, barX + barW / 2, barY + 15); // 3. Draw Active Hazards (Ice/Fire Balls) for (const h of this.hazards) { ctx.save(); ctx.translate(h.x, h.y); if (h.type === 'ice') { ctx.fillStyle = '#80deea'; ctx.shadowColor = '#00e5ff'; ctx.shadowBlur = 10; ctx.beginPath(); ctx.arc(0, 0, h.radius, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = '#ffffff'; ctx.lineWidth = 3; ctx.stroke(); } else { ctx.fillStyle = '#ff5722'; ctx.shadowColor = '#ff1744'; ctx.shadowBlur = 12; ctx.beginPath(); ctx.arc(0, 0, h.radius, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = '#ffeb3b'; ctx.lineWidth = 3; ctx.stroke(); } ctx.restore(); } ctx.restore(); } };