// Plant Entities (Sunflower, Peashooter, SnowPea, Wallnut, CherryBomb, PotatoMine, Repeater, Chomper, Jalapeno, Sunshroom, Fumeshroom, Lilypad, Gravebuster) window.Plant = class Plant { constructor(type, col, row, gridX, gridY, tileSizeX, tileSizeY, level = 1) { this.type = type; this.col = col; this.row = row; this.x = gridX + tileSizeX / 2; this.y = gridY + tileSizeY / 2; this.width = tileSizeX; this.height = tileSizeY; this.level = level || 1; this.timer = 0; this.animTime = Math.random() * 10; this.toRemove = false; // Attributes by type & level this.maxHealth = 300; this.damage = 20; this.armorReduction = 0; this.recoilDps = 0; switch (type) { case 'sunflower': this.sunInterval = this.level === 3 ? 9 : (this.level === 2 ? 10 : 13); this.sunAmount = this.level >= 2 ? 35 : 25; this.sunTimer = 3; break; case 'peashooter': this.shootInterval = this.level === 3 ? 1.05 : (this.level === 2 ? 1.15 : 1.4); this.damage = this.level === 3 ? 30 : (this.level === 2 ? 25 : 20); this.critChance = this.level === 2 ? 0.15 : 0; this.shootTimer = 0.5; break; case 'snowpea': this.shootInterval = this.level >= 2 ? 1.25 : 1.4; this.damage = this.level >= 2 ? 25 : 20; this.shootTimer = 0.5; break; case 'repeater': this.shootInterval = 1.35; this.damage = 20; this.shootTimer = 0.5; this.repeaterSecondPea = 0; this.repeaterThirdPea = 0; break; case 'wallnut': this.maxHealth = this.level === 3 ? 2400 : (this.level === 2 ? 1800 : 1200); this.armorReduction = this.level >= 2 ? 0.15 : 0; this.recoilDps = this.level === 3 ? 18 : 0; // Thorns on Tier 3! break; case 'cherrybomb': this.fuse = 1.2; this.fuseTimer = 0; break; case 'potatomine': this.armTime = this.level === 3 ? 4.0 : (this.level === 2 ? 6.0 : 14.0); this.armTimer = 0; this.isArmed = false; break; case 'chomper': this.maxHealth = 450; this.isChewing = false; this.chewTimer = 0; this.chewDuration = this.level >= 2 ? 14.0 : 25.0; this.bitesLeft = this.level === 3 ? 2 : 1; break; case 'jalapeno': this.fuse = 0.9; this.fuseTimer = 0; break; case 'sunshroom': this.age = 0; this.sunInterval = 12; this.sunTimer = 3; this.isBig = false; break; case 'fumeshroom': this.shootInterval = 1.5; this.shootTimer = 0.5; break; case 'lilypad': this.maxHealth = 300; this.isLilypad = true; break; case 'gravebuster': this.maxHealth = 300; this.bustTimer = 0; this.bustDuration = 5.0; break; case 'flowerpot': this.maxHealth = 300; this.isPot = true; break; case 'cabbagepult': this.damage = 40; this.shootInterval = 2.0; this.shootTimer = 0.8; break; case 'melonpult': this.damage = 80; this.shootInterval = 2.4; this.shootTimer = 1.0; break; } this.health = this.maxHealth; } update(dt, gameState) { this.animTime += dt; if (this.health <= 0) { this.toRemove = true; return; } // 1. Sunflower & Sun-shroom if (this.type === 'sunflower') { this.sunTimer += dt; if (this.sunTimer >= this.sunInterval) { this.sunTimer = 0; gameState.spawnSun(this.x + (Math.random() * 20 - 10), this.y + (Math.random() * 20 - 10), this.sunAmount || 25, false); // Tier 3: Heal neighboring plants! if (this.level === 3) { for (const p of gameState.plants) { if (p !== this && Math.abs(p.col - this.col) <= 1 && Math.abs(p.row - this.row) <= 1 && p.health > 0) { p.health = Math.min(p.maxHealth, p.health + 30); gameState.combat.createSplash(p.x, p.y, '#80deea', 6); } } } } } else if (this.type === 'sunshroom') { this.age += dt; if (this.age > 50 && !this.isBig) { this.isBig = true; } this.sunTimer += dt; if (this.sunTimer >= this.sunInterval) { this.sunTimer = 0; const val = this.isBig ? 25 : 15; gameState.spawnSun(this.x + (Math.random() * 16 - 8), this.y + (Math.random() * 16 - 8), val, false); } } // 2. Peashooter & SnowPea if (this.type === 'peashooter' || this.type === 'snowpea') { this.shootTimer += dt; if (this.shootTimer >= this.shootInterval) { const hasZombie = gameState.zombies.some(z => z.row === this.row && z.x > this.x && z.health > 0 && !z.toRemove); if (hasZombie) { this.shootTimer = 0; if (this.type === 'peashooter' && this.level === 3) { // Tier 3: Plasma Peashooter gameState.projectiles.push(new window.Projectile(this.x + 24, this.y - 12, this.row, 'plasma', 0, this.damage)); } else if (this.type === 'snowpea' && this.level === 3) { // Tier 3: Freeze Snowpea const deepFreeze = Math.random() < 0.2; gameState.projectiles.push(new window.Projectile(this.x + 24, this.y - 12, this.row, deepFreeze ? 'freeze_snowpea' : 'snowpea', 0, this.damage)); } else { const isCrit = this.critChance && Math.random() < this.critChance; const projType = this.type === 'snowpea' ? 'snowpea' : 'pea'; gameState.projectiles.push(new window.Projectile(this.x + 24, this.y - 12, this.row, projType, 0, this.damage, isCrit)); } if (window.PvZAudio) window.PvZAudio.playShoot(); } } } // 2.5 Cabbage-pult & Melon-pult (Lobbed Arc Shooters) if (this.type === 'cabbagepult' || this.type === 'melonpult') { this.shootTimer += dt; if (this.shootTimer >= this.shootInterval) { const targetZombie = gameState.zombies.find(z => z.row === this.row && z.x > this.x && z.health > 0 && !z.toRemove); const hasBoss = gameState.boss && gameState.boss.isActive; if (targetZombie || hasBoss) { this.shootTimer = 0; const targetX = targetZombie ? targetZombie.x : (hasBoss ? gameState.boss.x : 750); const projType = this.type === 'melonpult' ? 'melon' : 'cabbage'; gameState.projectiles.push(new window.Projectile(this.x + 15, this.y - 10, this.row, projType, 0, this.damage, false, targetX)); if (window.PvZAudio) window.PvZAudio.playShoot(); } } } // 3. Repeater (Double Pea) if (this.type === 'repeater') { this.shootTimer += dt; if (this.repeaterThirdPea > 0) { this.repeaterThirdPea -= dt; if (this.repeaterThirdPea <= 0) { const pType = this.level === 3 ? 'firepea' : 'pea'; gameState.projectiles.push(new window.Projectile(this.x + 24, this.y - 12, this.row, pType, 0, 22)); if (window.PvZAudio) window.PvZAudio.playShoot(); } } else if (this.repeaterSecondPea > 0) { this.repeaterSecondPea -= dt; if (this.repeaterSecondPea <= 0) { const pType = this.level === 3 ? 'firepea' : 'pea'; gameState.projectiles.push(new window.Projectile(this.x + 24, this.y - 12, this.row, pType, 0, 22)); if (window.PvZAudio) window.PvZAudio.playShoot(); } } else if (this.shootTimer >= this.shootInterval) { const hasZombie = gameState.zombies.some(z => z.row === this.row && z.x > this.x && z.health > 0 && !z.toRemove); if (hasZombie) { this.shootTimer = 0; const pType = this.level === 3 ? 'firepea' : 'pea'; gameState.projectiles.push(new window.Projectile(this.x + 24, this.y - 12, this.row, pType, 0, 22)); this.repeaterSecondPea = 0.16; if (this.level >= 2 && Math.random() < 0.25) { this.repeaterThirdPea = 0.32; // Tier 2+ bonus 3rd pea! } if (window.PvZAudio) window.PvZAudio.playShoot(); } } } // 4. Fume-shroom (Piercing Spore Cloud) if (this.type === 'fumeshroom') { this.shootTimer += dt; if (this.shootTimer >= this.shootInterval) { const hasZombie = gameState.zombies.some(z => z.row === this.row && z.x > this.x && (z.x - this.x) < 330 && z.health > 0 && !z.toRemove); if (hasZombie) { this.shootTimer = 0; gameState.projectiles.push(new window.Projectile(this.x + 20, this.y - 8, this.row, 'fume')); if (window.PvZAudio) window.PvZAudio.playShoot(); } } } // 5. Potato Mine if (this.type === 'potatomine') { if (!this.isArmed) { this.armTimer += dt; if (this.armTimer >= this.armTime) { this.isArmed = true; } } else { // Explode if zombie steps on it const victim = gameState.zombies.find(z => z.row === this.row && Math.abs(z.x - this.x) < 35 && z.health > 0 && !z.toRemove); if (victim) { this.toRemove = true; gameState.combat.createExplosionParticles(this.x, this.y); if (window.PvZAudio) window.PvZAudio.playMine(); if (gameState.achievements) gameState.achievements.trigger('spudow_master'); if (this.level === 3) { // Tier 3: 3x3 Explosion! gameState.explodeArea(this.col, this.row, 3); } else { for (const z of gameState.zombies) { if (z.row === this.row && Math.abs(z.x - this.x) < 45 && z.health > 0 && !z.toRemove) { z.takeDamage(1800); gameState.addScore(250); gameState.zombiesKilled++; } } } } } } // 6. Chomper (Instant Swallow) if (this.type === 'chomper') { if (this.isChewing) { this.chewTimer += dt; if (this.chewTimer >= this.chewDuration) { this.isChewing = false; this.chewTimer = 0; this.bitesLeft = this.level === 3 ? 2 : 1; } } else { const victim = gameState.zombies.find(z => z.row === this.row && z.x > this.x && (z.x - this.x) < 110 && z.health > 0 && !z.toRemove); if (victim) { victim.takeDamage(9999); gameState.addScore(200); gameState.zombiesKilled++; if (window.PvZAudio) window.PvZAudio.playGulp(); if (this.level === 3 && this.bitesLeft > 1) { this.bitesLeft--; // Still ready for another bite! } else { this.isChewing = true; this.chewTimer = 0; } } } } // 7. Cherry Bomb if (this.type === 'cherrybomb') { this.fuseTimer += dt; if (this.fuseTimer >= this.fuse) { this.toRemove = true; gameState.explodeArea(this.col, this.row, 3); if (window.PvZAudio) window.PvZAudio.playExplosion(); } } // 8. Jalapeno (Full Row Sizzle) if (this.type === 'jalapeno') { this.fuseTimer += dt; if (this.fuseTimer >= this.fuse) { this.toRemove = true; if (window.PvZAudio) window.PvZAudio.playSizzle(); // Fiery blast along entire row for (let c = 0; c < gameState.grid.cols; c++) { const bounds = gameState.grid.getCellBounds(c, this.row); gameState.combat.createExplosionParticles(bounds.centerX, bounds.centerY); } let killedAny = false; for (const z of gameState.zombies) { if (z.row === this.row && z.health > 0 && !z.toRemove) { z.takeDamage(1800); gameState.addScore(250); gameState.zombiesKilled++; killedAny = true; } } if (killedAny && gameState.achievements) { gameState.achievements.trigger('fire_jalapeno'); } } } // 9. Grave Buster if (this.type === 'gravebuster') { this.bustTimer += dt; if (this.bustTimer >= this.bustDuration) { this.toRemove = true; gameState.grid.removeGrave(this.col, this.row); gameState.spawnCoin(this.x, this.y, 'gold'); if (window.PvZAudio) window.PvZAudio.playCoin(); } } } takeDamage(amount) { const reduced = amount * (1 - (this.armorReduction || 0)); this.health -= reduced; if (this.health <= 0) { this.toRemove = true; } } draw(ctx) { ctx.save(); ctx.translate(this.x, this.y); // Ground shadow ctx.fillStyle = 'rgba(0, 0, 0, 0.25)'; ctx.beginPath(); ctx.ellipse(0, 32, 28, 10, 0, 0, Math.PI * 2); ctx.fill(); const sway = Math.sin(this.animTime * 3) * 0.05; ctx.rotate(sway); switch (this.type) { case 'sunflower': this.drawSunflower(ctx); break; case 'peashooter': this.drawPeashooter(ctx, false); break; case 'snowpea': this.drawPeashooter(ctx, true); break; case 'repeater': this.drawRepeater(ctx); break; case 'wallnut': this.drawWallnut(ctx); break; case 'cherrybomb': this.drawCherryBomb(ctx); break; case 'potatomine': this.drawPotatoMine(ctx); break; case 'chomper': this.drawChomper(ctx); break; case 'jalapeno': this.drawJalapeno(ctx); break; case 'sunshroom': this.drawSunshroom(ctx); break; case 'fumeshroom': this.drawFumeshroom(ctx); break; case 'lilypad': this.drawLilypad(ctx); break; case 'gravebuster': this.drawGravebuster(ctx); break; case 'flowerpot': this.drawFlowerPot(ctx); break; case 'cabbagepult': this.drawCabbagepult(ctx); break; case 'melonpult': this.drawMelonpult(ctx); break; } // Tier Stars (Level 2 & 3) if (this.level > 1) { ctx.font = 'bold 12px sans-serif'; ctx.textAlign = 'center'; ctx.fillStyle = this.level === 3 ? '#00e5ff' : '#ffd54f'; ctx.shadowColor = this.level === 3 ? 'rgba(0, 229, 255, 0.8)' : 'rgba(255, 213, 79, 0.8)'; ctx.shadowBlur = 4; const stars = this.level === 3 ? '★★★' : '★★'; ctx.fillText(stars, 0, -42); ctx.shadowBlur = 0; } ctx.restore(); } drawSunflower(ctx) { ctx.fillStyle = '#4caf50'; ctx.beginPath(); ctx.ellipse(-18, 18, 16, 7, -0.4, 0, Math.PI * 2); ctx.ellipse(18, 18, 16, 7, 0.4, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = '#2e7d32'; ctx.lineWidth = 6; ctx.beginPath(); ctx.moveTo(0, 30); ctx.quadraticCurveTo(-4, 10, 0, -5); ctx.stroke(); const petalCount = 12; const petalPulse = 1 + Math.sin(this.animTime * 4) * 0.06; const isAboutToSpawn = (this.sunInterval - this.sunTimer) < 1.5; ctx.save(); ctx.translate(0, -10); for (let i = 0; i < petalCount; i++) { const angle = (i * (Math.PI * 2) / petalCount) + this.animTime * 0.4; ctx.save(); ctx.rotate(angle); ctx.fillStyle = isAboutToSpawn ? '#fff59d' : '#fbc02d'; ctx.beginPath(); ctx.ellipse(0, -22 * petalPulse, 8, 14 * petalPulse, 0, 0, Math.PI * 2); ctx.fill(); ctx.restore(); } ctx.fillStyle = isAboutToSpawn ? '#8d6e63' : '#5d4037'; ctx.beginPath(); ctx.arc(0, 0, 18, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#212121'; ctx.beginPath(); ctx.arc(-6, -2, 2.5, 0, Math.PI * 2); ctx.arc(6, -2, 2.5, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = '#212121'; ctx.lineWidth = 1.8; ctx.beginPath(); ctx.arc(0, 2, 8, 0.2, Math.PI - 0.2); ctx.stroke(); ctx.restore(); } drawPeashooter(ctx, isSnow) { const mainColor = isSnow ? '#29b6f6' : '#4caf50'; const darkColor = isSnow ? '#0288d1' : '#2e7d32'; ctx.fillStyle = darkColor; ctx.beginPath(); ctx.ellipse(-20, 22, 16, 8, -0.3, 0, Math.PI * 2); ctx.ellipse(20, 22, 16, 8, 0.3, 0, Math.PI * 2); ctx.ellipse(0, 24, 18, 8, 0, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = darkColor; ctx.lineWidth = 7; ctx.beginPath(); ctx.moveTo(0, 24); ctx.quadraticCurveTo(-10, 4, -4, -10); ctx.stroke(); ctx.save(); ctx.translate(-4, -14); const headSquish = Math.sin(this.animTime * 5) * 0.05; ctx.scale(1 + headSquish, 1 - headSquish); ctx.fillStyle = mainColor; ctx.beginPath(); ctx.arc(0, 0, 20, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.ellipse(18, 0, 10, 13, 0, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = darkColor; ctx.beginPath(); ctx.ellipse(22, 0, 5, 9, 0, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#ffffff'; ctx.beginPath(); ctx.arc(6, -8, 5, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#000000'; ctx.beginPath(); ctx.arc(8, -8, 2.5, 0, Math.PI * 2); ctx.fill(); ctx.restore(); } drawRepeater(ctx) { this.drawPeashooter(ctx, false); // Extra top leaves/eyebrow ctx.save(); ctx.translate(-4, -14); ctx.fillStyle = '#1b5e20'; ctx.beginPath(); ctx.ellipse(-16, -12, 12, 6, -0.6, 0, Math.PI * 2); ctx.ellipse(-14, 0, 10, 5, -0.2, 0, Math.PI * 2); ctx.fill(); ctx.restore(); } drawWallnut(ctx) { const healthRatio = this.health / this.maxHealth; ctx.fillStyle = '#8d6e63'; ctx.beginPath(); ctx.ellipse(0, 2, 22, 28, 0, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#a1887f'; ctx.beginPath(); ctx.ellipse(-6, -4, 14, 20, -0.2, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#ffffff'; ctx.beginPath(); ctx.ellipse(-8, -8, 5, 7, 0, 0, Math.PI * 2); ctx.ellipse(8, -8, 5, 7, 0, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#3e2723'; ctx.beginPath(); ctx.arc(-6, -8, 2.5, 0, Math.PI * 2); ctx.arc(10, -8, 2.5, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = '#3e2723'; ctx.lineWidth = 2; ctx.beginPath(); if (healthRatio > 0.66) { ctx.arc(0, 6, 6, 0.2, Math.PI - 0.2); } else if (healthRatio > 0.33) { ctx.moveTo(-6, 8); ctx.lineTo(6, 8); } else { ctx.arc(0, 12, 6, Math.PI + 0.2, Math.PI * 2 - 0.2); } ctx.stroke(); if (healthRatio <= 0.66) { ctx.strokeStyle = '#3e2723'; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(12, -18); ctx.lineTo(6, -6); ctx.lineTo(14, 4); ctx.stroke(); } if (healthRatio <= 0.33) { ctx.strokeStyle = '#3e2723'; ctx.lineWidth = 2.5; ctx.beginPath(); ctx.moveTo(-14, -14); ctx.lineTo(-6, -2); ctx.lineTo(-12, 10); ctx.stroke(); } } drawPotatoMine(ctx) { if (!this.isArmed) { // Underground sprout ctx.fillStyle = '#795548'; ctx.beginPath(); ctx.ellipse(0, 28, 14, 6, 0, 0, Math.PI * 2); ctx.fill(); // Little green antenna ctx.strokeStyle = '#4caf50'; ctx.lineWidth = 3; ctx.beginPath(); ctx.moveTo(0, 28); ctx.lineTo(0, 14); ctx.stroke(); ctx.fillStyle = '#f44336'; ctx.beginPath(); ctx.arc(0, 14, 4, 0, Math.PI * 2); ctx.fill(); } else { // Emerged Potato ctx.fillStyle = '#a1887f'; ctx.beginPath(); ctx.ellipse(0, 16, 20, 16, 0, 0, Math.PI * 2); ctx.fill(); // Flashing red beacon const flash = Math.sin(this.animTime * 12) > 0; ctx.strokeStyle = '#3e2723'; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(0, 2); ctx.lineTo(0, -6); ctx.stroke(); ctx.fillStyle = flash ? '#ff1744' : '#b71c1c'; ctx.beginPath(); ctx.arc(0, -6, 5, 0, Math.PI * 2); ctx.fill(); // Eyes ctx.fillStyle = '#fff'; ctx.beginPath(); ctx.arc(-6, 12, 4, 0, Math.PI * 2); ctx.arc(6, 12, 4, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#000'; ctx.beginPath(); ctx.arc(-5, 12, 2, 0, Math.PI * 2); ctx.arc(7, 12, 2, 0, Math.PI * 2); ctx.fill(); } } drawChomper(ctx) { // Stem ctx.strokeStyle = '#2e7d32'; ctx.lineWidth = 8; ctx.beginPath(); ctx.moveTo(0, 28); ctx.quadraticCurveTo(-14, 0, -4, -12); ctx.stroke(); // Purple Head ctx.save(); ctx.translate(-4, -16); const jawOpen = this.isChewing ? Math.sin(this.animTime * 10) * 0.15 + 0.1 : 0.45; // Upper head ctx.save(); ctx.rotate(-jawOpen); ctx.fillStyle = '#7b1fa2'; ctx.beginPath(); ctx.arc(0, 0, 22, Math.PI, 0); ctx.lineTo(26, 0); ctx.fill(); // Teeth ctx.fillStyle = '#fff'; for (let i = 0; i < 4; i++) { ctx.beginPath(); ctx.moveTo(i * 6, 0); ctx.lineTo(i * 6 + 3, 6); ctx.lineTo(i * 6 + 6, 0); ctx.fill(); } ctx.restore(); // Lower jaw ctx.save(); ctx.rotate(jawOpen); ctx.fillStyle = '#4a148c'; ctx.beginPath(); ctx.arc(0, 4, 18, 0, Math.PI); ctx.fill(); ctx.restore(); ctx.restore(); } drawJalapeno(ctx) { const pulse = 1 + Math.sin(this.animTime * 15) * 0.1; ctx.scale(pulse, pulse); // Green cap ctx.fillStyle = '#2e7d32'; ctx.beginPath(); ctx.arc(0, -26, 8, Math.PI, 0); ctx.fill(); // Red pepper body ctx.fillStyle = '#d50000'; ctx.beginPath(); ctx.moveTo(-10, -22); ctx.quadraticCurveTo(-14, 10, 0, 28); ctx.quadraticCurveTo(14, 10, 10, -22); ctx.closePath(); ctx.fill(); // Furious Face ctx.fillStyle = '#fff'; ctx.beginPath(); ctx.arc(-4, -6, 3, 0, Math.PI * 2); ctx.arc(4, -6, 3, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#000'; ctx.beginPath(); ctx.arc(-4, -6, 1.5, 0, Math.PI * 2); ctx.arc(4, -6, 1.5, 0, Math.PI * 2); ctx.fill(); } drawSunshroom(ctx) { const scale = this.isBig ? 1.0 : 0.65; ctx.scale(scale, scale); // Stalk ctx.fillStyle = '#fff9c4'; ctx.fillRect(-6, 4, 12, 22); // Glowing Cap ctx.fillStyle = '#fbc02d'; ctx.beginPath(); ctx.arc(0, 6, 18, Math.PI, 0); ctx.fill(); // Orange spots ctx.fillStyle = '#f57f17'; ctx.beginPath(); ctx.arc(-6, -2, 4, 0, Math.PI * 2); ctx.arc(6, -2, 4, 0, Math.PI * 2); ctx.fill(); } drawFumeshroom(ctx) { // Stalk ctx.fillStyle = '#e1bee7'; ctx.fillRect(-7, 4, 14, 22); // Purple Cap ctx.fillStyle = '#8e24aa'; ctx.beginPath(); ctx.arc(0, 4, 20, Math.PI, 0); ctx.fill(); // Spout snout ctx.fillStyle = '#6a1b9a'; ctx.beginPath(); ctx.ellipse(16, 2, 7, 10, 0, 0, Math.PI * 2); ctx.fill(); } drawLilypad(ctx) { ctx.fillStyle = '#2e7d32'; ctx.beginPath(); ctx.ellipse(0, 24, 32, 16, 0, 0.4, Math.PI * 2 - 0.4); ctx.lineTo(0, 24); ctx.closePath(); ctx.fill(); ctx.strokeStyle = '#1b5e20'; ctx.lineWidth = 2; ctx.stroke(); } drawGravebuster(ctx) { // Green roots wrapping around grave ctx.strokeStyle = '#33691e'; ctx.lineWidth = 5; ctx.beginPath(); ctx.arc(0, 0, 24, 0, Math.PI * 2); ctx.stroke(); } drawCherryBomb(ctx) { const pulse = 1 + (this.fuseTimer / this.fuse) * 0.4 + Math.sin(this.animTime * 15) * 0.1; ctx.scale(pulse, pulse); ctx.strokeStyle = '#2e7d32'; ctx.lineWidth = 4; ctx.beginPath(); ctx.moveTo(0, -22); ctx.lineTo(-14, 0); ctx.moveTo(0, -22); ctx.lineTo(14, 0); ctx.stroke(); const flash = Math.sin(this.animTime * 20) > 0; const cherryColor = flash ? '#ff1744' : '#b71c1c'; ctx.fillStyle = cherryColor; ctx.beginPath(); ctx.arc(-14, 4, 16, 0, Math.PI * 2); ctx.arc(14, 4, 16, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#fff'; ctx.beginPath(); ctx.arc(-16, 2, 4, 0, Math.PI * 2); ctx.arc(12, 2, 4, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#000'; ctx.beginPath(); ctx.arc(-15, 2, 2, 0, Math.PI * 2); ctx.arc(13, 2, 2, 0, Math.PI * 2); ctx.fill(); } drawFlowerPot(ctx) { // Terracotta clay pot ctx.fillStyle = '#bf360c'; ctx.beginPath(); ctx.moveTo(-24, 8); ctx.lineTo(24, 8); ctx.lineTo(18, 32); ctx.lineTo(-18, 32); ctx.closePath(); ctx.fill(); // Rim ctx.fillStyle = '#d84315'; ctx.beginPath(); ctx.roundRect(-26, 2, 52, 9, 3); ctx.fill(); // Dark soil inside ctx.fillStyle = '#3e2723'; ctx.beginPath(); ctx.ellipse(0, 5, 20, 4, 0, 0, Math.PI * 2); ctx.fill(); // Little green leaf sprout on pot edge ctx.fillStyle = '#66bb6a'; ctx.beginPath(); ctx.ellipse(14, 2, 6, 3, -0.5, 0, Math.PI * 2); ctx.fill(); } drawCabbagepult(ctx) { const bob = Math.sin(this.animTime * 3) * 1.5; // Wooden catapult basket frame ctx.fillStyle = '#5d4037'; ctx.fillRect(-16, 12, 32, 10); ctx.fillStyle = '#8d6e63'; ctx.beginPath(); ctx.roundRect(-12, 0, 24, 14, 3); ctx.fill(); // Catapult Arm const armAngle = -0.4 + Math.sin(this.animTime * 4) * 0.1; ctx.save(); ctx.translate(-4, 6); ctx.rotate(armAngle); ctx.strokeStyle = '#4e342e'; ctx.lineWidth = 4; ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(-24, -18); ctx.stroke(); // Spoon basket ctx.fillStyle = '#8d6e63'; ctx.beginPath(); ctx.arc(-26, -18, 10, 0, Math.PI * 2); ctx.fill(); // Cabbage inside basket ctx.fillStyle = '#4caf50'; ctx.beginPath(); ctx.arc(-26, -20, 8, 0, Math.PI * 2); ctx.fill(); ctx.restore(); // Main Cabbage Head Face ctx.fillStyle = '#81c784'; ctx.beginPath(); ctx.arc(6, -10 + bob, 18, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = '#388e3c'; ctx.lineWidth = 2; ctx.stroke(); // Eyes ctx.fillStyle = '#fff'; ctx.beginPath(); ctx.arc(2, -12 + bob, 4, 0, Math.PI * 2); ctx.arc(12, -12 + bob, 4, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#000'; ctx.beginPath(); ctx.arc(3, -12 + bob, 2, 0, Math.PI * 2); ctx.arc(13, -12 + bob, 2, 0, Math.PI * 2); ctx.fill(); } drawMelonpult(ctx) { const bob = Math.sin(this.animTime * 2.5) * 2; // Heavy wooden frame ctx.fillStyle = '#3e2723'; ctx.fillRect(-22, 10, 44, 14); ctx.fillStyle = '#5d4037'; ctx.beginPath(); ctx.roundRect(-16, -2, 32, 16, 4); ctx.fill(); // Heavy catapult lever const armAngle = -0.5 + Math.sin(this.animTime * 3) * 0.1; ctx.save(); ctx.translate(-6, 4); ctx.rotate(armAngle); ctx.strokeStyle = '#263238'; ctx.lineWidth = 6; ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(-28, -22); ctx.stroke(); // Heavy steel basket ctx.fillStyle = '#455a64'; ctx.beginPath(); ctx.arc(-30, -24, 14, 0, Math.PI * 2); ctx.fill(); // Watermelon inside basket ctx.fillStyle = '#2e7d32'; ctx.beginPath(); ctx.ellipse(-30, -26, 12, 9, 0, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = '#a5d6a7'; ctx.lineWidth = 2; ctx.stroke(); ctx.restore(); // Melonpult Head Face ctx.fillStyle = '#388e3c'; ctx.beginPath(); ctx.ellipse(8, -14 + bob, 22, 17, 0, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = '#a5d6a7'; ctx.lineWidth = 2.5; ctx.stroke(); // Eyes ctx.fillStyle = '#fff'; ctx.beginPath(); ctx.arc(4, -16 + bob, 4.5, 0, Math.PI * 2); ctx.arc(15, -16 + bob, 4.5, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#000'; ctx.beginPath(); ctx.arc(5, -16 + bob, 2, 0, Math.PI * 2); ctx.arc(16, -16 + bob, 2, 0, Math.PI * 2); ctx.fill(); } };