User prompt
añade la pala para quitar plantas
User prompt
haz que en vez de que la semilla se recargue en 10 segundos tarde 3 segundos y añade las podadoras y la petaseta ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
as el mapa mas grande y que cada semilla dure 10 segundos en recarcar y que cada semilla de pltanta tenga iconos y sprites diferentes ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
haz que el jugador inicie con 150 soles
User prompt
añade ala planta carnivora y as que la papapum al explotar haga una explosion ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
as que el patio sea mas grande y que las plantas esten abajo
User prompt
haz que los zombies tarden 30 segundos en aparecer y añade a la papapum
Code edit (1 edits merged)
Please save this source code
User prompt
Plant Defense: Zombie Wave
Initial prompt
as un plantas vs zombies funcional
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var BowlingMinigame = Container.expand(function () { var self = Container.call(this); self.completed = false; self.timeLeft = -1; // No time limit self.score = 0; self.targetScore = 1000; // Need to hit 1000 zombies self.zombies = []; self.walnuts = []; self.walnutsAvailable = 0; self.maxWalnuts = 5; // Create background var bg = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 15, scaleY: 20, alpha: 0.4, tint: 0x654321 // Brown background }); self.addChild(bg); // Title text var titleText = new Text2('¡LANZA NUECES A 1000 ZOMBIES!', { size: 80, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 300; self.addChild(titleText); // Instructions text var instructionText = new Text2('Sin límite de tiempo - Elimina 1000 zombies', { size: 40, fill: 0xFFFFFF }); instructionText.anchor.set(0.5, 0.5); instructionText.x = 1024; instructionText.y = 400; self.addChild(instructionText); // Timer text var timerText = new Text2('Tiempo: 60', { size: 60, fill: 0xFFFF00 }); timerText.anchor.set(0.5, 0.5); timerText.x = 1024; timerText.y = 500; self.addChild(timerText); // Score text var scoreText = new Text2('Zombies eliminados: 0/1000', { size: 50, fill: 0x00FF00 }); scoreText.anchor.set(0.5, 0.5); scoreText.x = 1024; scoreText.y = 600; self.addChild(scoreText); // Walnuts available text self.walnutsText = new Text2('Nueces: 0', { size: 40, fill: 0xFFFFFF }); self.walnutsText.anchor.set(0.5, 0.5); self.walnutsText.x = 1024; self.walnutsText.y = 700; self.addChild(self.walnutsText); // Create conveyor belt var conveyorBelt = LK.getAsset('conveyorBelt', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 2000, scaleX: 1.5, scaleY: 1.5 }); self.addChild(conveyorBelt); // Conveyor belt mechanics self.conveyorTimer = 0; self.walnutGenerateRate = 180; // 3 seconds at 60fps var selectedWalnut = null; // Spawn zombies periodically self.zombieTimer = 0; self.zombieSpawnRate = 120; // 2 seconds at 60fps self.spawnZombie = function () { var zombie = LK.getAsset('zombie', { anchorX: 0.5, anchorY: 0.5, x: 2100, y: 800 + Math.random() * 800, scaleX: 0.8, scaleY: 0.8 }); self.addChild(zombie); zombie.speed = -1 - Math.random() * 2; zombie.health = 40; zombie.destroyed = false; zombie.update = function () { zombie.x += zombie.speed; if (zombie.x < 200) { // Zombie reached the left side zombie.destroy(); for (var i = self.zombies.length - 1; i >= 0; i--) { if (self.zombies[i] === zombie) { self.zombies.splice(i, 1); break; } } } }; // Zombies no longer need click interaction - walnuts will collide with them self.zombies.push(zombie); }; self.update = function () { if (self.completed) return; // No time limit - removed time decrease and timer update timerText.setText('Sin límite de tiempo'); // Generate walnuts on conveyor belt self.conveyorTimer++; if (self.conveyorTimer >= self.walnutGenerateRate && self.walnutsAvailable < self.maxWalnuts) { self.conveyorTimer = 0; self.walnutsAvailable++; self.walnutsText.setText('Nueces: ' + self.walnutsAvailable); // Visual indication that walnut is available (show icon on conveyor) var walnutIcon = LK.getAsset('wallnut', { anchorX: 0.5, anchorY: 0.5, x: 900 + (self.walnutsAvailable - 1) * 60, y: 2000, scaleX: 0.8, scaleY: 0.8 }); self.addChild(walnutIcon); self.walnuts.push(walnutIcon); // Fade in animation for walnut availability tween(walnutIcon, { alpha: 1, scaleX: 1.0, scaleY: 1.0 }, { duration: 300, easing: tween.easeOut }); } // Spawn zombies self.zombieTimer++; if (self.zombieTimer >= self.zombieSpawnRate) { self.zombieTimer = 0; self.spawnZombie(); } // Check win condition if (self.score >= self.targetScore) { self.endMinigame(true); return; } // No time limit lose condition - removed // Clean up off-screen zombies for (var i = self.zombies.length - 1; i >= 0; i--) { var zombie = self.zombies[i]; if (zombie.x < 100) { zombie.destroy(); self.zombies.splice(i, 1); } } }; self.endMinigame = function (success) { self.completed = true; var resultText = new Text2(success ? '¡EXCELENTE PUNTERÍA!' : '¡INTÉNTALO DE NUEVO!', { size: 100, fill: success ? 0x00FF00 : 0xFF0000 }); resultText.anchor.set(0.5, 0.5); resultText.x = 1024; resultText.y = 1400; self.addChild(resultText); var rewardText = new Text2('', { size: 60, fill: 0xFFFF00 }); rewardText.anchor.set(0.5, 0.5); rewardText.x = 1024; rewardText.y = 1550; self.addChild(rewardText); if (success) { var reward = Math.floor(self.score * 15); sunPoints += reward; rewardText.setText('¡+' + reward + ' soles de recompensa!'); updateSunDisplay(); } else { rewardText.setText('¡Sigue practicando tu puntería!'); } var continueText = new Text2('Toca para continuar', { size: 40, fill: 0xFFFFFF }); continueText.anchor.set(0.5, 0.5); continueText.x = 1024; continueText.y = 1700; self.addChild(continueText); // Make screen clickable to continue self.down = function (x, y, obj) { self.destroy(); currentMinigame = null; // Continue to level 5 startLevel(5); }; }; return self; }); var ConveyorBelt = Container.expand(function () { var self = Container.call(this); var beltGraphics = self.attachAsset('conveyorBelt', { anchorX: 0.5, anchorY: 0.5 }); self.seedTimer = 0; self.seedGenerateRate = 600; // 10 seconds at 60fps // Available seeds for boss level - excluding sun producers (sunflower, sunshroom) self.availableSeeds = ['wallnut', 'carnivorousplant', 'doomshroom', 'melonpulta', 'coltapulta', 'lanzamaiz']; // Create text display for seed feedback var seedText = new Text2('', { size: 40, fill: 0xFFFFFF }); seedText.anchor.set(0.5, 0.5); seedText.x = 0; seedText.y = -60; // Above the conveyor belt seedText.alpha = 0; // Start invisible self.addChild(seedText); // Create plant display on conveyor belt var plantDisplay = null; // Will hold the current plant image self.update = function () { self.seedTimer++; if (self.seedTimer >= self.seedGenerateRate) { self.seedTimer = 0; // Generate 1 pot and 2 random plants var generatedSeeds = []; // Always add pot first generatedSeeds.push('pot'); seedCooldowns['pot'] = 0; // Add 2 random plants from available seeds for (var i = 0; i < 2; i++) { var randomSeed = self.availableSeeds[Math.floor(Math.random() * self.availableSeeds.length)]; // Ensure no duplicates in the same batch while (generatedSeeds.indexOf(randomSeed) !== -1) { randomSeed = self.availableSeeds[Math.floor(Math.random() * self.availableSeeds.length)]; } generatedSeeds.push(randomSeed); // Reset cooldown for this seed type - properly reset it seedCooldowns[randomSeed] = 0; } // Automatically select the first plant type for placement selectedPlantType = generatedSeeds[0]; shovelSelected = false; // Remove previous plant display if it exists if (plantDisplay) { plantDisplay.destroy(); plantDisplay = null; } // Create display for all 3 plants side by side var plantDisplays = []; for (var j = 0; j < generatedSeeds.length; j++) { var plantSeed = generatedSeeds[j]; var display = LK.getAsset(plantSeed, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.6, alpha: 0 }); display.x = (j - 1) * 100; // Space them out: -100, 0, 100 display.y = 0; self.addChild(display); plantDisplays.push(display); } // Show clear text feedback of which seeds were given var seedNames = []; for (var k = 0; k < generatedSeeds.length; k++) { var seedName = generatedSeeds[k].toUpperCase(); if (generatedSeeds[k] === 'carnivorousplant') seedName = 'CARNIVOROUS PLANT'; if (generatedSeeds[k] === 'doomshroom') seedName = 'DOOM SHROOM'; if (generatedSeeds[k] === 'melonpulta') seedName = 'MELON PULTA'; if (generatedSeeds[k] === 'coltapulta') seedName = 'COL TAPULTA'; if (generatedSeeds[k] === 'lanzamaiz') seedName = 'LANZA MAIZ'; if (generatedSeeds[k] === 'pot') seedName = 'POT'; seedNames.push(seedName); } seedText.setText('1 POT + 2 PLANTS: ' + seedNames.join(', ') + '!'); // Show plant images with fade in animation for (var l = 0; l < plantDisplays.length; l++) { tween(plantDisplays[l], { alpha: 1, scaleX: 0.8, scaleY: 0.8 }, { duration: 300, easing: tween.easeOut }); } // Show text with fade in animation tween(seedText, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { // Keep text visible for 3 seconds, then fade out tween(seedText, { alpha: 0, scaleX: 1, scaleY: 1 }, { duration: 1500, easing: tween.easeIn }); } }); // Fade out plant displays after 4 seconds LK.setTimeout(function () { for (var m = 0; m < plantDisplays.length; m++) { if (plantDisplays[m]) { tween(plantDisplays[m], { alpha: 0, scaleX: 0.4, scaleY: 0.4 }, { duration: 1000, easing: tween.easeIn, onFinish: function onFinish() { if (plantDisplays[m]) { plantDisplays[m].destroy(); } } }); } } }, 4000); // Visual flash effect to show seeds were given tween(self, { scaleX: 1.2, scaleY: 1.2, tint: 0x00ff00 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xffffff }, { duration: 300, easing: tween.easeIn }); } }); } }; return self; }); var Corn = Container.expand(function () { var self = Container.call(this); self.speed = 8; self.damage = 40; self.isButter = Math.random() < 0.25; // 25% chance to be butter var cornGraphics; if (self.isButter) { cornGraphics = self.attachAsset('butter', { anchorX: 0.5, anchorY: 0.5 }); } else { cornGraphics = self.attachAsset('corn', { anchorX: 0.5, anchorY: 0.5 }); } self.update = function () { self.x += self.speed; if (self.x > 2200) { self.destroy(); } }; return self; }); var Fireball = Container.expand(function () { var self = Container.call(this); var fireballGraphics = self.attachAsset('fireball', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -6; self.damage = 50; self.update = function () { // Use trajectory-based movement if speed components are set, otherwise use default if (self.speedX !== undefined && self.speedY !== undefined) { self.x += self.speedX; self.y += self.speedY; } else { self.x += self.speed; } self.rotation += 0.2; // Spinning effect // Fireball trail effect if (LK.ticks % 5 === 0) { tween(self, { tint: 0xff8c00 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { tint: 0xff4500 }, { duration: 100, easing: tween.easeIn }); } }); } // Check if fireball is off-screen (expanded bounds for trajectory-based movement) if (self.x < -100 || self.x > 2200 || self.y < -100 || self.y > 2800) { self.destroy(); } }; return self; }); var Lawnmower = Container.expand(function () { var self = Container.call(this); var lawnmowerGraphics = self.attachAsset('lawnmower', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 0; // Starts stationary self.activated = false; self.update = function () { if (self.activated) { self.x += 12; // Move fast when activated if (self.x > 2200) { self.destroy(); } } }; return self; }); var LevelButton = Container.expand(function (levelNumber) { var self = Container.call(this); // Level button background var buttonBg = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); self.addChild(buttonBg); // Level number text var levelText = new Text2(levelNumber.toString(), { size: 60, fill: 0x000000 }); levelText.anchor.set(0.5, 0.5); self.addChild(levelText); self.levelNumber = levelNumber; self.down = function (x, y, obj) { // Start the selected level startLevel(self.levelNumber); }; return self; }); var LoadingScreen = Container.expand(function () { var self = Container.call(this); // Loading logo image var loadingLogo = self.attachAsset('loadingLogo', { anchorX: 0.5, anchorY: 0.5 }); loadingLogo.x = 1024; loadingLogo.y = 800; // Loading bar background var loadingBarBg = self.attachAsset('loadingBarBg', { anchorX: 0.5, anchorY: 0.5 }); loadingBarBg.x = 1024; loadingBarBg.y = 1400; // Loading bar fill var loadingBar = self.attachAsset('loadingBar', { anchorX: 0, anchorY: 0.5 }); loadingBar.x = 724; // Start at left edge of background loadingBar.y = 1400; loadingBar.width = 0; // Start with no fill // Loading text var loadingText = new Text2('CARGANDO...', { size: 80, fill: 0xFFFFFF }); loadingText.anchor.set(0.5, 0.5); loadingText.x = 1024; loadingText.y = 1200; self.addChild(loadingText); // Progress text var progressText = new Text2('0%', { size: 60, fill: 0xFFFFFF }); progressText.anchor.set(0.5, 0.5); progressText.x = 1024; progressText.y = 1500; self.addChild(progressText); self.progress = 0; self.maxProgress = 300; // 5 seconds at 60fps self.currentProgress = 0; self.update = function () { if (self.currentProgress < self.maxProgress) { self.currentProgress++; self.progress = self.currentProgress / self.maxProgress; // Update loading bar width loadingBar.width = 600 * self.progress; // Update progress text var percentage = Math.floor(self.progress * 100); progressText.setText(percentage + '%'); // Loading complete if (self.currentProgress >= self.maxProgress) { // Start menu music when loading completes LK.playMusic('menuMusic', { loop: true }); // Transition to level selection showLevelSelection(); } } }; return self; }); var Minigame = Container.expand(function (type) { var self = Container.call(this); self.type = type; self.completed = false; self.timeLeft = 30; // 30 seconds for most minigames self.score = 0; self.gameObjects = []; self.spawnTimer = 0; // Create background var bg = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 15, scaleY: 20, alpha: 0.3, tint: 0x000080 }); self.addChild(bg); // Title text var titleText = new Text2('', { size: 80, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 300; self.addChild(titleText); // Timer text var timerText = new Text2('Tiempo: 30', { size: 60, fill: 0xFFFF00 }); timerText.anchor.set(0.5, 0.5); timerText.x = 1024; timerText.y = 400; self.addChild(timerText); // Score text var scoreText = new Text2('Puntos: 0', { size: 50, fill: 0x00FF00 }); scoreText.anchor.set(0.5, 0.5); scoreText.x = 1024; scoreText.y = 500; self.addChild(scoreText); // Instructions text var instructionText = new Text2('', { size: 40, fill: 0xFFFFFF }); instructionText.anchor.set(0.5, 0.5); instructionText.x = 1024; instructionText.y = 600; self.addChild(instructionText); if (type === 'sunCollector') { titleText.setText('¡RECOLECTA SOLES!'); instructionText.setText('Toca los soles que caen para recolectarlos'); self.targetScore = 15; } else if (type === 'zombieShooter') { titleText.setText('¡DISPARA ZOMBIES!'); instructionText.setText('Toca zombies para eliminarlos'); self.targetScore = 20; } else if (type === 'plantDefense') { titleText.setText('¡PROTEGE LAS PLANTAS!'); instructionText.setText('Toca zombies antes de que lleguen a las plantas'); self.targetScore = 10; } else if (type === 'speedPlanting') { titleText.setText('¡PLANTA RÁPIDO!'); instructionText.setText('Planta en las casillas marcadas'); self.targetScore = 12; self.timeLeft = 20; // Shorter time for planting game } self.update = function () { if (self.completed) return; self.timeLeft -= 1 / 60; // Decrease by 1/60 each frame (60fps) timerText.setText('Tiempo: ' + Math.ceil(self.timeLeft)); scoreText.setText('Puntos: ' + self.score); if (self.timeLeft <= 0) { self.endMinigame(); return; } self.spawnTimer++; if (self.type === 'sunCollector') { if (self.spawnTimer % 90 === 0) { // Every 1.5 seconds self.spawnSun(); } } else if (self.type === 'zombieShooter') { if (self.spawnTimer % 120 === 0) { // Every 2 seconds self.spawnZombie(); } } else if (self.type === 'plantDefense') { if (self.spawnTimer % 180 === 0) { // Every 3 seconds self.spawnDefenseZombie(); } } else if (self.type === 'speedPlanting') { if (self.spawnTimer % 120 === 0) { // Every 2 seconds self.spawnPlantSpot(); } } // Clean up off-screen objects for (var i = self.gameObjects.length - 1; i >= 0; i--) { var obj = self.gameObjects[i]; if (obj && (obj.y > 2800 || obj.x < -100 || obj.x > 2200)) { obj.destroy(); self.gameObjects.splice(i, 1); } } }; self.spawnSun = function () { var sun = LK.getAsset('sun', { anchorX: 0.5, anchorY: 0.5, x: 400 + Math.random() * 1200, y: 200, scaleX: 1.5, scaleY: 1.5 }); self.addChild(sun); sun.speed = 2 + Math.random() * 3; sun.down = function (x, y, obj) { self.score++; LK.getSound('collect').play(); sun.destroy(); for (var i = self.gameObjects.length - 1; i >= 0; i--) { if (self.gameObjects[i] === sun) { self.gameObjects.splice(i, 1); break; } } }; sun.update = function () { sun.y += sun.speed; }; self.gameObjects.push(sun); }; self.spawnZombie = function () { var zombie = LK.getAsset('zombie', { anchorX: 0.5, anchorY: 0.5, x: 2100, y: 700 + Math.random() * 1000, scaleX: 0.8, scaleY: 0.8 }); self.addChild(zombie); zombie.speed = -1 - Math.random() * 2; zombie.down = function (x, y, obj) { zombiesKilled++; // Increment kill counter self.score++; LK.getSound('explode').play(); // Death animation tween(zombie, { scaleX: 0, scaleY: 0, rotation: Math.PI, alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { zombie.destroy(); } }); for (var i = self.gameObjects.length - 1; i >= 0; i--) { if (self.gameObjects[i] === zombie) { self.gameObjects.splice(i, 1); break; } } }; zombie.update = function () { zombie.x += zombie.speed; }; self.gameObjects.push(zombie); }; self.spawnDefenseZombie = function () { var zombie = LK.getAsset('fastZombie', { anchorX: 0.5, anchorY: 0.5, x: 1800, y: 800 + Math.random() * 800, scaleX: 0.7, scaleY: 0.7 }); self.addChild(zombie); zombie.speed = -3; zombie.down = function (x, y, obj) { zombiesKilled++; // Increment kill counter self.score++; LK.getSound('explode').play(); tween(zombie, { scaleX: 0, scaleY: 0, alpha: 0 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { zombie.destroy(); } }); for (var i = self.gameObjects.length - 1; i >= 0; i--) { if (self.gameObjects[i] === zombie) { self.gameObjects.splice(i, 1); break; } } }; zombie.update = function () { zombie.x += zombie.speed; if (zombie.x < 400) { // Reached plants area self.score = Math.max(0, self.score - 2); // Lose points zombie.destroy(); for (var i = self.gameObjects.length - 1; i >= 0; i--) { if (self.gameObjects[i] === zombie) { self.gameObjects.splice(i, 1); break; } } } }; self.gameObjects.push(zombie); }; self.spawnPlantSpot = function () { var spot = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, x: 400 + Math.random() * 1200, y: 800 + Math.random() * 800, scaleX: 1.5, scaleY: 1.5, tint: 0x00FF00, alpha: 0.8 }); self.addChild(spot); spot.lifeTime = 300; // 5 seconds to click spot.down = function (x, y, obj) { self.score++; LK.getSound('plant').play(); tween(spot, { scaleX: 2, scaleY: 2, alpha: 0 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { spot.destroy(); } }); for (var i = self.gameObjects.length - 1; i >= 0; i--) { if (self.gameObjects[i] === spot) { self.gameObjects.splice(i, 1); break; } } }; spot.update = function () { spot.lifeTime--; if (spot.lifeTime <= 0) { spot.destroy(); for (var i = self.gameObjects.length - 1; i >= 0; i--) { if (self.gameObjects[i] === spot) { self.gameObjects.splice(i, 1); break; } } } }; self.gameObjects.push(spot); }; self.endMinigame = function () { self.completed = true; var success = self.score >= self.targetScore; var resultText = new Text2(success ? '¡ÉXITO!' : '¡FALLASTE!', { size: 100, fill: success ? 0x00FF00 : 0xFF0000 }); resultText.anchor.set(0.5, 0.5); resultText.x = 1024; resultText.y = 1000; self.addChild(resultText); var rewardText = new Text2('', { size: 60, fill: 0xFFFF00 }); rewardText.anchor.set(0.5, 0.5); rewardText.x = 1024; rewardText.y = 1150; self.addChild(rewardText); if (success) { var reward = Math.floor(self.score * 10); sunPoints += reward; rewardText.setText('¡+' + reward + ' soles de recompensa!'); updateSunDisplay(); } else { rewardText.setText('Intenta de nuevo la próxima vez'); } var continueText = new Text2('Toca para continuar', { size: 40, fill: 0xFFFFFF }); continueText.anchor.set(0.5, 0.5); continueText.x = 1024; continueText.y = 1300; self.addChild(continueText); // Make screen clickable to continue self.down = function (x, y, obj) { self.destroy(); currentMinigame = null; // Continue to next level startLevel(currentLevel); }; }; return self; }); var MinigameSelector = Container.expand(function () { var self = Container.call(this); // Background var bg = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 15, scaleY: 20, alpha: 0.5, tint: 0x4B0082 }); self.addChild(bg); // Title var titleText = new Text2('¡MINIJUEGO!', { size: 120, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 400; self.addChild(titleText); var subtitleText = new Text2('Elige un minijuego para ganar soles extra', { size: 50, fill: 0xFFFF00 }); subtitleText.anchor.set(0.5, 0.5); subtitleText.x = 1024; subtitleText.y = 500; self.addChild(subtitleText); // Minigame buttons - add defense planning for level 19 var minigames = [{ type: 'sunCollector', name: 'Recolectar Soles', desc: 'Recoge 15 soles' }, { type: 'zombieShooter', name: 'Caza Zombies', desc: 'Elimina 20 zombies' }, { type: 'plantDefense', name: 'Defender Plantas', desc: 'Protege las plantas' }, { type: 'speedPlanting', name: 'Plantar Rápido', desc: 'Planta en 12 spots' }, { type: 'yoZombie', name: 'Yo Zombie', desc: 'Destruye 50 plantas' }]; // Add defense planning for level 19 if (currentLevel === 19) { minigames.push({ type: 'defenseSwap', name: 'Planea la Defensa', desc: '10,000 soles - Sin setas solares' }); } for (var i = 0; i < minigames.length; i++) { var mg = minigames[i]; var button = LK.getAsset('plantButton', { anchorX: 0.5, anchorY: 0.5, x: 300 + i % 4 * 200, y: 900 + Math.floor(i / 4) * 200, scaleX: 1.5, scaleY: 1.5 }); self.addChild(button); var nameText = new Text2(mg.name, { size: 25, fill: 0xFFFFFF }); nameText.anchor.set(0.5, 0.5); nameText.x = 300 + i % 4 * 200; nameText.y = 1050 + Math.floor(i / 4) * 200; self.addChild(nameText); var descText = new Text2(mg.desc, { size: 18, fill: 0xFFFFFF }); descText.anchor.set(0.5, 0.5); descText.x = 300 + i % 4 * 200; descText.y = 1100 + Math.floor(i / 4) * 200; self.addChild(descText); button.minigameType = mg.type; button.down = function (x, y, obj) { self.startMinigame(obj.minigameType); }; } // Skip button var skipButton = LK.getAsset('restartButton', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1400, scaleX: 1.2, scaleY: 1.2 }); self.addChild(skipButton); var skipText = new Text2('Saltar', { size: 40, fill: 0xFFFFFF }); skipText.anchor.set(0.5, 0.5); skipText.x = 1024; skipText.y = 1500; self.addChild(skipText); skipButton.down = function (x, y, obj) { self.destroy(); startLevel(currentLevel); }; self.startMinigame = function (type) { self.destroy(); if (type === 'defenseSwap') { currentMinigame = new PlantDefenseMinigame(); } else if (type === 'yoZombie') { currentMinigame = new YoZombieMinigame(); } else { currentMinigame = new Minigame(type); } game.addChild(currentMinigame); }; return self; }); var Pea = Container.expand(function () { var self = Container.call(this); var peaGraphics = self.attachAsset('pea', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.damage = 20; self.update = function () { self.x += self.speed; // Spinning animation self.rotation += 0.3; // Trail effect with color change if (LK.ticks % 3 === 0) { tween(self, { tint: 0x00ff00 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { tint: 0xffffff }, { duration: 100, easing: tween.easeIn }); } }); } if (self.x > 2200) { self.destroy(); } }; return self; }); var Plant = Container.expand(function (type) { var self = Container.call(this); self.type = type; self.health = 100; self.maxHealth = 100; self.shootCooldown = 0; self.sunTimer = 0; var plantGraphics; if (type === 'sunflower') { plantGraphics = self.attachAsset('sunflower', { anchorX: 0.5, anchorY: 0.5 }); self.sunGenerateRate = 300; // ticks between sun generation } else if (type === 'peashooter') { plantGraphics = self.attachAsset('peashooter', { anchorX: 0.5, anchorY: 0.5 }); self.shootRate = 90; // ticks between shots } else if (type === 'wallnut') { plantGraphics = self.attachAsset('wallnut', { anchorX: 0.5, anchorY: 0.5 }); self.health = 300; self.maxHealth = 300; } else if (type === 'potatomine') { plantGraphics = self.attachAsset('potatomine', { anchorX: 0.5, anchorY: 0.5 }); self.health = 50; self.maxHealth = 50; self.exploded = false; } else if (type === 'carnivorousplant') { plantGraphics = self.attachAsset('carnivorousplant', { anchorX: 0.5, anchorY: 0.5 }); self.health = 200; self.maxHealth = 200; self.attackCooldown = 0; self.attackRange = 100; } else if (type === 'doomshroom') { plantGraphics = self.attachAsset('doomshroom', { anchorX: 0.5, anchorY: 0.5 }); self.health = 100; self.maxHealth = 100; self.exploded = false; self.armingTime = 900; // 15 seconds to arm self.currentArmingTime = 0; } else if (type === 'repeater') { plantGraphics = self.attachAsset('repeater', { anchorX: 0.5, anchorY: 0.5 }); self.shootRate = 90; // Same as peashooter } else if (type === 'threepeater') { plantGraphics = self.attachAsset('threepeater', { anchorX: 0.5, anchorY: 0.5 }); self.shootRate = 120; // Slightly slower due to triple shot } else if (type === 'spikeweed') { plantGraphics = self.attachAsset('spikeweed', { anchorX: 0.5, anchorY: 0.5 }); self.health = 300; self.maxHealth = 300; } else if (type === 'splitpeater') { plantGraphics = self.attachAsset('splitpeater', { anchorX: 0.5, anchorY: 0.5 }); self.shootRate = 120; // Similar to threepeater } else if (type === 'lilypad') { plantGraphics = self.attachAsset('lilypad', { anchorX: 0.5, anchorY: 0.5 }); self.health = 50; self.maxHealth = 50; } else if (type === 'puffshroom') { plantGraphics = self.attachAsset('puffshroom', { anchorX: 0.5, anchorY: 0.5 }); self.shootRate = 60; // Faster than peashooter but shorter range self.range = 300; // Limited range } else if (type === 'sunshroom') { plantGraphics = self.attachAsset('sunshroom', { anchorX: 0.5, anchorY: 0.5 }); self.sunGenerateRate = 150; // Faster than sunflower but produces less self.sunValue = 15; // Produces less sun than sunflower } else if (type === 'fumeshroom') { plantGraphics = self.attachAsset('fumeshroom', { anchorX: 0.5, anchorY: 0.5 }); self.shootRate = 120; // Slower but affects multiple zombies } else if (type === 'gravebuster') { plantGraphics = self.attachAsset('gravebuster', { anchorX: 0.5, anchorY: 0.5 }); self.health = 1; // Dies after one use self.maxHealth = 1; } else if (type === 'hypnoshroom') { plantGraphics = self.attachAsset('hypnoshroom', { anchorX: 0.5, anchorY: 0.5 }); self.health = 50; self.maxHealth = 50; } else if (type === 'scaredyshroom') { plantGraphics = self.attachAsset('scaredyshroom', { anchorX: 0.5, anchorY: 0.5 }); self.shootRate = 60; } else if (type === 'iceshroom') { plantGraphics = self.attachAsset('iceshroom', { anchorX: 0.5, anchorY: 0.5 }); self.health = 1; self.maxHealth = 1; } else if (type === 'melonpulta') { plantGraphics = self.attachAsset('melonpulta', { anchorX: 0.5, anchorY: 0.5 }); self.shootRate = 180; // Slower but more powerful self.projectileDamage = 80; // High damage } else if (type === 'coltapulta') { plantGraphics = self.attachAsset('coltapulta', { anchorX: 0.5, anchorY: 0.5 }); self.shootRate = 240; // Slower rate self.projectileDamage = 60; // Medium damage } else if (type === 'lanzamaiz') { plantGraphics = self.attachAsset('lanzamaiz', { anchorX: 0.5, anchorY: 0.5 }); self.shootRate = 150; // Fast rate self.projectileDamage = 40; // Lower damage but faster } else if (type === 'pot') { plantGraphics = self.attachAsset('pot', { anchorX: 0.5, anchorY: 0.5 }); self.health = 50; self.maxHealth = 50; } self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.destroy(); return true; } return false; }; self.update = function () { // Gentle swaying animation for all plants if (LK.ticks % 240 === Math.floor(self.x) % 240) { tween(self, { rotation: 0.1 }, { duration: 1200, easing: tween.easeInOut, onFinish: function onFinish() { tween(self, { rotation: -0.1 }, { duration: 1200, easing: tween.easeInOut, onFinish: function onFinish() { tween(self, { rotation: 0 }, { duration: 1200, easing: tween.easeInOut }); } }); } }); } if (self.type === 'sunflower') { self.sunTimer++; if (self.sunTimer >= self.sunGenerateRate) { self.sunTimer = 0; var newSun = new Sun(); newSun.x = self.x + Math.random() * 40 - 20; newSun.y = self.y + Math.random() * 40 - 20; // Start invisible and small newSun.alpha = 0; newSun.scaleX = 0.3; newSun.scaleY = 0.3; game.addChild(newSun); suns.push(newSun); // Sun appearance animation with bounce tween(newSun, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { tween(newSun, { scaleX: 1.0, scaleY: 1.0 }, { duration: 200, easing: tween.bounceOut }); } }); } } else if (self.type === 'peashooter') { self.shootCooldown++; if (self.shootCooldown >= self.shootRate) { // Check if there's a zombie in this row var myRow = Math.floor((self.y - 500) / 140); var hasZombieInRow = false; for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var zombieRow = Math.floor((zombie.y - 500) / 140); if (zombieRow === myRow && zombie.x > self.x) { hasZombieInRow = true; break; } } if (hasZombieInRow) { self.shootCooldown = 0; // Fisheye shooting effect tween(self, { scaleX: 1.3, scaleY: 0.7 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.easeIn }); } }); var pea = new Pea(); pea.x = self.x + 60; pea.y = self.y; game.addChild(pea); peas.push(pea); LK.getSound('shoot').play(); } } } else if (self.type === 'repeater') { self.shootCooldown++; if (self.shootCooldown >= self.shootRate) { // Check if there's a zombie in this row var myRow = Math.floor((self.y - 500) / 140); var hasZombieInRow = false; for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var zombieRow = Math.floor((zombie.y - 500) / 140); if (zombieRow === myRow && zombie.x > self.x) { hasZombieInRow = true; break; } } if (hasZombieInRow) { self.shootCooldown = 0; // Fisheye shooting effect tween(self, { scaleX: 1.3, scaleY: 0.7 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.easeIn }); } }); // Shoot two peas with slight delay var pea1 = new Pea(); pea1.x = self.x + 60; pea1.y = self.y; game.addChild(pea1); peas.push(pea1); // Second pea with slight delay LK.setTimeout(function () { var pea2 = new Pea(); pea2.x = self.x + 60; pea2.y = self.y; game.addChild(pea2); peas.push(pea2); }, 150); LK.getSound('shoot').play(); } } } else if (self.type === 'threepeater') { self.shootCooldown++; if (self.shootCooldown >= self.shootRate) { // Check if there's a zombie in any of the three rows (current, above, below) var myRow = Math.floor((self.y - 500) / 140); var hasZombieInAnyRow = false; for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var zombieRow = Math.floor((zombie.y - 500) / 140); if ((zombieRow === myRow || zombieRow === myRow - 1 || zombieRow === myRow + 1) && zombie.x > self.x) { hasZombieInAnyRow = true; break; } } if (hasZombieInAnyRow) { self.shootCooldown = 0; // Fisheye shooting effect tween(self, { scaleX: 1.3, scaleY: 0.7 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.easeIn }); } }); // Shoot three peas - center, above, below var centerPea = new Pea(); centerPea.x = self.x + 60; centerPea.y = self.y; game.addChild(centerPea); peas.push(centerPea); // Above pea (if row exists) if (myRow > 0) { var abovePea = new Pea(); abovePea.x = self.x + 60; abovePea.y = self.y - 140; game.addChild(abovePea); peas.push(abovePea); } // Below pea (if row exists) if (myRow < gridRows - 1) { var belowPea = new Pea(); belowPea.x = self.x + 60; belowPea.y = self.y + 140; game.addChild(belowPea); peas.push(belowPea); } LK.getSound('shoot').play(); } } } else if (self.type === 'potatomine' && !self.exploded) { // Check for zombies stepping on the mine var myRow = Math.floor((self.y - 500) / 140); for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var zombieRow = Math.floor((zombie.y - 500) / 140); if (zombieRow === myRow && Math.abs(self.x - zombie.x) < 80) { // Explode! self.exploded = true; LK.getSound('explode').play(); // Create explosion visual effect tween(self, { scaleX: 3, scaleY: 3, alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { for (var k = plants.length - 1; k >= 0; k--) { if (plants[k] === self) { plants.splice(k, 1); self.destroy(); break; } } } }); LK.effects.flashObject(self, 0xFF4500, 500); // Damage all zombies in the area for (var j = zombies.length - 1; j >= 0; j--) { var targetZombie = zombies[j]; var distance = Math.sqrt(Math.pow(self.x - targetZombie.x, 2) + Math.pow(self.y - targetZombie.y, 2)); if (distance < 120) { if (targetZombie.takeDamage(200)) { zombiesKilled++; // Increment kill counter targetZombie.destroy(); zombies.splice(j, 1); } } } break; } } } else if (self.type === 'carnivorousplant') { self.attackCooldown++; if (self.attackCooldown >= 180) { // Attack every 3 seconds var myRow = Math.floor((self.y - 500) / 140); var closestZombie = null; var closestDistance = Infinity; for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var zombieRow = Math.floor((zombie.y - 500) / 140); if (zombieRow === myRow) { var distance = Math.abs(self.x - zombie.x); if (distance < self.attackRange && distance < closestDistance) { closestDistance = distance; closestZombie = zombie; } } } if (closestZombie) { self.attackCooldown = 0; // Attack animation tween(self, { scaleX: 1.5, scaleY: 1.5 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeIn }); } }); if (closestZombie.takeDamage(100)) { zombiesKilled++; // Increment kill counter for (var j = zombies.length - 1; j >= 0; j--) { if (zombies[j] === closestZombie) { closestZombie.destroy(); zombies.splice(j, 1); break; } } } } } } else if (self.type === 'doomshroom' && !self.exploded) { self.currentArmingTime++; if (self.currentArmingTime >= self.armingTime) { // Doom shroom is now armed and explodes self.exploded = true; LK.getSound('explode').play(); // Create massive explosion visual effect tween(self, { scaleX: 8, scaleY: 8, alpha: 0 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { for (var k = plants.length - 1; k >= 0; k--) { if (plants[k] === self) { plants.splice(k, 1); self.destroy(); break; } } } }); LK.effects.flashScreen(0x4B0082, 1000); // Damage all zombies on screen for (var j = zombies.length - 1; j >= 0; j--) { var targetZombie = zombies[j]; if (targetZombie.takeDamage(300)) { zombiesKilled++; // Increment kill counter targetZombie.destroy(); zombies.splice(j, 1); } } } } else if (self.type === 'spikeweed') { // Damage zombies walking over spikeweed - but don't stop them var myRow = Math.floor((self.y - 500) / 140); for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var zombieRow = Math.floor((zombie.y - 500) / 140); if (zombieRow === myRow && Math.abs(self.x - zombie.x) < 70) { // Only damage zombie if it hasn't been damaged by this spikeweed recently if (!zombie.lastSpikeweeedDamageTime || LK.ticks - zombie.lastSpikeweeedDamageTime > 30) { zombie.lastSpikeweeedDamageTime = LK.ticks; if (zombie.takeDamage(20)) { zombiesKilled++; // Increment kill counter for (var j = zombies.length - 1; j >= 0; j--) { if (zombies[j] === zombie) { zombie.destroy(); zombies.splice(j, 1); break; } } } // Damage spikeweed when used if (self.takeDamage(1)) { for (var k = plants.length - 1; k >= 0; k--) { if (plants[k] === self) { plants.splice(k, 1); self.destroy(); break; } } } } } } } else if (self.type === 'splitpeater') { self.shootCooldown++; if (self.shootCooldown >= self.shootRate) { // Check if there's a zombie in front or behind var myRow = Math.floor((self.y - 500) / 140); var hasZombieInFront = false; var hasZombieBehind = false; for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var zombieRow = Math.floor((zombie.y - 500) / 140); if (zombieRow === myRow) { if (zombie.x > self.x) { hasZombieInFront = true; } else if (zombie.x < self.x) { hasZombieBehind = true; } } } if (hasZombieInFront || hasZombieBehind) { self.shootCooldown = 0; // Fisheye shooting effect tween(self, { scaleX: 1.3, scaleY: 0.7 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.easeIn }); } }); // Shoot forward if zombie in front if (hasZombieInFront) { var forwardPea = new Pea(); forwardPea.x = self.x + 60; forwardPea.y = self.y; game.addChild(forwardPea); peas.push(forwardPea); } // Shoot backward if zombie behind if (hasZombieBehind) { var backwardPea = new Pea(); backwardPea.x = self.x - 60; backwardPea.y = self.y; backwardPea.speed = -8; // Negative speed for backward movement game.addChild(backwardPea); peas.push(backwardPea); } LK.getSound('shoot').play(); } } } else if (self.type === 'puffshroom') { self.shootCooldown++; if (self.shootCooldown >= self.shootRate) { var myRow = Math.floor((self.y - 500) / 140); var hasZombieInRange = false; for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var zombieRow = Math.floor((zombie.y - 500) / 140); if (zombieRow === myRow && zombie.x > self.x && zombie.x - self.x < self.range) { hasZombieInRange = true; break; } } if (hasZombieInRange) { self.shootCooldown = 0; var pea = new Pea(); pea.x = self.x + 60; pea.y = self.y; game.addChild(pea); peas.push(pea); LK.getSound('shoot').play(); } } } else if (self.type === 'sunshroom') { self.sunTimer++; if (self.sunTimer >= self.sunGenerateRate) { self.sunTimer = 0; var newSun = new Sun(); newSun.value = self.sunValue; newSun.x = self.x + Math.random() * 40 - 20; newSun.y = self.y + Math.random() * 40 - 20; game.addChild(newSun); suns.push(newSun); } } else if (self.type === 'gravebuster') { // Check for tombstones to destroy var myRow = Math.floor((self.y - gridStartY + cellSize / 2) / cellSize); var myCol = Math.floor((self.x - gridStartX + cellSize / 2) / cellSize); for (var i = tombstones.length - 1; i >= 0; i--) { var tombstone = tombstones[i]; var tombstoneRow = Math.floor((tombstone.y - gridStartY + cellSize / 2) / cellSize); var tombstoneCol = Math.floor((tombstone.x - gridStartX + cellSize / 2) / cellSize); if (tombstoneRow === myRow && tombstoneCol === myCol) { // Destroy tombstone tombstone.destroy(); tombstones.splice(i, 1); // Destroy gravebuster after use for (var j = plants.length - 1; j >= 0; j--) { if (plants[j] === self) { plants.splice(j, 1); self.destroy(); break; } } break; } } } else if (self.type === 'scaredyshroom') { self.shootCooldown++; if (self.shootCooldown >= self.shootRate) { var myRow = Math.floor((self.y - 500) / 140); var hasZombieInRow = false; var zombieTooClose = false; for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var zombieRow = Math.floor((zombie.y - 500) / 140); if (zombieRow === myRow && zombie.x > self.x) { hasZombieInRow = true; if (zombie.x - self.x < 200) { zombieTooClose = true; } break; } } if (hasZombieInRow && !zombieTooClose) { self.shootCooldown = 0; var pea = new Pea(); pea.x = self.x + 60; pea.y = self.y; game.addChild(pea); peas.push(pea); LK.getSound('shoot').play(); } } } else if (self.type === 'melonpulta' || self.type === 'coltapulta') { self.shootCooldown++; if (self.shootCooldown >= self.shootRate) { // Check if there's a zombie in this row var myRow = Math.floor((self.y - 500) / 140); var hasZombieInRow = false; for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var zombieRow = Math.floor((zombie.y - 500) / 140); if (zombieRow === myRow && zombie.x > self.x) { hasZombieInRow = true; break; } } if (hasZombieInRow) { self.shootCooldown = 0; // Catapult shooting animation tween(self, { scaleX: 1.3, scaleY: 0.7 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.easeIn }); } }); var pea = new Pea(); pea.damage = self.projectileDamage; // Use custom damage pea.x = self.x + 60; pea.y = self.y; game.addChild(pea); peas.push(pea); LK.getSound('shoot').play(); } } } else if (self.type === 'lanzamaiz') { self.shootCooldown++; if (self.shootCooldown >= self.shootRate) { // Check if there's a zombie in this row var myRow = Math.floor((self.y - 500) / 140); var hasZombieInRow = false; for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var zombieRow = Math.floor((zombie.y - 500) / 140); if (zombieRow === myRow && zombie.x > self.x) { hasZombieInRow = true; break; } } if (hasZombieInRow) { self.shootCooldown = 0; // Catapult shooting animation tween(self, { scaleX: 1.3, scaleY: 0.7 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.easeIn }); } }); var corn = new Corn(); corn.damage = self.projectileDamage; // Use custom damage corn.x = self.x + 60; corn.y = self.y; game.addChild(corn); corns.push(corn); LK.getSound('shoot').play(); } } } }; return self; }); var PlantDefenseMinigame = Container.expand(function () { var self = Container.call(this); self.completed = false; self.timeLeft = 60; // 60 seconds to plan defense self.score = 0; self.targetScore = 50; // Need to survive 50 zombies self.zombies = []; self.plants = []; self.selectedPlantType = null; self.planningPhase = true; self.sunPoints = 10000; // Start with 10,000 suns // Available plants (no sun mushrooms allowed) self.availablePlants = ['wallnut', 'carnivorousplant', 'peashooter', 'repeater', 'threepeater', 'spikeweed', 'doomshroom', 'potatomine']; // Create background var bg = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 15, scaleY: 20, alpha: 0.4, tint: 0x1a1a2e // Dark night background }); self.addChild(bg); // Title text var titleText = new Text2('¡PLANEA LA DEFENSA!', { size: 80, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 200; self.addChild(titleText); // Instructions text var instructionText = new Text2('Tienes 60 segundos y 10,000 soles para preparar tu defensa', { size: 40, fill: 0xFFFFFF }); instructionText.anchor.set(0.5, 0.5); instructionText.x = 1024; instructionText.y = 280; self.addChild(instructionText); // Timer text var timerText = new Text2('Tiempo de planificación: 60', { size: 50, fill: 0xFFFF00 }); timerText.anchor.set(0.5, 0.5); timerText.x = 1024; timerText.y = 340; self.addChild(timerText); // Sun display var sunText = new Text2('Soles: 10000', { size: 50, fill: 0xFFFF00 }); sunText.anchor.set(0.5, 0.5); sunText.x = 1024; sunText.y = 400; self.addChild(sunText); // Create simple 8x5 grid for planting self.gridStartX = 500; self.gridStartY = 600; self.gridCols = 8; self.gridRows = 5; self.cellSize = 120; self.plantGrid = []; // Initialize plant grid for (var row = 0; row < self.gridRows; row++) { self.plantGrid[row] = []; for (var col = 0; col < self.gridCols; col++) { self.plantGrid[row][col] = null; } } // Draw grid cells for (var row = 0; row < self.gridRows; row++) { for (var col = 0; col < self.gridCols; col++) { var gridCell = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, x: self.gridStartX + col * self.cellSize, y: self.gridStartY + row * self.cellSize, alpha: 0.3, tint: 0x228B22 }); self.addChild(gridCell); } } // Create plant selection buttons self.plantButtons = []; for (var i = 0; i < self.availablePlants.length; i++) { var plantType = self.availablePlants[i]; var button = LK.getAsset(plantType, { anchorX: 0.5, anchorY: 0.5, x: 200 + i % 4 * 150, y: 1200 + Math.floor(i / 4) * 150, scaleX: 0.6, scaleY: 0.6 }); self.addChild(button); self.plantButtons.push(button); // Add click handler button.plantType = plantType; button.down = function (x, y, obj) { if (self.planningPhase) { self.selectedPlantType = obj.plantType; // Visual feedback for (var j = 0; j < self.plantButtons.length; j++) { self.plantButtons[j].tint = 0xffffff; } obj.tint = 0x808080; } }; } // Zombie spawn during battle phase self.zombieSpawnTimer = 0; self.zombiesSpawned = 0; self.update = function () { if (self.completed) return; if (self.planningPhase) { self.timeLeft -= 1 / 60; timerText.setText('Tiempo de planificación: ' + Math.ceil(self.timeLeft)); sunText.setText('Soles: ' + self.sunPoints); if (self.timeLeft <= 0) { // Switch to battle phase self.planningPhase = false; self.timeLeft = 120; // 2 minutes to survive titleText.setText('¡DEFIENDE!'); instructionText.setText('Sobrevive a 50 zombies'); timerText.setText('Tiempo restante: 120'); sunText.visible = false; // Hide plant buttons for (var i = 0; i < self.plantButtons.length; i++) { self.plantButtons[i].visible = false; } } } else { // Battle phase self.timeLeft -= 1 / 60; timerText.setText('Tiempo restante: ' + Math.ceil(self.timeLeft)); // Spawn zombies self.zombieSpawnTimer++; if (self.zombieSpawnTimer >= 120 && self.zombiesSpawned < 50) { // Every 2 seconds self.zombieSpawnTimer = 0; self.spawnZombie(); self.zombiesSpawned++; } // Check win condition if (self.zombiesSpawned >= 50 && self.zombies.length === 0) { self.endMinigame(true); return; } // Check lose condition if (self.timeLeft <= 0) { self.endMinigame(false); return; } // Update plant vs zombie combat self.updateCombat(); } }; self.spawnZombie = function () { var zombie = LK.getAsset('zombie', { anchorX: 0.5, anchorY: 0.5, x: 1400, y: self.gridStartY + Math.floor(Math.random() * self.gridRows) * self.cellSize, scaleX: 0.8, scaleY: 0.8 }); self.addChild(zombie); zombie.health = 100; zombie.speed = -1; zombie.row = Math.floor((zombie.y - self.gridStartY + self.cellSize / 2) / self.cellSize); self.zombies.push(zombie); zombie.update = function () { zombie.x += zombie.speed; if (zombie.x < 400) { // Zombie reached the left side - game over self.endMinigame(false); } }; }; self.updateCombat = function () { // Simple combat system for (var i = self.zombies.length - 1; i >= 0; i--) { var zombie = self.zombies[i]; var zombieDestroyed = false; // Check for plant attacks for (var j = 0; j < self.plants.length; j++) { var plant = self.plants[j]; if (plant.row === zombie.row && plant.x < zombie.x && zombie.x - plant.x < 300) { // Plant can attack zombie if (!plant.lastAttack || LK.ticks - plant.lastAttack > 60) { plant.lastAttack = LK.ticks; zombie.health -= 25; // Visual attack effect tween(plant, { scaleX: 1.2, scaleY: 1.2, tint: 0xff4444 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(plant, { scaleX: 0.8, scaleY: 0.8, tint: 0xffffff }, { duration: 100, easing: tween.easeIn }); } }); if (zombie.health <= 0) { zombiesKilled++; zombie.destroy(); self.zombies.splice(i, 1); self.score++; zombieDestroyed = true; break; } } } } if (!zombieDestroyed) { // Check if zombie reached end if (zombie.x <= 400) { zombie.destroy(); self.zombies.splice(i, 1); // Penalty for letting zombie through self.score = Math.max(0, self.score - 1); } } } }; self.endMinigame = function (success) { self.completed = true; var resultText = new Text2(success ? '¡DEFENSA EXITOSA!' : '¡DEFENSA FALLIDA!', { size: 100, fill: success ? 0x00FF00 : 0xFF0000 }); resultText.anchor.set(0.5, 0.5); resultText.x = 1024; resultText.y = 1366; self.addChild(resultText); var rewardText = new Text2('', { size: 60, fill: 0xFFFF00 }); rewardText.anchor.set(0.5, 0.5); rewardText.x = 1024; rewardText.y = 1500; self.addChild(rewardText); if (success) { var reward = 500; sunPoints += reward; rewardText.setText('¡+' + reward + ' soles de recompensa!'); updateSunDisplay(); } else { rewardText.setText('¡Mejora tu estrategia!'); } var continueText = new Text2('Toca para continuar', { size: 40, fill: 0xFFFFFF }); continueText.anchor.set(0.5, 0.5); continueText.x = 1024; continueText.y = 1650; self.addChild(continueText); self.down = function (x, y, obj) { self.destroy(); currentMinigame = null; startLevel(19); }; }; // Handle plant placement during planning phase self.down = function (x, y, obj) { if (!self.planningPhase || !self.selectedPlantType) return; // Check if click is on grid var col = Math.floor((x - self.gridStartX + self.cellSize / 2) / self.cellSize); var row = Math.floor((y - self.gridStartY + self.cellSize / 2) / self.cellSize); if (col >= 0 && col < self.gridCols && row >= 0 && row < self.gridRows) { // Check if position is empty and player has enough suns var plantCost = plantCosts[self.selectedPlantType] || 50; if (!self.plantGrid[row][col] && self.sunPoints >= plantCost) { // Place plant var plant = LK.getAsset(self.selectedPlantType, { anchorX: 0.5, anchorY: 0.5, x: self.gridStartX + col * self.cellSize, y: self.gridStartY + row * self.cellSize, scaleX: 0.8, scaleY: 0.8 }); self.addChild(plant); plant.row = row; plant.col = col; self.plantGrid[row][col] = plant; self.plants.push(plant); self.sunPoints -= plantCost; LK.getSound('plant').play(); } } }; return self; }); var PlantSelector = Container.expand(function () { var self = Container.call(this); self.completed = false; // Create background var bg = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 15, scaleY: 20, alpha: 0.6, tint: 0x2F4F2F }); self.addChild(bg); // Title var titleText = new Text2('¡ELIGE TUS PLANTAS!', { size: 100, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 200; self.addChild(titleText); var subtitleText = new Text2('Selecciona hasta 8 plantas para este nivel', { size: 50, fill: 0xFFFF00 }); subtitleText.anchor.set(0.5, 0.5); subtitleText.x = 1024; subtitleText.y = 280; self.addChild(subtitleText); // All available plants - show all plants from all modes self.allPlants = ['sunflower', 'peashooter', 'wallnut', 'potatomine', 'carnivorousplant', 'doomshroom', 'repeater', 'threepeater', 'spikeweed', 'splitpeater', 'lilypad', 'puffshroom', 'sunshroom', 'fumeshroom', 'gravebuster', 'hypnoshroom', 'scaredyshroom', 'iceshroom', 'melonpulta', 'coltapulta', 'lanzamaiz', 'pot']; self.selectedPlants = []; self.selectedDisplays = []; // Store the ordered display plants self.maxPlants = 8; // Selected plants display area at top var selectedAreaText = new Text2('PLANTAS SELECCIONADAS:', { size: 40, fill: 0xFFFF00 }); selectedAreaText.anchor.set(0.5, 0.5); selectedAreaText.x = 1024; selectedAreaText.y = 350; self.addChild(selectedAreaText); // Create plant buttons in a grid showing ALL plants var buttonsPerRow = 6; var buttonSize = 100; var startX = 1024 - buttonsPerRow * buttonSize / 2 + buttonSize / 2; var startY = 700; for (var i = 0; i < self.allPlants.length; i++) { var plantType = self.allPlants[i]; var row = Math.floor(i / buttonsPerRow); var col = i % buttonsPerRow; var button = LK.getAsset(plantType, { anchorX: 0.5, anchorY: 0.5, x: startX + col * buttonSize, y: startY + row * buttonSize, scaleX: 0.7, scaleY: 0.7 }); self.addChild(button); // Add cost label var costText = new Text2(plantCosts[plantType].toString(), { size: 16, fill: 0xFFFFFF }); costText.anchor.set(0.5, 0); costText.x = startX + col * buttonSize; costText.y = startY + row * buttonSize + 40; self.addChild(costText); button.plantType = plantType; button.selected = false; button.down = function (x, y, obj) { // Use 'this' to refer to the button since obj might be undefined var buttonRef = this; if (buttonRef.selected) { // Deselect - remove from selectedPlants and destroy corresponding display buttonRef.selected = false; buttonRef.tint = 0xffffff; var index = self.selectedPlants.indexOf(buttonRef.plantType); if (index > -1) { self.selectedPlants.splice(index, 1); // Remove and destroy the display element if (self.selectedDisplays[index]) { self.selectedDisplays[index].destroy(); } self.selectedDisplays.splice(index, 1); // Reposition remaining displays self.repositionSelectedDisplays(); } } else if (self.selectedPlants.length < self.maxPlants) { // Select - add to selectedPlants and create display buttonRef.selected = true; buttonRef.tint = 0x00ff00; self.selectedPlants.push(buttonRef.plantType); // Create display element in the selected area var selectedDisplay = LK.getAsset(buttonRef.plantType, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); self.addChild(selectedDisplay); self.selectedDisplays.push(selectedDisplay); self.repositionSelectedDisplays(); } }; } // Function to reposition selected plant displays in order self.repositionSelectedDisplays = function () { var displayStartX = 1024 - (self.selectedDisplays.length - 1) * 80 / 2; var displayY = 450; for (var i = 0; i < self.selectedDisplays.length; i++) { if (self.selectedDisplays[i]) { self.selectedDisplays[i].x = displayStartX + i * 80; self.selectedDisplays[i].y = displayY; } } }; // Selected count display var countText = new Text2('Seleccionadas: 0/' + self.maxPlants, { size: 40, fill: 0xFFFFFF }); countText.anchor.set(0.5, 0.5); countText.x = 1024; countText.y = 520; self.addChild(countText); // Continue button var continueButton = LK.getAsset('plantButton', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1600, scaleX: 2, scaleY: 2 }); self.addChild(continueButton); var continueText = new Text2('CONTINUAR', { size: 40, fill: 0xFFFFFF }); continueText.anchor.set(0.5, 0.5); continueText.x = 1024; continueText.y = 1650; self.addChild(continueText); continueButton.down = function (x, y, obj) { if (self.selectedPlants.length >= 3) { // Minimum 3 plants required self.completed = true; self.destroy(); // Continue with level using selected plants startLevelWithSelectedPlants(currentLevel, self.selectedPlants); } }; self.update = function () { countText.setText('Seleccionadas: ' + self.selectedPlants.length + '/' + self.maxPlants); continueButton.alpha = self.selectedPlants.length >= 3 ? 1 : 0.5; }; return self; }); var RobotBoss = Container.expand(function () { var self = Container.call(this); var bossGraphics = self.attachAsset('robotBoss', { anchorX: 0.5, anchorY: 0.5 }); self.health = 1000; self.maxHealth = 1000; self.attackTimer = 0; self.attackRate = 240; // 4 seconds between attacks self.mouthOpen = false; self.takeDamage = function (damage) { self.health -= damage; // Flash red when taking damage tween(self, { tint: 0xff0000 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { tint: 0xffffff }, { duration: 200, easing: tween.easeIn }); } }); if (self.health <= 0) { // Boss death animation tween(self, { scaleX: 2, scaleY: 2, alpha: 0, rotation: Math.PI }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); // Trigger win condition if (gameState === 'playing') { gameState = 'won'; LK.showYouWin(); } } }); return true; } return false; }; self.update = function () { self.attackTimer++; if (self.attackTimer >= self.attackRate) { self.attackTimer = 0; // Randomly choose between fireball attack or zombie spawn if (Math.random() < 0.6) { // Show red circle where fireball will land at random position var landingIndicators = []; var fireballTargets = []; // Store target position for fireball // Generate random target position within grid area var targetX = gridStartX + Math.random() * (gridCols * cellSize); var targetY = gridStartY + Math.random() * (gridRows * cellSize); fireballTargets.push({ x: targetX, y: targetY }); var indicator = LK.getAsset('fireball', { anchorX: 0.5, anchorY: 0.5, x: targetX, y: targetY, scaleX: 2, scaleY: 2, tint: 0xff0000, alpha: 0.7 }); game.addChild(indicator); landingIndicators.push(indicator); // Open mouth animation for fireball attack self.mouthOpen = true; tween(self, { scaleY: 1.3, tint: 0xff4500 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Remove landing indicator for (var k = 0; k < landingIndicators.length; k++) { landingIndicators[k].destroy(); } // Spit single fireball toward random target position from boss mouth var fireball = new Fireball(); // Set initial position at boss mouth fireball.x = self.x - 80; // Spawn from mouth area (further left from center) fireball.y = self.y - 50; // Spawn from mouth height (upper part of boss) // Calculate trajectory toward target var targetX = fireballTargets[0].x; var targetY = fireballTargets[0].y; var deltaX = targetX - fireball.x; var deltaY = targetY - fireball.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); // Set fireball direction and speed toward target fireball.speedX = deltaX / distance * 8; // Horizontal speed component fireball.speedY = deltaY / distance * 8; // Vertical speed component game.addChild(fireball); fireballs.push(fireball); // Close mouth tween(self, { scaleY: 1, tint: 0xffffff }, { duration: 300, easing: tween.easeIn, onFinish: function onFinish() { self.mouthOpen = false; } }); } }); } else { // Spawn zombies including Zombistein var zombieTypes = ['normal', 'fast', 'snailBucket', 'zombistein']; var randomType = zombieTypes[Math.floor(Math.random() * zombieTypes.length)]; var newZombie = new Zombie(randomType); newZombie.x = 2100; newZombie.y = gridStartY + Math.floor(Math.random() * gridRows) * cellSize; game.addChild(newZombie); zombies.push(newZombie); // Boss spawn effect tween(self, { tint: 0x8000ff }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { tint: 0xffffff }, { duration: 200, easing: tween.easeIn }); } }); } } // Update boss health display if (bossHealthDisplay) { bossHealthDisplay.setText('Boss Health: ' + self.health + '/' + self.maxHealth); } }; return self; }); var Sun = Container.expand(function () { var self = Container.call(this); var sunGraphics = self.attachAsset('sun', { anchorX: 0.5, anchorY: 0.5 }); self.value = 25; self.collected = false; self.lifeTimer = 0; self.maxLifeTime = 300; // 5 seconds at 60 FPS self.update = function () { if (!self.collected) { self.lifeTimer++; // Gentle floating animation if (self.lifeTimer % 120 === 0) { tween(self, { y: self.y - 15 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(self, { y: self.y + 15 }, { duration: 1000, easing: tween.easeInOut }); } }); } // Gentle pulsing if (self.lifeTimer % 180 === 0) { tween(self, { scaleX: 1.1, scaleY: 1.1 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { tween(self, { scaleX: 1.0, scaleY: 1.0 }, { duration: 800, easing: tween.easeInOut }); } }); } if (self.lifeTimer >= self.maxLifeTime) { self.collected = true; // Fade out animation before destroying tween(self, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 300, easing: tween.easeIn, onFinish: function onFinish() { self.destroy(); } }); } } }; self.down = function (x, y, obj) { if (!self.collected) { self.collected = true; sunPoints += self.value; updateSunDisplay(); LK.getSound('collect').play(); // Collection animation - fly to sun display and shrink var targetX = sunDisplay.x; var targetY = sunDisplay.y; tween(self, { x: targetX, y: targetY, scaleX: 0.3, scaleY: 0.3, alpha: 0.8 }, { duration: 400, easing: tween.easeIn, onFinish: function onFinish() { // Final shrink and fade tween(self, { scaleX: 0, scaleY: 0, alpha: 0 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); } }); } }; return self; }); var Tombstone = Container.expand(function () { var self = Container.call(this); var tombstoneGraphics = self.attachAsset('tombstone', { anchorX: 0.5, anchorY: 0.5 }); self.health = 100; self.maxHealth = 100; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.destroy(); return true; } return false; }; return self; }); var YoZombieMinigame = Container.expand(function () { var self = Container.call(this); self.completed = false; self.timeLeft = -1; // No time limit self.score = 0; self.targetScore = 50; // Need to destroy 50 plants self.zombies = []; self.plants = []; self.zombieTypes = ['normal', 'fast', 'snailBucket', 'cart']; self.selectedZombieType = 'normal'; self.sunPoints = 200; // Start with suns for zombie spawning // Zombie costs in suns self.zombieCosts = { normal: 25, fast: 50, snailBucket: 75, cart: 100 }; // Create background var bg = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 15, scaleY: 20, alpha: 0.4, tint: 0x4a0e4e // Dark purple background }); self.addChild(bg); // Title text var titleText = new Text2('¡YO ZOMBIE!', { size: 100, fill: 0xFF0000 }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 250; self.addChild(titleText); // Instructions text var instructionText = new Text2('¡Lanza zombies para destruir 50 plantas!', { size: 50, fill: 0xFFFFFF }); instructionText.anchor.set(0.5, 0.5); instructionText.x = 1024; instructionText.y = 350; self.addChild(instructionText); // Timer text var timerText = new Text2('Sin límite de tiempo', { size: 60, fill: 0xFFFF00 }); timerText.anchor.set(0.5, 0.5); timerText.x = 1024; timerText.y = 450; self.addChild(timerText); // Score text var scoreText = new Text2('Plantas destruidas: 0/50', { size: 50, fill: 0x00FF00 }); scoreText.anchor.set(0.5, 0.5); scoreText.x = 1024; scoreText.y = 550; self.addChild(scoreText); // Sun display for zombies var sunText = new Text2('Soles: 200', { size: 50, fill: 0xFFFF00 }); sunText.anchor.set(0.5, 0.5); sunText.x = 1024; sunText.y = 610; self.addChild(sunText); // Zombie selection buttons - positioned like plant seed bar var zombieButtons = []; for (var i = 0; i < self.zombieTypes.length; i++) { var zombieType = self.zombieTypes[i]; var assetName = zombieType === 'normal' ? 'zombie' : zombieType === 'fast' ? 'fastZombie' : zombieType === 'snailBucket' ? 'snailBucket' : 'cartZombie'; var button = LK.getAsset(assetName, { anchorX: 0.5, anchorY: 0.5, x: 200 + i * 100, y: 2100, scaleX: 0.6, scaleY: 0.6 }); self.addChild(button); button.zombieType = zombieType; button.selected = i === 0; if (button.selected) button.tint = 0x00ff00; button.down = function (x, y, obj) { // Only allow selection if player has enough suns var cost = self.zombieCosts[this.zombieType]; if (self.sunPoints >= cost) { // Deselect all buttons for (var j = 0; j < zombieButtons.length; j++) { zombieButtons[j].selected = false; zombieButtons[j].tint = 0xffffff; } // Select this button this.selected = true; this.tint = 0x00ff00; self.selectedZombieType = this.zombieType; } }; zombieButtons.push(button); // Add cost label positioned below button like plant costs var costText = new Text2(self.zombieCosts[zombieType].toString(), { size: 20, fill: 0xFFFF00 }); costText.anchor.set(0.5, 0); costText.x = 200 + i * 100; costText.y = 2140; self.addChild(costText); } // Spawn plants to attack self.spawnPlants = function () { var plantTypes = ['sunflower', 'peashooter', 'wallnut', 'potatomine']; for (var i = 0; i < 60; i++) { // Spawn 60 plants total var plantType = plantTypes[Math.floor(Math.random() * plantTypes.length)]; var plant = LK.getAsset(plantType, { anchorX: 0.5, anchorY: 0.5, x: 300 + Math.random() * 1400, y: 800 + Math.random() * 1200, scaleX: 0.8, scaleY: 0.8 }); self.addChild(plant); plant.health = 100; plant.destroyed = false; self.plants.push(plant); } }; // Spawn additional plants periodically during gameplay self.plantSpawnTimer = 0; self.plantSpawnRate = 300; // 5 seconds self.spawnRandomPlants = function () { var plantTypes = ['sunflower', 'peashooter', 'wallnut', 'potatomine']; // Spawn 3-5 random plants var plantsToSpawn = 3 + Math.floor(Math.random() * 3); for (var i = 0; i < plantsToSpawn; i++) { var plantType = plantTypes[Math.floor(Math.random() * plantTypes.length)]; var plant = LK.getAsset(plantType, { anchorX: 0.5, anchorY: 0.5, x: 300 + Math.random() * 1400, y: 800 + Math.random() * 1200, scaleX: 0.8, scaleY: 0.8 }); self.addChild(plant); plant.health = 100; plant.destroyed = false; self.plants.push(plant); } }; self.update = function () { if (self.completed) return; // No time limit - removed time decrease timerText.setText('Sin límite de tiempo'); scoreText.setText('Plantas destruidas: ' + self.score + '/50'); sunText.setText('Soles: ' + self.sunPoints); // Check win condition if (self.score >= self.targetScore) { self.endMinigame(true); return; } // Spawn random plants periodically self.plantSpawnTimer++; if (self.plantSpawnTimer >= self.plantSpawnRate) { self.plantSpawnTimer = 0; self.spawnRandomPlants(); } // Add suns periodically so players can continue spawning zombies if (LK.ticks % 300 === 0) { // Every 5 seconds self.sunPoints += 25; } // Update zombie vs plant combat for (var i = self.zombies.length - 1; i >= 0; i--) { var zombie = self.zombies[i]; if (zombie.destroyed) continue; // Move zombie toward nearest plant var nearestPlant = null; var nearestDistance = Infinity; for (var j = 0; j < self.plants.length; j++) { var plant = self.plants[j]; if (!plant.destroyed) { var distance = Math.sqrt(Math.pow(zombie.x - plant.x, 2) + Math.pow(zombie.y - plant.y, 2)); if (distance < nearestDistance) { nearestDistance = distance; nearestPlant = plant; } } } if (nearestPlant) { // Move toward plant var deltaX = nearestPlant.x - zombie.x; var deltaY = nearestPlant.y - zombie.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance > 5) { zombie.x += deltaX / distance * zombie.speed; zombie.y += deltaY / distance * zombie.speed; } // Attack plant if close enough if (distance < 70) { // Only damage once per second if (!nearestPlant.lastDamageTime || LK.ticks - nearestPlant.lastDamageTime > 60) { nearestPlant.lastDamageTime = LK.ticks; nearestPlant.health -= zombie.attackDamage; if (nearestPlant.health <= 0 && !nearestPlant.destroyed) { nearestPlant.destroyed = true; self.score++; LK.getSound('explode').play(); // Plant destruction animation tween(nearestPlant, { scaleX: 0, scaleY: 0, alpha: 0, rotation: Math.PI }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { nearestPlant.destroy(); } }); } } } } // Remove zombies that are off screen or destroyed if (zombie.x < -100 || zombie.x > 2200 || zombie.y < -100 || zombie.y > 2800 || zombie.destroyed) { if (!zombie.destroyed) { zombie.destroy(); } self.zombies.splice(i, 1); } } }; self.endMinigame = function (success) { self.completed = true; var resultText = new Text2(success ? '¡ZOMBIES VICTORIOSOS!' : '¡LAS PLANTAS RESISTIERON!', { size: 80, fill: success ? 0x00FF00 : 0xFF0000 }); resultText.anchor.set(0.5, 0.5); resultText.x = 1024; resultText.y = 1400; self.addChild(resultText); var rewardText = new Text2('', { size: 60, fill: 0xFFFF00 }); rewardText.anchor.set(0.5, 0.5); rewardText.x = 1024; rewardText.y = 1550; self.addChild(rewardText); if (success) { var reward = Math.floor(self.score * 20); sunPoints += reward; rewardText.setText('¡+' + reward + ' soles de recompensa!'); updateSunDisplay(); } else { rewardText.setText('¡Entrena más a tus zombies!'); } var continueText = new Text2('Toca para continuar', { size: 40, fill: 0xFFFFFF }); continueText.anchor.set(0.5, 0.5); continueText.x = 1024; continueText.y = 1700; self.addChild(continueText); // Make screen clickable to continue self.down = function (x, y, obj) { self.destroy(); currentMinigame = null; // Continue to level selection showLevelSelection(); }; }; // Handle zombie spawning on click self.down = function (x, y, obj) { if (self.completed) return; // Check if player has enough suns var zombieCost = self.zombieCosts[self.selectedZombieType]; if (self.sunPoints < zombieCost) return; // Deduct sun cost self.sunPoints -= zombieCost; // Spawn zombie at click position var zombie = LK.getAsset(self.selectedZombieType === 'normal' ? 'zombie' : self.selectedZombieType === 'fast' ? 'fastZombie' : self.selectedZombieType === 'snailBucket' ? 'snailBucket' : 'cartZombie', { anchorX: 0.5, anchorY: 0.5, x: x, y: y, scaleX: 0.8, scaleY: 0.8 }); self.addChild(zombie); // Set zombie properties based on type if (self.selectedZombieType === 'fast') { zombie.speed = 3; zombie.health = 60; zombie.attackDamage = 15; } else if (self.selectedZombieType === 'snailBucket') { zombie.speed = 1.5; zombie.health = 250; zombie.attackDamage = 30; } else if (self.selectedZombieType === 'cart') { zombie.speed = 2; zombie.health = 300; zombie.attackDamage = 50; } else { zombie.speed = 2; zombie.health = 100; zombie.attackDamage = 20; } zombie.destroyed = false; self.zombies.push(zombie); LK.getSound('plant').play(); }; // Initialize plants self.spawnPlants(); return self; }); var Zombie = Container.expand(function (type) { var self = Container.call(this); self.type = type || 'normal'; self.health = 100; self.maxHealth = 100; self.speed = -1; self.baseSpeed = -1; // Store original speed self.attackDamage = 25; self.attackCooldown = 0; self.isAttacking = false; self.butterSlowTime = 0; // Butter slow effect timer self.isSlowed = false; // Track if zombie is slowed var zombieGraphics; if (self.type === 'fast') { zombieGraphics = self.attachAsset('fastZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -2; self.baseSpeed = -2; self.health = 60; self.maxHealth = 60; } else if (self.type === 'snailBucket') { zombieGraphics = self.attachAsset('snailBucket', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -0.5; self.baseSpeed = -0.5; self.health = 250; self.maxHealth = 250; } else if (self.type === 'miner') { zombieGraphics = self.attachAsset('minerZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -1.2; self.baseSpeed = -1.2; self.health = 120; self.maxHealth = 120; self.underground = false; self.surfaceTimer = 0; self.canAttack = true; // Can attack and be attacked when above ground } else if (self.type === 'cart') { zombieGraphics = self.attachAsset('cartZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -0.7; self.baseSpeed = -0.7; self.health = 300; self.maxHealth = 300; self.cartHealth = 200; // Cart has its own health self.cartDestroyed = false; } else if (self.type === 'newspaper') { zombieGraphics = self.attachAsset('newspaperZombie', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -0.8; self.baseSpeed = -0.8; self.health = 150; self.maxHealth = 150; self.newspaperHealth = 75; // Newspaper has its own health self.newspaperDestroyed = false; self.enraged = false; } else if (self.type === 'zombistein') { zombieGraphics = self.attachAsset('zombistein', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.8, scaleY: 1.8, tint: 0x00ff00 }); self.speed = -0.5; self.baseSpeed = -0.5; self.health = 500; self.maxHealth = 500; self.electricAttack = true; self.lastElectricAttack = 0; } else { zombieGraphics = self.attachAsset('zombie', { anchorX: 0.5, anchorY: 0.5 }); self.baseSpeed = -1; } self.takeDamage = function (damage) { // Cart zombie takes damage to cart first if (self.type === 'cart' && !self.cartDestroyed) { self.cartHealth -= damage; if (self.cartHealth <= 0) { self.cartDestroyed = true; self.speed = -1.5; // Move faster without cart zombieGraphics.tint = 0x888888; } return false; // Cart zombie doesn't die until cart is destroyed } else if (self.type === 'newspaper' && !self.newspaperDestroyed) { self.newspaperHealth -= damage; if (self.newspaperHealth <= 0) { self.newspaperDestroyed = true; self.enraged = true; self.speed = -2.5; // Move much faster when enraged zombieGraphics.tint = 0xff4444; // Red tint when angry } return false; // Newspaper zombie doesn't die until newspaper is destroyed } else { // Miner zombie cannot be damaged when underground if (self.type === 'miner' && self.underground) { return false; // Cannot be damaged underground } self.health -= damage; // Damage flash animation tween(self, { tint: 0xff4444 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { tint: 0xffffff }, { duration: 150, easing: tween.easeIn }); } }); // Slight knockback effect tween(self, { x: self.x - 10 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { x: self.x + 10 }, { duration: 200, easing: tween.easeIn }); } }); if (self.health <= 0) { // Type-specific death animations if (self.type === 'fast') { // Fast zombie spins rapidly while shrinking tween(self, { rotation: Math.PI * 4, // Multiple spins scaleX: 0.2, scaleY: 0.2, alpha: 0, tint: 0xff4444 }, { duration: 600, easing: tween.easeOut }); } else if (self.type === 'snailBucket') { // Snail bucket zombie falls backward with bucket rolling tween(self, { rotation: -Math.PI / 2, y: self.y + 30, x: self.x - 20, scaleX: 1.3, scaleY: 0.6, alpha: 0.3, tint: 0x666666 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { // Bucket rolls away effect tween(self, { rotation: -Math.PI * 2, x: self.x - 50, alpha: 0 }, { duration: 400, easing: tween.easeIn }); } }); } else if (self.type === 'miner') { // Miner zombie explodes underground effect tween(self, { scaleX: 2.0, scaleY: 0.3, alpha: 0, tint: 0x8B4513, // Brown dirt color rotation: Math.PI }, { duration: 500, easing: tween.easeOut }); } else if (self.type === 'cart') { // Cart zombie tips over with cart debris tween(self, { rotation: Math.PI / 3, y: self.y + 40, x: self.x - 30, scaleX: 1.4, scaleY: 0.7, alpha: 0.4, tint: 0x444444 }, { duration: 700, easing: tween.easeOut, onFinish: function onFinish() { // Final collapse tween(self, { scaleX: 0.5, scaleY: 0.2, alpha: 0 }, { duration: 300, easing: tween.easeIn }); } }); } else if (self.type === 'newspaper') { // Newspaper zombie crumples like paper tween(self, { scaleX: 0.1, scaleY: 1.8, rotation: Math.PI / 4, alpha: 0.6, tint: 0xF5F5DC // Beige newspaper color }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { // Paper flutter effect tween(self, { scaleX: 0.05, scaleY: 0.05, rotation: Math.PI * 3, y: self.y - 20, alpha: 0 }, { duration: 600, easing: tween.easeIn }); } }); } else if (self.type === 'zombistein') { // Zombistein electric death with sparks tween(self, { scaleX: 1.8, scaleY: 1.8, tint: 0x00FFFF, // Electric blue alpha: 0.8 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { // Electric explosion tween(self, { scaleX: 3.0, scaleY: 0.2, rotation: Math.PI, alpha: 0, tint: 0xFFFF00 // Yellow spark }, { duration: 500, easing: tween.easeOut }); } }); } else { // Default zombie death animation - dramatic fall and fade tween(self, { rotation: Math.PI / 2, y: self.y + 50, scaleX: 1.2, scaleY: 0.8, alpha: 0.7 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { // Second phase - complete fade out tween(self, { scaleX: 0.8, scaleY: 0.5, alpha: 0, y: self.y + 30 }, { duration: 400, easing: tween.easeIn }); } }); } return true; } } return false; }; self.update = function () { // Handle butter slow effect if (self.butterSlowTime > 0) { self.butterSlowTime--; if (!self.isSlowed) { self.isSlowed = true; self.speed = self.baseSpeed * 0.3; // Slow to 30% of original speed // Visual indication of slowing with butter color tween(self, { tint: 0xFFFF99 }, { duration: 200, easing: tween.easeOut }); } } else if (self.isSlowed) { // Restore normal speed self.isSlowed = false; self.speed = self.baseSpeed; // Remove visual effect tween(self, { tint: 0xffffff }, { duration: 200, easing: tween.easeOut }); } // Miner zombie behavior if (self.type === 'miner') { var lastColumnX = gridStartX; // X position of the last (leftmost) grid column // Check if miner has reached or passed the last column var hasReachedLastColumn = self.x <= lastColumnX; if (!self.underground && self.x < 1500 && !hasReachedLastColumn) { // Go underground when reaching middle of screen, but only if hasn't reached last column yet self.underground = true; self.canAttack = false; // Cannot attack when underground self.alpha = 0.3; // Make more transparent when underground self.speed = -2; // Move faster underground } if (self.underground) { // When underground, cannot attack plants - just move through them // Check if reached the last column (leftmost grid column) to surface and turn around if (hasReachedLastColumn) { // Surface at last column and turn around self.underground = false; self.canAttack = true; // Can attack again when surfaced self.alpha = 1; self.speed = 1.2; // Move right (positive speed) to attack from behind self.surfaceTimer = 0; } } // Once surfaced at last column, continue moving right and cannot go underground again if (hasReachedLastColumn && !self.underground && self.speed > 0) { // Ensure miner continues moving right and cannot go underground self.canAttack = true; self.alpha = 1; } } // Zombistein electric attack behavior if (self.type === 'zombistein' && LK.ticks - self.lastElectricAttack > 300) { // Electric attack every 5 seconds self.lastElectricAttack = LK.ticks; // Damage all plants in a 200px radius for (var plantIdx = 0; plantIdx < plants.length; plantIdx++) { var plant = plants[plantIdx]; var distance = Math.sqrt(Math.pow(self.x - plant.x, 2) + Math.pow(self.y - plant.y, 2)); if (distance < 200) { // Electric visual effect tween(plant, { tint: 0x00ffff }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(plant, { tint: 0xffffff }, { duration: 200, easing: tween.easeIn }); } }); if (plant.takeDamage(30)) { for (var k = plants.length - 1; k >= 0; k--) { if (plants[k] === plant) { plants.splice(k, 1); plant.destroy(); break; } } } } } } // Cart zombie behavior if (self.type === 'cart' && !self.cartDestroyed) { // Check if cart should be destroyed (takes damage first before zombie) if (self.cartHealth <= 0) { self.cartDestroyed = true; self.speed = -1.5; // Move faster without cart // Change appearance to show cart is destroyed zombieGraphics.tint = 0x888888; } } if (!self.isAttacking) { self.x += self.speed; // Walking bob animation if (LK.ticks % 60 === 0) { tween(self, { y: self.y - 5 }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { tween(self, { y: self.y + 5 }, { duration: 300, easing: tween.easeInOut }); } }); } // Check if zombie reached the left side (but allow lawnmower to activate first) if (self.x < 50) { gameOver(); return; } // Check for plants to attack (ignore spikeweed) var myRow = Math.floor((self.y - 500) / 140); for (var i = 0; i < plants.length; i++) { var plant = plants[i]; var plantRow = Math.floor((plant.y - 500) / 140); // Ignore spikeweed plants - zombies walk over them // Miner zombie cannot attack when underground if (plantRow === myRow && Math.abs(self.x - plant.x) < 80 && plant.type !== 'spikeweed' && self.canAttack) { // Cart zombie instantly destroys plants except spikeweed if (self.type === 'cart') { plant.takeDamage(9999); // Instant destruction for (var plantIdx = plants.length - 1; plantIdx >= 0; plantIdx--) { if (plants[plantIdx] === plant) { plants.splice(plantIdx, 1); break; } } } else if (self.type === 'newspaper' && self.enraged) { // Newspaper zombie can eat when enraged plant.takeDamage(9999); // Instant destruction when enraged for (var plantIdx = plants.length - 1; plantIdx >= 0; plantIdx--) { if (plants[plantIdx] === plant) { plants.splice(plantIdx, 1); break; } } } else { self.isAttacking = true; } break; } } } else { // Attack mode self.attackCooldown++; if (self.attackCooldown >= 60) { // Attack every second self.attackCooldown = 0; // Find plant to attack (ignore spikeweed) var myRow = Math.floor((self.y - 500) / 140); var targetPlant = null; for (var i = 0; i < plants.length; i++) { var plant = plants[i]; var plantRow = Math.floor((plant.y - 500) / 140); // Ignore spikeweed plants - zombies walk over them // Miner zombie cannot attack when underground if (plantRow === myRow && Math.abs(self.x - plant.x) < 80 && plant.type !== 'spikeweed' && self.canAttack) { // Cart zombie instantly destroys plants except spikeweed if (self.type === 'cart') { plant.takeDamage(9999); // Instant destruction for (var plantIdx = plants.length - 1; plantIdx >= 0; plantIdx--) { if (plants[plantIdx] === plant) { plants.splice(plantIdx, 1); break; } } self.isAttacking = false; // Continue moving after instant destruction targetPlant = null; } else if (self.type === 'newspaper' && self.enraged) { // Newspaper zombie can eat when enraged plant.takeDamage(9999); // Instant destruction when enraged for (var plantIdx = plants.length - 1; plantIdx >= 0; plantIdx--) { if (plants[plantIdx] === plant) { plants.splice(plantIdx, 1); break; } } self.isAttacking = false; // Continue moving after instant destruction targetPlant = null; } else { targetPlant = plant; } break; } } if (targetPlant) { // Fisheye eating effect tween(self, { scaleX: 1.3, scaleY: 0.7 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.easeIn }); } }); if (targetPlant.takeDamage(self.attackDamage)) { // Plant destroyed, remove from array for (var j = plants.length - 1; j >= 0; j--) { if (plants[j] === targetPlant) { plants.splice(j, 1); break; } } self.isAttacking = false; } } else { self.isAttacking = false; } } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x228B22 }); /**** * Game Code ****/ // Night plants (mushrooms) // Game variables var sunPoints = 150; var currentWave = 1; var maxWaves = 5; var waveSpawnTimer = 0; var zombiesInWave = 0; var maxZombiesInWave = 30; var zombiesKilled = 0; // Track total zombies killed var selectedPlantType = null; var shovelSelected = false; var gameState = 'loading'; // 'loading', 'levelSelect', 'playing', 'won', 'lost', 'minigame' var currentLevel = 1; var loadingScreen = null; var levelButtons = []; var currentMinigame = null; var minigameSelector = null; // Seed recharge system - 3 seconds = 180 ticks at 60 FPS var seedRechargeTime = 180; var seedCooldowns = { sunflower: 0, peashooter: 0, wallnut: 0, potatomine: 0, carnivorousplant: 0, doomshroom: 0, repeater: 0, threepeater: 0, spikeweed: 0, splitpeater: 0, lilypad: 0, puffshroom: 0, sunshroom: 0, fumeshroom: 0, gravebuster: 0, hypnoshroom: 0, scaredyshroom: 0, iceshroom: 0, melonpulta: 0, coltapulta: 0, lanzamaiz: 0, pot: 0 }; // Game arrays var plants = []; var zombies = []; var peas = []; var corns = []; var suns = []; var lawnmowers = []; var waterTiles = []; var tombstones = []; var conveyorBelt = null; var robotBoss = null; var fireballs = []; // Grid setup var gridStartX = 300; var gridStartY = 350; var gridCols = 12; var gridRows = 12; var cellSize = 140; // Plant costs var plantCosts = { sunflower: 50, peashooter: 100, wallnut: 50, potatomine: 25, carnivorousplant: 150, doomshroom: 125, repeater: 200, threepeater: 325, spikeweed: 100, splitpeater: 175, lilypad: 25, puffshroom: 0, sunshroom: 25, fumeshroom: 75, gravebuster: 75, hypnoshroom: 75, scaredyshroom: 25, iceshroom: 75, melonpulta: 300, coltapulta: 100, lanzamaiz: 100, pot: 25 }; // UI Elements var sunDisplay = new Text2('Sun: ' + sunPoints, { size: 60, fill: 0xFFFF00 }); sunDisplay.anchor.set(0, 0); LK.gui.topLeft.addChild(sunDisplay); sunDisplay.x = 120; sunDisplay.y = 20; var waveDisplay = new Text2('Wave: ' + currentWave + '/' + maxWaves, { size: 50, fill: 0xFFFFFF }); waveDisplay.anchor.set(0.5, 0); LK.gui.top.addChild(waveDisplay); waveDisplay.y = 20; // Plant selection buttons arranged vertically at bottom of screen var sunflowerButton = LK.getAsset('sunflower', { anchorX: 0.5, anchorY: 0.5, x: 200, y: 2100, scaleX: 0.6, scaleY: 0.6 }); game.addChild(sunflowerButton); var peashooterButton = LK.getAsset('peashooter', { anchorX: 0.5, anchorY: 0.5, x: 300, y: 2100, scaleX: 0.6, scaleY: 0.6 }); game.addChild(peashooterButton); var wallnutButton = LK.getAsset('wallnut', { anchorX: 0.5, anchorY: 0.5, x: 400, y: 2100, scaleX: 0.6, scaleY: 0.6 }); game.addChild(wallnutButton); var potatomineButton = LK.getAsset('potatomine', { anchorX: 0.5, anchorY: 0.5, x: 500, y: 2100, scaleX: 0.6, scaleY: 0.6 }); game.addChild(potatomineButton); var carnivorousplantButton = LK.getAsset('carnivorousplant', { anchorX: 0.5, anchorY: 0.5, x: 600, y: 2100, scaleX: 0.6, scaleY: 0.6 }); game.addChild(carnivorousplantButton); var doomshroomButton = LK.getAsset('doomshroom', { anchorX: 0.5, anchorY: 0.5, x: 700, y: 2100, scaleX: 0.6, scaleY: 0.6 }); game.addChild(doomshroomButton); var repeaterButton = LK.getAsset('repeater', { anchorX: 0.5, anchorY: 0.5, x: 800, y: 2100, scaleX: 0.6, scaleY: 0.6 }); game.addChild(repeaterButton); var threepeaterButton = LK.getAsset('threepeater', { anchorX: 0.5, anchorY: 0.5, x: 900, y: 2100, scaleX: 0.6, scaleY: 0.6 }); game.addChild(threepeaterButton); var spikeButton = LK.getAsset('spikeweed', { anchorX: 0.5, anchorY: 0.5, x: 1000, y: 2100, scaleX: 0.6, scaleY: 0.6 }); game.addChild(spikeButton); var splitpeaterButton = LK.getAsset('splitpeater', { anchorX: 0.5, anchorY: 0.5, x: 1100, y: 2100, scaleX: 0.6, scaleY: 0.6 }); game.addChild(splitpeaterButton); var lilypadButton = LK.getAsset('lilypad', { anchorX: 0.5, anchorY: 0.5, x: 1200, y: 2100, scaleX: 0.6, scaleY: 0.6 }); game.addChild(lilypadButton); // Restart button and shovel moved to right side of screen var restartButton = LK.getAsset('restartButton', { anchorX: 0.5, anchorY: 0.5, x: 1800, y: 120, scaleX: 0.8, scaleY: 0.8 }); game.addChild(restartButton); var shovelButton = LK.getAsset('shovel', { anchorX: 0.5, anchorY: 0.5, x: 1920, y: 120, scaleX: 0.6, scaleY: 0.6 }); game.addChild(shovelButton); // Plant cost labels - arranged horizontally at bottom next to buttons var sunflowerCost = new Text2('50', { size: 20, fill: 0xFFFFFF }); sunflowerCost.anchor.set(0.5, 0); sunflowerCost.x = 200; sunflowerCost.y = 2140; game.addChild(sunflowerCost); var peashooterCost = new Text2('100', { size: 20, fill: 0xFFFFFF }); peashooterCost.anchor.set(0.5, 0); peashooterCost.x = 300; peashooterCost.y = 2140; game.addChild(peashooterCost); var wallnutCost = new Text2('50', { size: 20, fill: 0xFFFFFF }); wallnutCost.anchor.set(0.5, 0); wallnutCost.x = 400; wallnutCost.y = 2140; game.addChild(wallnutCost); var potatomineeCost = new Text2('25', { size: 20, fill: 0xFFFFFF }); potatomineeCost.anchor.set(0.5, 0); potatomineeCost.x = 500; potatomineeCost.y = 2140; game.addChild(potatomineeCost); var carnivorousplantCost = new Text2('150', { size: 20, fill: 0xFFFFFF }); carnivorousplantCost.anchor.set(0.5, 0); carnivorousplantCost.x = 600; carnivorousplantCost.y = 2140; game.addChild(carnivorousplantCost); var doomshroomCost = new Text2('125', { size: 20, fill: 0xFFFFFF }); doomshroomCost.anchor.set(0.5, 0); doomshroomCost.x = 700; doomshroomCost.y = 2140; game.addChild(doomshroomCost); var repeaterCost = new Text2('200', { size: 20, fill: 0xFFFFFF }); repeaterCost.anchor.set(0.5, 0); repeaterCost.x = 800; repeaterCost.y = 2140; game.addChild(repeaterCost); var threepeaterCost = new Text2('325', { size: 20, fill: 0xFFFFFF }); threepeaterCost.anchor.set(0.5, 0); threepeaterCost.x = 900; threepeaterCost.y = 2140; game.addChild(threepeaterCost); var spikeCost = new Text2('100', { size: 20, fill: 0xFFFFFF }); spikeCost.anchor.set(0.5, 0); spikeCost.x = 1000; spikeCost.y = 2140; game.addChild(spikeCost); var splitpeaterCost = new Text2('175', { size: 20, fill: 0xFFFFFF }); splitpeaterCost.anchor.set(0.5, 0); splitpeaterCost.x = 1100; splitpeaterCost.y = 2140; game.addChild(splitpeaterCost); var lilypadCost = new Text2('25', { size: 20, fill: 0xFFFFFF }); lilypadCost.anchor.set(0.5, 0); lilypadCost.x = 1200; lilypadCost.y = 2140; game.addChild(lilypadCost); // Create separate catapult plant buttons for roof levels var melonpultaButton = LK.getAsset('melonpulta', { anchorX: 0.5, anchorY: 0.5, x: 800, y: 2100, scaleX: 0.6, scaleY: 0.6 }); game.addChild(melonpultaButton); melonpultaButton.visible = false; // Hidden by default var coltapultaButton = LK.getAsset('coltapulta', { anchorX: 0.5, anchorY: 0.5, x: 900, y: 2100, scaleX: 0.6, scaleY: 0.6 }); game.addChild(coltapultaButton); coltapultaButton.visible = false; // Hidden by default var lanzamaizButton = LK.getAsset('lanzamaiz', { anchorX: 0.5, anchorY: 0.5, x: 1100, y: 2100, scaleX: 0.6, scaleY: 0.6 }); game.addChild(lanzamaizButton); lanzamaizButton.visible = false; // Hidden by default // Create cost labels for catapult plants var melonpultaCost = new Text2('300', { size: 20, fill: 0xFFFFFF }); melonpultaCost.anchor.set(0.5, 0); melonpultaCost.x = 800; melonpultaCost.y = 2140; game.addChild(melonpultaCost); melonpultaCost.visible = false; // Hidden by default var coltapultaCost = new Text2('100', { size: 20, fill: 0xFFFFFF }); coltapultaCost.anchor.set(0.5, 0); coltapultaCost.x = 900; coltapultaCost.y = 2140; game.addChild(coltapultaCost); coltapultaCost.visible = false; // Hidden by default var lanzamaizCost = new Text2('100', { size: 20, fill: 0xFFFFFF }); lanzamaizCost.anchor.set(0.5, 0); lanzamaizCost.x = 1100; lanzamaizCost.y = 2140; game.addChild(lanzamaizCost); lanzamaizCost.visible = false; // Hidden by default // Create pot plant button for roof levels var potButton = LK.getAsset('pot', { anchorX: 0.5, anchorY: 0.5, x: 300, y: 2100, scaleX: 0.6, scaleY: 0.6 }); game.addChild(potButton); potButton.visible = false; // Hidden by default // Create cost label for pot plant var potCost = new Text2('25', { size: 20, fill: 0xFFFFFF }); potCost.anchor.set(0.5, 0); potCost.x = 300; potCost.y = 2140; game.addChild(potCost); potCost.visible = false; // Hidden by default // Boss health display var bossHealthDisplay = new Text2('Boss Health: 1000/1000', { size: 40, fill: 0xFF0000 }); bossHealthDisplay.anchor.set(0, 1); // Bottom left anchor bossHealthDisplay.x = 120; bossHealthDisplay.y = 2600; game.addChild(bossHealthDisplay); bossHealthDisplay.visible = false; // Hidden by default var restartText = new Text2('RESTART', { size: 16, fill: 0xFFFFFF }); restartText.anchor.set(0.5, 0); restartText.x = 1800; restartText.y = 90; game.addChild(restartText); // Exit level button - positioned next to restart button var exitLevelButton = LK.getAsset('restartButton', { anchorX: 0.5, anchorY: 0.5, x: 1650, y: 120, scaleX: 0.8, scaleY: 0.8 }); game.addChild(exitLevelButton); var exitText = new Text2('EXIT', { size: 16, fill: 0xFFFFFF }); exitText.anchor.set(0.5, 0); exitText.x = 1650; exitText.y = 90; game.addChild(exitText); // Draw grid var gridTiles = []; for (var row = 0; row < gridRows; row++) { for (var col = 0; col < gridCols; col++) { var gridCell = LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, x: gridStartX + col * cellSize, y: gridStartY + row * cellSize, alpha: 0.3 }); game.addChild(gridCell); gridTiles.push(gridCell); } } // Add decorative bushes on the right to cover zombie spawn area - more bushes arranged at different depths var bushes = LK.getAsset('bushes', { anchorX: 0.5, anchorY: 0.5, x: 2000, y: 600, scaleX: 1.8, scaleY: 1.8 }); game.addChild(bushes); var bushes2 = LK.getAsset('bushes', { anchorX: 0.5, anchorY: 0.5, x: 2000, y: 900, scaleX: 1.6, scaleY: 1.6 }); game.addChild(bushes2); var bushes3 = LK.getAsset('bushes', { anchorX: 0.5, anchorY: 0.5, x: 2000, y: 1200, scaleX: 1.7, scaleY: 1.7 }); game.addChild(bushes3); var bushes4 = LK.getAsset('bushes', { anchorX: 0.5, anchorY: 0.5, x: 2000, y: 1500, scaleX: 1.5, scaleY: 1.5 }); game.addChild(bushes4); var bushes5 = LK.getAsset('bushes', { anchorX: 0.5, anchorY: 0.5, x: 2000, y: 1800, scaleX: 1.4, scaleY: 1.4 }); game.addChild(bushes5); var bushes6 = LK.getAsset('bushes', { anchorX: 0.5, anchorY: 0.5, x: 1900, y: 750, scaleX: 1.3, scaleY: 1.3 }); game.addChild(bushes6); var bushes7 = LK.getAsset('bushes', { anchorX: 0.5, anchorY: 0.5, x: 1900, y: 1350, scaleX: 1.2, scaleY: 1.2 }); game.addChild(bushes7); var bushes8 = LK.getAsset('bushes', { anchorX: 0.5, anchorY: 0.5, x: 1950, y: 450, scaleX: 1.6, scaleY: 1.6 }); game.addChild(bushes8); var bushes9 = LK.getAsset('bushes', { anchorX: 0.5, anchorY: 0.5, x: 1950, y: 1050, scaleX: 1.4, scaleY: 1.4 }); game.addChild(bushes9); var bushes10 = LK.getAsset('bushes', { anchorX: 0.5, anchorY: 0.5, x: 1950, y: 1650, scaleX: 1.5, scaleY: 1.5 }); game.addChild(bushes10); var bushes11 = LK.getAsset('bushes', { anchorX: 0.5, anchorY: 0.5, x: 1850, y: 600, scaleX: 1.1, scaleY: 1.1 }); game.addChild(bushes11); var bushes12 = LK.getAsset('bushes', { anchorX: 0.5, anchorY: 0.5, x: 1850, y: 1200, scaleX: 1.0, scaleY: 1.0 }); game.addChild(bushes12); var bushes13 = LK.getAsset('bushes', { anchorX: 0.5, anchorY: 0.5, x: 1850, y: 1800, scaleX: 1.3, scaleY: 1.3 }); game.addChild(bushes13); // Initialize lawnmowers for (var row = 0; row < gridRows; row++) { var lawnmower = new Lawnmower(); lawnmower.x = 220; lawnmower.y = gridStartY + row * cellSize; game.addChild(lawnmower); lawnmowers.push(lawnmower); } // Move all bushes to front to render above zombies bushes.parent.removeChild(bushes); game.addChild(bushes); bushes2.parent.removeChild(bushes2); game.addChild(bushes2); bushes3.parent.removeChild(bushes3); game.addChild(bushes3); bushes4.parent.removeChild(bushes4); game.addChild(bushes4); bushes5.parent.removeChild(bushes5); game.addChild(bushes5); bushes6.parent.removeChild(bushes6); game.addChild(bushes6); bushes7.parent.removeChild(bushes7); game.addChild(bushes7); bushes8.parent.removeChild(bushes8); game.addChild(bushes8); bushes9.parent.removeChild(bushes9); game.addChild(bushes9); bushes10.parent.removeChild(bushes10); game.addChild(bushes10); bushes11.parent.removeChild(bushes11); game.addChild(bushes11); bushes12.parent.removeChild(bushes12); game.addChild(bushes12); bushes13.parent.removeChild(bushes13); game.addChild(bushes13); // Initialize loading screen if (gameState === 'loading') { loadingScreen = new LoadingScreen(); game.addChild(loadingScreen); // Hide all game elements initially hideGameElements(); } function updateGridTiles(levelNumber) { // Destroy existing grid tiles for (var i = gridTiles.length - 1; i >= 0; i--) { gridTiles[i].destroy(); } gridTiles = []; // Determine tile type based on level var tileAsset = 'gridCell'; if (levelNumber > 15) { // Roof levels if (levelNumber === 19) { tileAsset = 'roofTileNight'; // Night roof } else { tileAsset = 'roofTileDay'; // Day roof } } else if (levelNumber > 5 && levelNumber <= 10 || levelNumber === 19) { // Night levels (6-10) tileAsset = 'grassTileNight'; } else { // Day levels (1-5) and pool levels (11-15) use day grass tileAsset = 'grassTileDay'; } // Create new grid with appropriate tiles for (var row = 0; row < gridRows; row++) { for (var col = 0; col < gridCols; col++) { var gridCell = LK.getAsset(tileAsset, { anchorX: 0.5, anchorY: 0.5, x: gridStartX + col * cellSize, y: gridStartY + row * cellSize, alpha: 0.3 }); game.addChild(gridCell); gridTiles.push(gridCell); } } } function updateSunDisplay() { sunDisplay.setText('Sun: ' + sunPoints); } function canAffordPlant(plantType) { return sunPoints >= plantCosts[plantType]; } function isSeedReady(plantType) { return seedCooldowns[plantType] <= 0; } function canPlant(plantType) { return canAffordPlant(plantType) && isSeedReady(plantType); } function getGridPosition(x, y) { var col = Math.floor((x - gridStartX + cellSize / 2) / cellSize); var row = Math.floor((y - gridStartY + cellSize / 2) / cellSize); if (col >= 0 && col < gridCols && row >= 0 && row < gridRows) { return { row: row, col: col, x: gridStartX + col * cellSize, y: gridStartY + row * cellSize }; } return null; } function isGridOccupied(row, col) { for (var i = 0; i < plants.length; i++) { var plant = plants[i]; var plantRow = Math.floor((plant.y - gridStartY + cellSize / 2) / cellSize); var plantCol = Math.floor((plant.x - gridStartX + cellSize / 2) / cellSize); if (plantRow === row && plantCol === col) { return true; } } return false; } function isPositionOnWater(row, col) { // Check if position is on water (pool levels 11-15, rows 4-8) var isPoolLevel = currentLevel > 10; if (isPoolLevel && row >= 4 && row <= 8) { return true; } return false; } function canPlantOnPosition(plantType, row, col) { // Check what's already at this position var hasLilypad = false; var hasPot = false; var isOccupied = false; for (var i = 0; i < plants.length; i++) { var plant = plants[i]; var plantRow = Math.floor((plant.y - gridStartY + cellSize / 2) / cellSize); var plantCol = Math.floor((plant.x - gridStartX + cellSize / 2) / cellSize); if (plantRow === row && plantCol === col) { if (plant.type === 'lilypad') { hasLilypad = true; } else if (plant.type === 'pot') { hasPot = true; } else { isOccupied = true; // Other plant types occupy space completely } } } // Check if this is a roof level var isRoofLevel = currentLevel > 15; // Check if this is a water position var isWaterPos = isPositionOnWater(row, col); // Handle roof level rules if (isRoofLevel) { if (plantType === 'pot') { return !isOccupied && !hasPot; // Pot needs empty space } else { return hasPot && !isOccupied; // Other plants need pot and no other plants } } // Handle water level rules if (isWaterPos) { if (plantType === 'lilypad') { return !isOccupied && !hasLilypad; // Lilypad needs empty water } else { return hasLilypad && !isOccupied; // Other plants need lilypad and no other plants } } // Handle regular ground rules if (plantType === 'lilypad') { return false; // Lilypad can only be on water } return !isOccupied && !hasLilypad && !hasPot; // Regular plants need completely empty space } function spawnTombstones() { // Spawn 3-5 tombstones randomly on the grid for night levels var tombstoneCount = 3 + Math.floor(Math.random() * 3); // Define vertical middle line at column 6 (middle of 12 columns) var middleColumn = Math.floor(gridCols / 2); for (var i = 0; i < tombstoneCount; i++) { var attempts = 0; var placed = false; while (!placed && attempts < 20) { var randomRow = Math.floor(Math.random() * gridRows); // Only spawn tombstones to the right of the middle line (behind middle) var randomCol = middleColumn + Math.floor(Math.random() * (gridCols - middleColumn - 1)); // Check if position is already occupied var occupied = false; for (var j = 0; j < tombstones.length; j++) { var tombstoneRow = Math.floor((tombstones[j].y - gridStartY + cellSize / 2) / cellSize); var tombstoneCol = Math.floor((tombstones[j].x - gridStartX + cellSize / 2) / cellSize); if (tombstoneRow === randomRow && tombstoneCol === randomCol) { occupied = true; break; } } if (!occupied) { var tombstone = new Tombstone(); tombstone.x = gridStartX + randomCol * cellSize; tombstone.y = gridStartY + randomRow * cellSize; game.addChild(tombstone); tombstones.push(tombstone); placed = true; } attempts++; } } } function spawnZombie() { var rand = Math.random(); var zombieType = 'normal'; if (rand < 0.15) { zombieType = 'fast'; } else if (rand < 0.25) { zombieType = 'snailBucket'; } else if (rand < 0.35) { zombieType = 'miner'; } else if (rand < 0.45) { zombieType = 'cart'; } else if (rand < 0.55) { zombieType = 'newspaper'; } var zombie = new Zombie(zombieType); zombie.x = 2100; zombie.y = gridStartY + Math.floor(Math.random() * gridRows) * cellSize; // Start with spawn effect zombie.alpha = 0; zombie.scaleX = 1.5; zombie.scaleY = 1.5; zombie.tint = 0x88ff88; game.addChild(zombie); zombies.push(zombie); // Spawn animation tween(zombie, { alpha: 1, scaleX: 1.0, scaleY: 1.0, tint: 0xffffff }, { duration: 600, easing: tween.easeOut }); } function gameOver() { if (gameState === 'playing') { gameState = 'lost'; LK.showGameOver(); } } function checkWinCondition() { if (currentWave > maxWaves && zombies.length === 0) { if (gameState === 'playing') { gameState = 'won'; // Victory celebration animation for all plants for (var i = 0; i < plants.length; i++) { var plant = plants[i]; var delay = i * 100; // Stagger the animations LK.setTimeout(function (capturedPlant) { return function () { tween(capturedPlant, { scaleX: 1.3, scaleY: 1.3, rotation: 0.5 }, { duration: 400, easing: tween.bounceOut, onFinish: function onFinish() { tween(capturedPlant, { scaleX: 1.0, scaleY: 1.0, rotation: 0 }, { duration: 400, easing: tween.bounceOut }); } }); }; }(plant), delay); } // Award bonus sun for completing level var levelBonus = currentLevel * 5; sunPoints += levelBonus; updateSunDisplay(); LK.showYouWin(); } } } function showLevelSelection() { gameState = 'levelSelect'; // Stop background music when returning to level selection LK.stopMusic(); // Start menu music for level selection LK.playMusic('menuMusic', { loop: true }); // Clear loading screen if (loadingScreen) { loadingScreen.destroy(); loadingScreen = null; } // Hide all game elements hideGameElements(); // Create day level buttons (1-5) var dayButtonSpacing = 150; var dayStartX = 1024 - 2 * dayButtonSpacing; for (var i = 1; i <= 5; i++) { var levelButton = new LevelButton(i); levelButton.x = dayStartX + (i - 1) * dayButtonSpacing; levelButton.y = 600; game.addChild(levelButton); levelButtons.push(levelButton); } // Create night level buttons (6-10) var nightButtonSpacing = 150; var nightStartX = 1024 - 2 * nightButtonSpacing; for (var j = 6; j <= 10; j++) { var nightLevelButton = new LevelButton(j); nightLevelButton.x = nightStartX + (j - 6) * nightButtonSpacing; nightLevelButton.y = 900; // Tint night buttons with dark blue nightLevelButton.tint = 0x000080; game.addChild(nightLevelButton); levelButtons.push(nightLevelButton); } // Create pool level buttons (11-15) var poolButtonSpacing = 150; var poolStartX = 1024 - 2 * poolButtonSpacing; for (var k = 11; k <= 15; k++) { var poolLevelButton = new LevelButton(k); poolLevelButton.x = poolStartX + (k - 11) * poolButtonSpacing; poolLevelButton.y = 1200; // Tint pool buttons with cyan/blue for water theme poolLevelButton.tint = 0x00FFFF; game.addChild(poolLevelButton); levelButtons.push(poolLevelButton); } // Create roof level buttons (16-18) - day roof levels var roofButtonSpacing = 150; var roofStartX = 1024 - 1 * roofButtonSpacing; for (var r = 16; r <= 18; r++) { var roofLevelButton = new LevelButton(r); roofLevelButton.x = roofStartX + (r - 16) * roofButtonSpacing; roofLevelButton.y = 1500; // Tint roof buttons with brown/gray for roof theme roofLevelButton.tint = 0x8B4513; game.addChild(roofLevelButton); levelButtons.push(roofLevelButton); } // Create night roof boss fight button (19) var bossLevelButton = new LevelButton(19); bossLevelButton.x = 1024; bossLevelButton.y = 1800; // Tint boss button with dark red for boss theme bossLevelButton.tint = 0x8B0000; // Make boss button bigger to indicate it's special bossLevelButton.scaleX = 1.5; bossLevelButton.scaleY = 1.5; game.addChild(bossLevelButton); levelButtons.push(bossLevelButton); // Create tombstone button for minigames var minigameTombstoneButton = LK.getAsset('tombstone', { anchorX: 0.5, anchorY: 0.5, x: 1424, y: 1800, scaleX: 1.2, scaleY: 1.2 }); game.addChild(minigameTombstoneButton); levelButtons.push(minigameTombstoneButton); // Add minigames text label var minigamesText = new Text2('MINIJUEGOS', { size: 40, fill: 0xFFFFFF }); minigamesText.anchor.set(0.5, 0.5); minigamesText.x = 1424; minigamesText.y = 1900; game.addChild(minigamesText); levelButtons.push(minigamesText); // Add click handler for minigame tombstone minigameTombstoneButton.down = function (x, y, obj) { // Clear level selection elements for (var i = levelButtons.length - 1; i >= 0; i--) { levelButtons[i].destroy(); } levelButtons = []; // Show minigame selector gameState = 'minigame'; minigameSelector = new MinigameSelector(); game.addChild(minigameSelector); }; } function hideDayPlants() { sunflowerButton.visible = false; peashooterButton.visible = false; wallnutButton.visible = false; potatomineButton.visible = false; carnivorousplantButton.visible = false; doomshroomButton.visible = false; repeaterButton.visible = false; threepeaterButton.visible = false; spikeButton.visible = false; splitpeaterButton.visible = false; lilypadButton.visible = false; melonpultaButton.visible = false; coltapultaButton.visible = false; lanzamaizButton.visible = false; sunflowerCost.visible = false; peashooterCost.visible = false; wallnutCost.visible = false; potatomineeCost.visible = false; carnivorousplantCost.visible = false; doomshroomCost.visible = false; repeaterCost.visible = false; threepeaterCost.visible = false; spikeCost.visible = false; splitpeaterCost.visible = false; lilypadCost.visible = false; melonpultaCost.visible = false; coltapultaCost.visible = false; lanzamaizCost.visible = false; potButton.visible = false; potCost.visible = false; } function showDayPlants() { sunflowerButton.visible = true; peashooterButton.visible = true; wallnutButton.visible = true; potatomineButton.visible = true; carnivorousplantButton.visible = true; doomshroomButton.visible = true; repeaterButton.visible = true; threepeaterButton.visible = true; spikeButton.visible = true; splitpeaterButton.visible = true; lilypadButton.visible = true; sunflowerCost.visible = true; peashooterCost.visible = true; wallnutCost.visible = true; potatomineeCost.visible = true; carnivorousplantCost.visible = true; doomshroomCost.visible = true; repeaterCost.visible = true; threepeaterCost.visible = true; spikeCost.visible = true; splitpeaterCost.visible = true; lilypadCost.visible = true; } // Night plant buttons - store globally to manage them var nightPlantButtons = []; var nightPlantCosts = []; function createNightPlantButtons() { // Create night plant buttons var puffshroomButton = LK.getAsset('puffshroom', { anchorX: 0.5, anchorY: 0.5, x: 200, y: 2100, scaleX: 0.8, scaleY: 0.8 }); game.addChild(puffshroomButton); nightPlantButtons.push(puffshroomButton); var sunshroomButton = LK.getAsset('sunshroom', { anchorX: 0.5, anchorY: 0.5, x: 300, y: 2100, scaleX: 0.8, scaleY: 0.8 }); game.addChild(sunshroomButton); nightPlantButtons.push(sunshroomButton); var fumeshroomButton = LK.getAsset('fumeshroom', { anchorX: 0.5, anchorY: 0.5, x: 400, y: 2100, scaleX: 0.8, scaleY: 0.8 }); game.addChild(fumeshroomButton); nightPlantButtons.push(fumeshroomButton); var gravebusterButton = LK.getAsset('gravebuster', { anchorX: 0.5, anchorY: 0.5, x: 500, y: 2100, scaleX: 0.8, scaleY: 0.8 }); game.addChild(gravebusterButton); nightPlantButtons.push(gravebusterButton); var hypnoshroomButton = LK.getAsset('hypnoshroom', { anchorX: 0.5, anchorY: 0.5, x: 600, y: 2100, scaleX: 0.8, scaleY: 0.8 }); game.addChild(hypnoshroomButton); nightPlantButtons.push(hypnoshroomButton); var scaredyshroomButton = LK.getAsset('scaredyshroom', { anchorX: 0.5, anchorY: 0.5, x: 700, y: 2100, scaleX: 0.8, scaleY: 0.8 }); game.addChild(scaredyshroomButton); nightPlantButtons.push(scaredyshroomButton); var iceshroomButton = LK.getAsset('iceshroom', { anchorX: 0.5, anchorY: 0.5, x: 800, y: 2100, scaleX: 0.8, scaleY: 0.8 }); game.addChild(iceshroomButton); nightPlantButtons.push(iceshroomButton); // Create night plant cost labels var puffshroomCost = new Text2('0', { size: 20, fill: 0xFFFFFF }); puffshroomCost.anchor.set(0.5, 0); puffshroomCost.x = 200; puffshroomCost.y = 2140; game.addChild(puffshroomCost); nightPlantCosts.push(puffshroomCost); var sunshroomCost = new Text2('25', { size: 20, fill: 0xFFFFFF }); sunshroomCost.anchor.set(0.5, 0); sunshroomCost.x = 300; sunshroomCost.y = 2140; game.addChild(sunshroomCost); nightPlantCosts.push(sunshroomCost); var fumeshroomCost = new Text2('75', { size: 20, fill: 0xFFFFFF }); fumeshroomCost.anchor.set(0.5, 0); fumeshroomCost.x = 400; fumeshroomCost.y = 2140; game.addChild(fumeshroomCost); nightPlantCosts.push(fumeshroomCost); var gravebusterCost = new Text2('75', { size: 20, fill: 0xFFFFFF }); gravebusterCost.anchor.set(0.5, 0); gravebusterCost.x = 500; gravebusterCost.y = 2140; game.addChild(gravebusterCost); nightPlantCosts.push(gravebusterCost); var hypnoshroomCost = new Text2('75', { size: 20, fill: 0xFFFFFF }); hypnoshroomCost.anchor.set(0.5, 0); hypnoshroomCost.x = 600; hypnoshroomCost.y = 2140; game.addChild(hypnoshroomCost); nightPlantCosts.push(hypnoshroomCost); var scaredyshroomCost = new Text2('25', { size: 20, fill: 0xFFFFFF }); scaredyshroomCost.anchor.set(0.5, 0); scaredyshroomCost.x = 700; scaredyshroomCost.y = 2140; game.addChild(scaredyshroomCost); nightPlantCosts.push(scaredyshroomCost); var iceshroomCost = new Text2('75', { size: 20, fill: 0xFFFFFF }); iceshroomCost.anchor.set(0.5, 0); iceshroomCost.x = 800; iceshroomCost.y = 2140; game.addChild(iceshroomCost); nightPlantCosts.push(iceshroomCost); // Add event handlers for night plant buttons puffshroomButton.down = function (x, y, obj) { if (canPlant('puffshroom')) { selectedPlantType = 'puffshroom'; shovelSelected = false; } }; sunshroomButton.down = function (x, y, obj) { if (canPlant('sunshroom')) { selectedPlantType = 'sunshroom'; shovelSelected = false; } }; fumeshroomButton.down = function (x, y, obj) { if (canPlant('fumeshroom')) { selectedPlantType = 'fumeshroom'; shovelSelected = false; } }; gravebusterButton.down = function (x, y, obj) { if (canPlant('gravebuster')) { selectedPlantType = 'gravebuster'; shovelSelected = false; } }; hypnoshroomButton.down = function (x, y, obj) { if (canPlant('hypnoshroom')) { selectedPlantType = 'hypnoshroom'; shovelSelected = false; } }; scaredyshroomButton.down = function (x, y, obj) { if (canPlant('scaredyshroom')) { selectedPlantType = 'scaredyshroom'; shovelSelected = false; } }; iceshroomButton.down = function (x, y, obj) { if (canPlant('iceshroom')) { selectedPlantType = 'iceshroom'; shovelSelected = false; } }; } function cleanupNightPlantButtons() { // Destroy night plant buttons for (var i = nightPlantButtons.length - 1; i >= 0; i--) { if (nightPlantButtons[i]) { nightPlantButtons[i].destroy(); } } nightPlantButtons = []; // Destroy night plant cost labels for (var j = nightPlantCosts.length - 1; j >= 0; j--) { if (nightPlantCosts[j]) { nightPlantCosts[j].destroy(); } } nightPlantCosts = []; } function hideGameElements() { // Hide all UI elements sunDisplay.alpha = 0; waveDisplay.alpha = 0; // Hide all plant buttons sunflowerButton.alpha = 0; peashooterButton.alpha = 0; wallnutButton.alpha = 0; potatomineButton.alpha = 0; carnivorousplantButton.alpha = 0; doomshroomButton.alpha = 0; repeaterButton.alpha = 0; threepeaterButton.alpha = 0; spikeButton.alpha = 0; splitpeaterButton.alpha = 0; restartButton.alpha = 0; shovelButton.alpha = 0; // Hide cost labels sunflowerCost.alpha = 0; peashooterCost.alpha = 0; wallnutCost.alpha = 0; potatomineeCost.alpha = 0; carnivorousplantCost.alpha = 0; doomshroomCost.alpha = 0; repeaterCost.alpha = 0; threepeaterCost.alpha = 0; spikeCost.alpha = 0; splitpeaterCost.alpha = 0; lilypadCost.alpha = 0; restartText.alpha = 0; // Hide lilypad button lilypadButton.alpha = 0; // Hide exit button exitLevelButton.alpha = 0; exitText.alpha = 0; // Hide tombstone button - check if it exists first if (tombstoneButton) { tombstoneButton.alpha = 0; } if (tombstoneText) { tombstoneText.alpha = 0; } // Hide boss health display bossHealthDisplay.visible = false; } function showGameElements() { // Show all UI elements sunDisplay.alpha = 1; waveDisplay.alpha = 1; // Show all plant buttons sunflowerButton.alpha = 1; peashooterButton.alpha = 1; wallnutButton.alpha = 1; potatomineButton.alpha = 1; carnivorousplantButton.alpha = 1; doomshroomButton.alpha = 1; repeaterButton.alpha = 1; threepeaterButton.alpha = 1; spikeButton.alpha = 1; splitpeaterButton.alpha = 1; restartButton.alpha = 1; shovelButton.alpha = 1; // Show cost labels sunflowerCost.alpha = 1; peashooterCost.alpha = 1; wallnutCost.alpha = 1; potatomineeCost.alpha = 1; carnivorousplantCost.alpha = 1; doomshroomCost.alpha = 1; repeaterCost.alpha = 1; threepeaterCost.alpha = 1; spikeCost.alpha = 1; splitpeaterCost.alpha = 1; lilypadCost.alpha = 1; restartText.alpha = 1; // Show lilypad button lilypadButton.alpha = 1; // Show exit button exitLevelButton.alpha = 1; exitText.alpha = 1; // Show tombstone button - check if it exists first if (tombstoneButton) { tombstoneButton.alpha = 1; } if (tombstoneText) { tombstoneText.alpha = 1; } } function startLevelWithSelectedPlants(levelNumber, selectedPlants) { currentLevel = levelNumber; gameState = 'playing'; // Show game elements showGameElements(); // Stop menu music and start background music for all levels LK.stopMusic(); LK.playMusic('backgroundMusic', { loop: true }); // Reset game based on level with selected plants resetGameForLevel(levelNumber); // Hide all plant buttons first hideDayPlants(); // For boss fight (level 19), allow conveyor belt plant placement but no manual selection if (levelNumber === 19) { // Boss fight - conveyor belt will provide plants, no manual selection needed // But still allow placement from conveyor belt seeds return; } // Show only selected plants and create their buttons dynamically for (var i = 0; i < selectedPlants.length && i < 8; i++) { var plantType = selectedPlants[i]; var xPos = 200 + i * 100; var yPos = 2100; // Get the corresponding button for this plant type var button = null; var costLabel = null; if (plantType === 'sunflower') { button = sunflowerButton; costLabel = sunflowerCost; } else if (plantType === 'peashooter') { button = peashooterButton; costLabel = peashooterCost; } else if (plantType === 'wallnut') { button = wallnutButton; costLabel = wallnutCost; } else if (plantType === 'potatomine') { button = potatomineButton; costLabel = potatomineeCost; } else if (plantType === 'carnivorousplant') { button = carnivorousplantButton; costLabel = carnivorousplantCost; } else if (plantType === 'doomshroom') { button = doomshroomButton; costLabel = doomshroomCost; } else if (plantType === 'repeater') { button = repeaterButton; costLabel = repeaterCost; } else if (plantType === 'threepeater') { button = threepeaterButton; costLabel = threepeaterCost; } else if (plantType === 'spikeweed') { button = spikeButton; costLabel = spikeCost; } else if (plantType === 'splitpeater') { button = splitpeaterButton; costLabel = splitpeaterCost; } else if (plantType === 'lilypad') { button = lilypadButton; costLabel = lilypadCost; } else if (plantType === 'melonpulta') { button = melonpultaButton; costLabel = melonpultaCost; } else if (plantType === 'coltapulta') { button = coltapultaButton; costLabel = coltapultaCost; } else if (plantType === 'lanzamaiz') { button = lanzamaizButton; costLabel = lanzamaizCost; } else if (plantType === 'pot') { button = potButton; costLabel = potCost; } else if (plantType === 'puffshroom' || plantType === 'sunshroom' || plantType === 'fumeshroom' || plantType === 'gravebuster' || plantType === 'hypnoshroom' || plantType === 'scaredyshroom' || plantType === 'iceshroom') { // Handle night plants ONLY if they were actually selected - create temporary buttons for selected night plants var nightButton = LK.getAsset(plantType, { anchorX: 0.5, anchorY: 0.5, x: xPos, y: yPos, scaleX: 0.6, scaleY: 0.6 }); game.addChild(nightButton); nightButton.plantType = plantType; nightButton.visible = true; nightButton.alpha = 1; // Create closure to capture the plantType correctly (function (capturedPlantType) { nightButton.down = function (x, y, obj) { if (canPlant(capturedPlantType)) { selectedPlantType = capturedPlantType; shovelSelected = false; // Update button visual state nightButton.tint = 0x808080; // Reset other buttons' visual states for (var btnIdx = 0; btnIdx < nightPlantButtons.length; btnIdx++) { if (nightPlantButtons[btnIdx] !== nightButton) { nightPlantButtons[btnIdx].tint = 0xffffff; } } } }; })(plantType); // Store the button for later management nightPlantButtons.push(nightButton); var nightCostLabel = new Text2(plantCosts[plantType].toString(), { size: 20, fill: 0xFFFFFF }); nightCostLabel.anchor.set(0.5, 0); nightCostLabel.x = xPos; nightCostLabel.y = yPos + 40; nightCostLabel.visible = true; nightCostLabel.alpha = 1; game.addChild(nightCostLabel); nightPlantCosts.push(nightCostLabel); // Skip to next iteration since we handled the night plant continue; } // Position and show the selected plant button if (button && costLabel) { button.x = xPos; button.y = yPos; button.visible = true; button.alpha = 1; costLabel.x = xPos; costLabel.y = yPos + 40; costLabel.visible = true; costLabel.alpha = 1; } } } function startLevel(levelNumber) { currentLevel = levelNumber; // Show plant selector for all levels gameState = 'plantSelector'; // Clear level selection elements for (var i = levelButtons.length - 1; i >= 0; i--) { levelButtons[i].destroy(); } levelButtons = []; // Hide game elements hideGameElements(); // Show plant selector var plantSelector = new PlantSelector(); game.addChild(plantSelector); } function resetGameForLevel(levelNumber) { // Clean up any existing night plant buttons first cleanupNightPlantButtons(); // Reset all game variables sunPoints = 150; currentWave = 1; zombiesKilled = 0; // Reset kill counter // Special case for level 5: set to 3 waves with reasonable zombie count if (levelNumber === 5) { maxZombiesInWave = 30; // Manageable number of zombies per wave maxWaves = 3; // Exactly 3 waves } else { maxZombiesInWave = 1000; // Set to 1000 zombies per wave maxWaves = 3 + levelNumber; // More waves for higher levels } waveSpawnTimer = 0; zombiesInWave = 0; selectedPlantType = null; shovelSelected = false; // Set mode based on level number var isNightMode = levelNumber > 5 && levelNumber <= 10 || levelNumber === 19; var isPoolMode = levelNumber > 10 && levelNumber <= 15; var isRoofMode = levelNumber > 15; if (isRoofMode && levelNumber === 19) { // Night roof boss fight - defense planning mode with 10,000 suns game.setBackgroundColor(0x0F0F23); // Very dark night background sunPoints = 10000; // Start with 10,000 suns for defense planning hideDayPlants(); // Show only specific plants for roof levels (no sun mushrooms) peashooterButton.visible = true; wallnutButton.visible = true; carnivorousplantButton.visible = true; doomshroomButton.visible = true; peashooterCost.visible = true; wallnutCost.visible = true; carnivorousplantCost.visible = true; doomshroomCost.visible = true; // Show catapult plants for night roof boss level melonpultaButton.visible = true; coltapultaButton.visible = true; lanzamaizButton.visible = true; melonpultaCost.visible = true; coltapultaCost.visible = true; lanzamaizCost.visible = true; // Show pot plant for roof levels potButton.visible = true; potCost.visible = true; // Show boss health display bossHealthDisplay.visible = true; maxZombiesInWave = 50 + (levelNumber - 1) * 15; // Much harder boss fight maxWaves = 5 + levelNumber; // Many waves for boss } else if (isRoofMode) { // Day roof levels - only allow sunflower, doomshroom, wallnut, carnivorousplant, melonpulta, coltapulta, lanzamaiz game.setBackgroundColor(0x696969); // Gray roof background sunPoints = 125; // Medium-low sun for roof levels hideDayPlants(); // Show only specific plants for roof levels sunflowerButton.visible = true; wallnutButton.visible = true; carnivorousplantButton.visible = true; doomshroomButton.visible = true; sunflowerCost.visible = true; wallnutCost.visible = true; carnivorousplantCost.visible = true; doomshroomCost.visible = true; // Show catapult plants for day roof levels melonpultaButton.visible = true; coltapultaButton.visible = true; lanzamaizButton.visible = true; melonpultaCost.visible = true; coltapultaCost.visible = true; lanzamaizCost.visible = true; // Show pot plant for roof levels potButton.visible = true; potCost.visible = true; } else if (isPoolMode) { game.setBackgroundColor(0x006994); // Blue pool background sunPoints = 100; // Medium sun for pool levels showDayPlants(); // Pool levels use day plants } else if (isNightMode) { game.setBackgroundColor(0x1a1a2e); // Dark blue night background sunPoints = 75; // Start with less sun at night hideDayPlants(); // Hide day plants createNightPlantButtons(); // Show night plants } else { game.setBackgroundColor(0x228B22); // Green day background showDayPlants(); // Show day plants // Hide boss health display for non-boss levels bossHealthDisplay.visible = false; } // Clear all arrays and destroy objects for (var i = plants.length - 1; i >= 0; i--) { plants[i].destroy(); } plants = []; for (var j = zombies.length - 1; j >= 0; j--) { zombies[j].destroy(); } zombies = []; for (var k = peas.length - 1; k >= 0; k--) { peas[k].destroy(); } peas = []; for (var c = corns.length - 1; c >= 0; c--) { corns[c].destroy(); } corns = []; for (var l = suns.length - 1; l >= 0; l--) { suns[l].destroy(); } suns = []; for (var m = lawnmowers.length - 1; m >= 0; m--) { lawnmowers[m].destroy(); } lawnmowers = []; for (var t = tombstones.length - 1; t >= 0; t--) { tombstones[t].destroy(); } tombstones = []; // Clear boss-specific objects if (conveyorBelt) { conveyorBelt.destroy(); conveyorBelt = null; } if (robotBoss) { robotBoss.destroy(); robotBoss = null; } for (var f = fireballs.length - 1; f >= 0; f--) { fireballs[f].destroy(); } fireballs = []; // Create boss-specific objects for level 19 if (isRoofMode && levelNumber === 19) { // Create conveyor belt for boss level conveyorBelt = new ConveyorBelt(); conveyorBelt.x = 1024; // Center of screen conveyorBelt.y = 200; // Top area game.addChild(conveyorBelt); // Create robot boss robotBoss = new RobotBoss(); robotBoss.x = 1800; // Right side of screen robotBoss.y = 1000; // Middle vertically game.addChild(robotBoss); } // Spawn tombstones for night levels after clearing existing ones (but not for boss fight) if (isNightMode && levelNumber !== 19) { spawnTombstones(); } // Reset seed cooldowns for (var seedType in seedCooldowns) { seedCooldowns[seedType] = 0; } // Reinitialize lawnmowers for (var row = 0; row < gridRows; row++) { var lawnmower = new Lawnmower(); lawnmower.x = 220; lawnmower.y = gridStartY + row * cellSize; game.addChild(lawnmower); lawnmowers.push(lawnmower); } // Add water tiles for pool levels (11-15) if (isPoolMode) { // Add water in the middle rows (approximately rows 4-8) for all columns for (var waterRow = 4; waterRow <= 8; waterRow++) { for (var waterCol = 0; waterCol < gridCols; waterCol++) { var waterTile = LK.getAsset('water', { anchorX: 0.5, anchorY: 0.5, x: gridStartX + waterCol * cellSize, y: gridStartY + waterRow * cellSize, alpha: 0.7 }); game.addChild(waterTile); waterTiles.push(waterTile); } } } // Update grid tiles for all levels updateGridTiles(levelNumber); // Update bushes visibility based on level type if (isRoofMode) { // Hide bushes for roof levels bushes.visible = false; bushes2.visible = false; bushes3.visible = false; bushes4.visible = false; bushes5.visible = false; bushes6.visible = false; bushes7.visible = false; bushes8.visible = false; bushes9.visible = false; bushes10.visible = false; bushes11.visible = false; bushes12.visible = false; bushes13.visible = false; } else { // Show bushes for non-roof levels bushes.visible = true; bushes2.visible = true; bushes3.visible = true; bushes4.visible = true; bushes5.visible = true; bushes6.visible = true; bushes7.visible = true; bushes8.visible = true; bushes9.visible = true; bushes10.visible = true; bushes11.visible = true; bushes12.visible = true; bushes13.visible = true; } // Add 3 vertical lines of pots for roof levels if (isRoofMode) { // Create 3 vertical lines of pots at the first 3 columns from the left (0, 1, 2) var potColumns = [0, 1, 2]; for (var potColIndex = 0; potColIndex < potColumns.length; potColIndex++) { var potCol = potColumns[potColIndex]; for (var potRow = 0; potRow < gridRows; potRow++) { var potPlant = new Plant('pot'); potPlant.x = gridStartX + potCol * cellSize; potPlant.y = gridStartY + potRow * cellSize; game.addChild(potPlant); plants.push(potPlant); } } } // Update displays updateSunDisplay(); waveDisplay.setText('Wave: ' + currentWave + '/' + maxWaves); } // Button event handlers sunflowerButton.down = function (x, y, obj) { if (canPlant('sunflower')) { selectedPlantType = 'sunflower'; shovelSelected = false; // Button press animation tween(sunflowerButton, { scaleX: 0.8, scaleY: 0.8 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(sunflowerButton, { scaleX: 0.6, scaleY: 0.6 }, { duration: 100, easing: tween.bounceOut }); } }); sunflowerButton.alpha = 1; sunflowerButton.tint = 0x808080; peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5; peashooterButton.tint = 0xffffff; wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5; wallnutButton.tint = 0xffffff; potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5; potatomineButton.tint = 0xffffff; carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5; carnivorousplantButton.tint = 0xffffff; doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5; doomshroomButton.tint = 0xffffff; repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5; repeaterButton.tint = 0xffffff; threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5; threepeaterButton.tint = 0xffffff; spikeButton.alpha = isSeedReady('spikeweed') ? 1 : 0.5; spikeButton.tint = 0xffffff; splitpeaterButton.alpha = isSeedReady('splitpeater') ? 1 : 0.5; splitpeaterButton.tint = 0xffffff; shovelButton.alpha = 1; shovelButton.tint = 0xffffff; } }; peashooterButton.down = function (x, y, obj) { if (canPlant('peashooter')) { selectedPlantType = 'peashooter'; shovelSelected = false; peashooterButton.alpha = 1; peashooterButton.tint = 0x808080; sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5; sunflowerButton.tint = 0xffffff; wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5; wallnutButton.tint = 0xffffff; potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5; potatomineButton.tint = 0xffffff; carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5; carnivorousplantButton.tint = 0xffffff; doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5; doomshroomButton.tint = 0xffffff; repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5; repeaterButton.tint = 0xffffff; threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5; threepeaterButton.tint = 0xffffff; shovelButton.alpha = 1; shovelButton.tint = 0xffffff; } }; wallnutButton.down = function (x, y, obj) { if (canPlant('wallnut')) { selectedPlantType = 'wallnut'; shovelSelected = false; wallnutButton.alpha = 1; wallnutButton.tint = 0x808080; sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5; sunflowerButton.tint = 0xffffff; peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5; peashooterButton.tint = 0xffffff; potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5; potatomineButton.tint = 0xffffff; carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5; carnivorousplantButton.tint = 0xffffff; doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5; doomshroomButton.tint = 0xffffff; repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5; repeaterButton.tint = 0xffffff; threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5; threepeaterButton.tint = 0xffffff; shovelButton.alpha = 1; shovelButton.tint = 0xffffff; } }; potatomineButton.down = function (x, y, obj) { if (canPlant('potatomine')) { selectedPlantType = 'potatomine'; shovelSelected = false; potatomineButton.alpha = 1; potatomineButton.tint = 0x808080; sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5; sunflowerButton.tint = 0xffffff; peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5; peashooterButton.tint = 0xffffff; wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5; wallnutButton.tint = 0xffffff; carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5; carnivorousplantButton.tint = 0xffffff; doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5; doomshroomButton.tint = 0xffffff; repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5; repeaterButton.tint = 0xffffff; threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5; threepeaterButton.tint = 0xffffff; shovelButton.alpha = 1; shovelButton.tint = 0xffffff; } }; carnivorousplantButton.down = function (x, y, obj) { if (canPlant('carnivorousplant')) { selectedPlantType = 'carnivorousplant'; shovelSelected = false; carnivorousplantButton.alpha = 1; carnivorousplantButton.tint = 0x808080; sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5; sunflowerButton.tint = 0xffffff; peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5; peashooterButton.tint = 0xffffff; wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5; wallnutButton.tint = 0xffffff; potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5; potatomineButton.tint = 0xffffff; doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5; doomshroomButton.tint = 0xffffff; repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5; repeaterButton.tint = 0xffffff; threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5; threepeaterButton.tint = 0xffffff; shovelButton.alpha = 1; shovelButton.tint = 0xffffff; } }; doomshroomButton.down = function (x, y, obj) { if (canPlant('doomshroom')) { selectedPlantType = 'doomshroom'; shovelSelected = false; doomshroomButton.alpha = 1; doomshroomButton.tint = 0x808080; sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5; sunflowerButton.tint = 0xffffff; peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5; peashooterButton.tint = 0xffffff; wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5; wallnutButton.tint = 0xffffff; potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5; potatomineButton.tint = 0xffffff; carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5; carnivorousplantButton.tint = 0xffffff; repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5; repeaterButton.tint = 0xffffff; threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5; threepeaterButton.tint = 0xffffff; shovelButton.alpha = 1; shovelButton.tint = 0xffffff; } }; repeaterButton.down = function (x, y, obj) { if (canPlant('repeater')) { selectedPlantType = 'repeater'; shovelSelected = false; repeaterButton.alpha = 1; repeaterButton.tint = 0x808080; sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5; sunflowerButton.tint = 0xffffff; peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5; peashooterButton.tint = 0xffffff; wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5; wallnutButton.tint = 0xffffff; potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5; potatomineButton.tint = 0xffffff; carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5; carnivorousplantButton.tint = 0xffffff; doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5; doomshroomButton.tint = 0xffffff; threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5; threepeaterButton.tint = 0xffffff; shovelButton.alpha = 1; shovelButton.tint = 0xffffff; } }; threepeaterButton.down = function (x, y, obj) { if (canPlant('threepeater')) { selectedPlantType = 'threepeater'; shovelSelected = false; threepeaterButton.alpha = 1; threepeaterButton.tint = 0x808080; sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5; sunflowerButton.tint = 0xffffff; peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5; peashooterButton.tint = 0xffffff; wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5; wallnutButton.tint = 0xffffff; potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5; potatomineButton.tint = 0xffffff; carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5; carnivorousplantButton.tint = 0xffffff; doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5; doomshroomButton.tint = 0xffffff; repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5; repeaterButton.tint = 0xffffff; shovelButton.alpha = 1; shovelButton.tint = 0xffffff; } }; spikeButton.down = function (x, y, obj) { if (canPlant('spikeweed')) { selectedPlantType = 'spikeweed'; shovelSelected = false; spikeButton.alpha = 1; spikeButton.tint = 0x808080; sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5; sunflowerButton.tint = 0xffffff; peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5; peashooterButton.tint = 0xffffff; wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5; wallnutButton.tint = 0xffffff; potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5; potatomineButton.tint = 0xffffff; carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5; carnivorousplantButton.tint = 0xffffff; doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5; doomshroomButton.tint = 0xffffff; repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5; repeaterButton.tint = 0xffffff; threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5; threepeaterButton.tint = 0xffffff; splitpeaterButton.alpha = isSeedReady('splitpeater') ? 1 : 0.5; splitpeaterButton.tint = 0xffffff; shovelButton.alpha = 1; shovelButton.tint = 0xffffff; } }; splitpeaterButton.down = function (x, y, obj) { if (canPlant('splitpeater')) { selectedPlantType = 'splitpeater'; shovelSelected = false; splitpeaterButton.alpha = 1; splitpeaterButton.tint = 0x808080; sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5; sunflowerButton.tint = 0xffffff; peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5; peashooterButton.tint = 0xffffff; wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5; wallnutButton.tint = 0xffffff; potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5; potatomineButton.tint = 0xffffff; carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5; carnivorousplantButton.tint = 0xffffff; doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5; doomshroomButton.tint = 0xffffff; repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5; repeaterButton.tint = 0xffffff; threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5; threepeaterButton.tint = 0xffffff; spikeButton.alpha = isSeedReady('spikeweed') ? 1 : 0.5; spikeButton.tint = 0xffffff; shovelButton.alpha = 1; shovelButton.tint = 0xffffff; } }; restartButton.down = function (x, y, obj) { // Reset all game variables sunPoints = 150; currentWave = 1; maxZombiesInWave = 30; waveSpawnTimer = 0; zombiesInWave = 0; selectedPlantType = null; shovelSelected = false; gameState = 'playing'; // Start background music LK.playMusic('backgroundMusic', { loop: true }); // Clear all arrays and destroy objects for (var i = plants.length - 1; i >= 0; i--) { plants[i].destroy(); } plants = []; for (var j = zombies.length - 1; j >= 0; j--) { zombies[j].destroy(); } zombies = []; for (var k = peas.length - 1; k >= 0; k--) { peas[k].destroy(); } peas = []; for (var c = corns.length - 1; c >= 0; c--) { corns[c].destroy(); } corns = []; for (var l = suns.length - 1; l >= 0; l--) { suns[l].destroy(); } suns = []; for (var m = lawnmowers.length - 1; m >= 0; m--) { lawnmowers[m].destroy(); } lawnmowers = []; for (var n = waterTiles.length - 1; n >= 0; n--) { waterTiles[n].destroy(); } waterTiles = []; for (var t = tombstones.length - 1; t >= 0; t--) { tombstones[t].destroy(); } tombstones = []; // Reset seed cooldowns for (var seedType in seedCooldowns) { seedCooldowns[seedType] = 0; } // Reinitialize lawnmowers for (var row = 0; row < gridRows; row++) { var lawnmower = new Lawnmower(); lawnmower.x = 220; lawnmower.y = gridStartY + row * cellSize; game.addChild(lawnmower); lawnmowers.push(lawnmower); } // Update displays updateSunDisplay(); waveDisplay.setText('Wave: ' + currentWave + '/' + maxWaves); // Reset button states sunflowerButton.alpha = 1; sunflowerButton.tint = 0xffffff; peashooterButton.alpha = 1; peashooterButton.tint = 0xffffff; wallnutButton.alpha = 1; wallnutButton.tint = 0xffffff; potatomineButton.alpha = 1; potatomineButton.tint = 0xffffff; carnivorousplantButton.alpha = 1; carnivorousplantButton.tint = 0xffffff; doomshroomButton.alpha = 1; doomshroomButton.tint = 0xffffff; repeaterButton.alpha = 1; repeaterButton.tint = 0xffffff; threepeaterButton.alpha = 1; threepeaterButton.tint = 0xffffff; spikeButton.alpha = 1; spikeButton.tint = 0xffffff; splitpeaterButton.alpha = 1; splitpeaterButton.tint = 0xffffff; shovelButton.alpha = 1; shovelButton.tint = 0xffffff; }; lilypadButton.down = function (x, y, obj) { if (canPlant('lilypad')) { selectedPlantType = 'lilypad'; shovelSelected = false; lilypadButton.alpha = 1; lilypadButton.tint = 0x808080; sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5; sunflowerButton.tint = 0xffffff; peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5; peashooterButton.tint = 0xffffff; wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5; wallnutButton.tint = 0xffffff; potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5; potatomineButton.tint = 0xffffff; carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5; carnivorousplantButton.tint = 0xffffff; doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5; doomshroomButton.tint = 0xffffff; repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5; repeaterButton.tint = 0xffffff; threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5; threepeaterButton.tint = 0xffffff; spikeButton.alpha = isSeedReady('spikeweed') ? 1 : 0.5; spikeButton.tint = 0xffffff; splitpeaterButton.alpha = isSeedReady('splitpeater') ? 1 : 0.5; splitpeaterButton.tint = 0xffffff; shovelButton.alpha = 1; shovelButton.tint = 0xffffff; } }; shovelButton.down = function (x, y, obj) { shovelSelected = true; selectedPlantType = null; shovelButton.alpha = 1; shovelButton.tint = 0x808080; sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5; sunflowerButton.tint = 0xffffff; peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5; peashooterButton.tint = 0xffffff; wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5; wallnutButton.tint = 0xffffff; potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5; potatomineButton.tint = 0xffffff; carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5; carnivorousplantButton.tint = 0xffffff; doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5; doomshroomButton.tint = 0xffffff; repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5; repeaterButton.tint = 0xffffff; threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5; threepeaterButton.tint = 0xffffff; spikeButton.alpha = 1; spikeButton.tint = 0xffffff; lilypadButton.alpha = 1; lilypadButton.tint = 0xffffff; }; // Add event handlers for catapult plant buttons melonpultaButton.down = function (x, y, obj) { if (canPlant('melonpulta')) { selectedPlantType = 'melonpulta'; shovelSelected = false; melonpultaButton.alpha = 1; melonpultaButton.tint = 0x808080; // Reset other buttons sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5; sunflowerButton.tint = 0xffffff; wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5; wallnutButton.tint = 0xffffff; carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5; carnivorousplantButton.tint = 0xffffff; doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5; doomshroomButton.tint = 0xffffff; coltapultaButton.alpha = isSeedReady('coltapulta') ? 1 : 0.5; coltapultaButton.tint = 0xffffff; lanzamaizButton.alpha = isSeedReady('lanzamaiz') ? 1 : 0.5; lanzamaizButton.tint = 0xffffff; shovelButton.alpha = 1; shovelButton.tint = 0xffffff; } }; coltapultaButton.down = function (x, y, obj) { if (canPlant('coltapulta')) { selectedPlantType = 'coltapulta'; shovelSelected = false; coltapultaButton.alpha = 1; coltapultaButton.tint = 0x808080; // Reset other buttons sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5; sunflowerButton.tint = 0xffffff; wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5; wallnutButton.tint = 0xffffff; carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5; carnivorousplantButton.tint = 0xffffff; doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5; doomshroomButton.tint = 0xffffff; melonpultaButton.alpha = isSeedReady('melonpulta') ? 1 : 0.5; melonpultaButton.tint = 0xffffff; lanzamaizButton.alpha = isSeedReady('lanzamaiz') ? 1 : 0.5; lanzamaizButton.tint = 0xffffff; shovelButton.alpha = 1; shovelButton.tint = 0xffffff; } }; lanzamaizButton.down = function (x, y, obj) { if (canPlant('lanzamaiz')) { selectedPlantType = 'lanzamaiz'; shovelSelected = false; lanzamaizButton.alpha = 1; lanzamaizButton.tint = 0x808080; // Reset other buttons sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5; sunflowerButton.tint = 0xffffff; wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5; wallnutButton.tint = 0xffffff; carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5; carnivorousplantButton.tint = 0xffffff; doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5; doomshroomButton.tint = 0xffffff; melonpultaButton.alpha = isSeedReady('melonpulta') ? 1 : 0.5; melonpultaButton.tint = 0xffffff; coltapultaButton.alpha = isSeedReady('coltapulta') ? 1 : 0.5; coltapultaButton.tint = 0xffffff; shovelButton.alpha = 1; shovelButton.tint = 0xffffff; } }; // Add event handler for pot plant button potButton.down = function (x, y, obj) { if (canPlant('pot')) { selectedPlantType = 'pot'; shovelSelected = false; potButton.alpha = 1; potButton.tint = 0x808080; // Reset other buttons sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5; sunflowerButton.tint = 0xffffff; wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5; wallnutButton.tint = 0xffffff; carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5; carnivorousplantButton.tint = 0xffffff; doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5; doomshroomButton.tint = 0xffffff; melonpultaButton.alpha = isSeedReady('melonpulta') ? 1 : 0.5; melonpultaButton.tint = 0xffffff; coltapultaButton.alpha = isSeedReady('coltapulta') ? 1 : 0.5; coltapultaButton.tint = 0xffffff; lanzamaizButton.alpha = isSeedReady('lanzamaiz') ? 1 : 0.5; lanzamaizButton.tint = 0xffffff; shovelButton.alpha = 1; shovelButton.tint = 0xffffff; } }; // Create tombstone button for minigames - positioned next to exit button var tombstoneButton = LK.getAsset('tombstone', { anchorX: 0.5, anchorY: 0.5, x: 1500, y: 120, scaleX: 0.6, scaleY: 0.6 }); game.addChild(tombstoneButton); var tombstoneText = new Text2('MINIGAMES', { size: 16, fill: 0xFFFFFF }); tombstoneText.anchor.set(0.5, 0); tombstoneText.x = 1500; tombstoneText.y = 90; game.addChild(tombstoneText); // Tombstone button event handler - shows minigame selector tombstoneButton.down = function (x, y, obj) { gameState = 'minigame'; // Hide game elements hideGameElements(); // Show minigame selector minigameSelector = new MinigameSelector(); game.addChild(minigameSelector); }; exitLevelButton.down = function (x, y, obj) { // Clear all game objects and return to level selection // Clear plants first to ensure they visually disappear for (var i = plants.length - 1; i >= 0; i--) { plants[i].destroy(); } plants = []; for (var j = zombies.length - 1; j >= 0; j--) { zombies[j].destroy(); } zombies = []; for (var k = peas.length - 1; k >= 0; k--) { peas[k].destroy(); } peas = []; for (var c = corns.length - 1; c >= 0; c--) { corns[c].destroy(); } corns = []; for (var l = suns.length - 1; l >= 0; l--) { suns[l].destroy(); } suns = []; for (var m = lawnmowers.length - 1; m >= 0; m--) { lawnmowers[m].destroy(); } lawnmowers = []; for (var n = waterTiles.length - 1; n >= 0; n--) { waterTiles[n].destroy(); } waterTiles = []; for (var t = tombstones.length - 1; t >= 0; t--) { tombstones[t].destroy(); } tombstones = []; // Clear seed cooldowns for night mode for (var seedType in seedCooldowns) { seedCooldowns[seedType] = 0; } // Stop background music LK.stopMusic(); // Return to level selection showLevelSelection(); }; // Game event handlers game.down = function (x, y, obj) { // Special handling for bowling minigame if (gameState === 'minigame' && currentMinigame && currentMinigame.walnutsAvailable > 0) { // Check if click position is on a grass tile var gridPos = getGridPosition(x, y); if (!gridPos) { return; // Not on grid, can't place walnut } // Place walnut that will roll toward zombies var walnut = LK.getAsset('wallnut', { anchorX: 0.5, anchorY: 0.5, x: gridPos.x, // Use grid position instead of click position y: gridPos.y, // Use grid position instead of click position scaleX: 1.0, scaleY: 1.0 }); game.addChild(walnut); // Set walnut physics for rolling walnut.speedX = 6; // Move right toward zombies walnut.speedY = 0; walnut.damage = 50; walnut.destroyed = false; walnut.bouncedUp = false; // Track if walnut has bounced upward // Decrease available walnuts currentMinigame.walnutsAvailable--; // Update walnut physics and collision walnut.update = function () { if (walnut.destroyed) return; walnut.x += walnut.speedX; walnut.y += walnut.speedY; walnut.rotation += 0.2; // Rolling animation // Check collision with zombies for (var i = currentMinigame.zombies.length - 1; i >= 0; i--) { var zombie = currentMinigame.zombies[i]; if (Math.abs(walnut.x - zombie.x) < 60 && Math.abs(walnut.y - zombie.y) < 60) { // Hit zombie if (!zombie.destroyed) { zombie.destroyed = true; zombiesKilled++; // Increment kill counter currentMinigame.score++; // Update score display var scoreText = currentMinigame.children.find(function (child) { return child.text && child.text.indexOf('Zombies eliminados:') >= 0; }); if (scoreText) { scoreText.setText('Zombies eliminados: ' + currentMinigame.score + '/1000'); } LK.getSound('explode').play(); // Zombie death animation tween(zombie, { scaleX: 0, scaleY: 0, rotation: Math.PI, alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { zombie.destroy(); } }); // Remove zombie from array currentMinigame.zombies.splice(i, 1); // Reset bounced up state when killing a zombie - allows new bounce direction walnut.bouncedUp = false; // Bounce walnut to the right and up/down walnut.speedX = Math.abs(walnut.speedX) * 0.8; // Always bounce right, reduce speed slightly // Random bounce direction: either up or down if (Math.random() < 0.5) { walnut.speedY = -Math.abs(walnut.speedY || 2) * 1.2; // Bounce up walnut.bouncedUp = true; // Mark that walnut bounced upward } else { walnut.speedY = Math.abs(walnut.speedY || 2) * 1.2; // Bounce down walnut.bouncedUp = false; // Mark that walnut bounced downward } // Visual bounce effect tween(walnut, { scaleX: 1.3, scaleY: 0.7 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(walnut, { scaleX: 1.0, scaleY: 1.0 }, { duration: 100, easing: tween.easeIn }); } }); break; } } } // Add gravity effect to Y movement if (walnut.speedY !== 0) { walnut.speedY += 0.1; // Gravity pulls down } // Bounce off left boundary only if (walnut.x < 300) { walnut.speedX = Math.abs(walnut.speedX); // Bounce right } // Disappear when hitting bushes (right side around x=1900-2000) if (walnut.x > 1900) { walnut.destroy(); return; } if (walnut.y < gridStartY) { walnut.speedY = Math.abs(walnut.speedY); // Bounce down } if (walnut.y > gridStartY + gridRows * cellSize) { // Only allow upward bounce if walnut hasn't already bounced up, or allow if it bounced up before if (!walnut.bouncedUp) { walnut.speedY = -Math.abs(walnut.speedY * 0.7); // Bounce up with energy loss walnut.bouncedUp = true; // Mark that it bounced upward } else { // If already bounced up, don't bounce down - let it fall off screen walnut.speedY = Math.abs(walnut.speedY); // Continue downward } } // Remove walnut if it moves too slowly (comes to rest) if (Math.abs(walnut.speedX) < 0.5 && Math.abs(walnut.speedY) < 0.5) { walnut.destroy(); } }; return; } if (shovelSelected) { // Remove plant functionality var gridPos = getGridPosition(x, y); if (gridPos) { for (var i = plants.length - 1; i >= 0; i--) { var plant = plants[i]; var plantRow = Math.floor((plant.y - gridStartY + cellSize / 2) / cellSize); var plantCol = Math.floor((plant.x - gridStartX + cellSize / 2) / cellSize); if (plantRow === gridPos.row && plantCol === gridPos.col) { plant.destroy(); plants.splice(i, 1); shovelSelected = false; // Reset button alphas and tints sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5; sunflowerButton.tint = 0xffffff; peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5; peashooterButton.tint = 0xffffff; wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5; wallnutButton.tint = 0xffffff; potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5; potatomineButton.tint = 0xffffff; carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5; carnivorousplantButton.tint = 0xffffff; doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5; doomshroomButton.tint = 0xffffff; repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5; repeaterButton.tint = 0xffffff; threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5; threepeaterButton.tint = 0xffffff; shovelButton.alpha = 1; shovelButton.tint = 0xffffff; break; } } } } else if (selectedPlantType) { var gridPos = getGridPosition(x, y); if (gridPos && canPlantOnPosition(selectedPlantType, gridPos.row, gridPos.col)) { // For conveyor belt seeds (boss level), don't check sun cost or cooldown var isBossLevel = currentLevel === 19; var canPlaceFromBelt = isBossLevel && selectedPlantType && seedCooldowns[selectedPlantType] === 0; var canPlaceNormal = !isBossLevel && canPlant(selectedPlantType); if (canPlaceFromBelt || canPlaceNormal) { // Only deduct sun points for non-boss levels if (!isBossLevel) { sunPoints -= plantCosts[selectedPlantType]; updateSunDisplay(); // Start seed recharge for normal levels seedCooldowns[selectedPlantType] = seedRechargeTime; } var newPlant = new Plant(selectedPlantType); newPlant.x = gridPos.x; newPlant.y = gridPos.y; // Start small and grow newPlant.scaleX = 0.1; newPlant.scaleY = 0.1; newPlant.alpha = 0.5; game.addChild(newPlant); plants.push(newPlant); // Growing animation tween(newPlant, { scaleX: 1.0, scaleY: 1.0, alpha: 1.0 }, { duration: 500, easing: tween.bounceOut }); LK.getSound('plant').play(); selectedPlantType = null; // Reset button alphas and tints based on cooldown status sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5; sunflowerButton.tint = 0xffffff; peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5; peashooterButton.tint = 0xffffff; wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5; wallnutButton.tint = 0xffffff; potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5; potatomineButton.tint = 0xffffff; carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5; carnivorousplantButton.tint = 0xffffff; doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5; doomshroomButton.tint = 0xffffff; repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5; repeaterButton.tint = 0xffffff; threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5; threepeaterButton.tint = 0xffffff; shovelButton.alpha = 1; shovelButton.tint = 0xffffff; } } } }; // Main game update loop game.update = function () { if (gameState === 'loading') { // Loading screen handles its own updates return; } else if (gameState === 'levelSelect') { // Level selection screen - no game updates needed return; } else if (gameState === 'minigame') { // Minigame handles its own updates - but ensure minigame exists if (currentMinigame && currentMinigame.update) { // Let minigame handle its own update } return; } else if (gameState === 'plantSelector') { // Plant selector handles its own updates return; } else if (gameState !== 'playing') { return; } // Wave management waveSpawnTimer++; if (currentWave <= maxWaves) { // Only spawn zombies if not boss level (level 19) - boss will spawn them if (currentLevel !== 19 && waveSpawnTimer >= 1500 && zombiesInWave < maxZombiesInWave) { // Spawn 1 zombie every 25 seconds (1500 ticks) spawnZombie(); zombiesInWave++; waveSpawnTimer = 0; } else if (zombiesInWave >= maxZombiesInWave && zombies.length === 0) { // Wave completed currentWave++; zombiesInWave = 0; maxZombiesInWave += 5; // Increase difficulty more waveDisplay.setText('Wave: ' + currentWave + '/' + maxWaves); } } // Pea vs Zombie collision for (var i = peas.length - 1; i >= 0; i--) { var pea = peas[i]; var peaHit = false; for (var j = zombies.length - 1; j >= 0; j--) { var zombie = zombies[j]; if (Math.abs(pea.x - zombie.x) < 50 && Math.abs(pea.y - zombie.y) < 50) { // Play zombie hit sound LK.getSound('zombieHit').play(); // Pea impact effect - flash and small explosion tween(pea, { scaleX: 2.0, scaleY: 2.0, alpha: 0, tint: 0xffff00 }, { duration: 150, easing: tween.easeOut }); if (zombie.takeDamage(pea.damage)) { zombiesKilled++; // Increment kill counter zombie.destroy(); zombies.splice(j, 1); } pea.destroy(); peas.splice(i, 1); peaHit = true; break; } } if (!peaHit && pea.x > 2200) { pea.destroy(); peas.splice(i, 1); } } // Corn vs Zombie collision for (var i = corns.length - 1; i >= 0; i--) { var corn = corns[i]; var cornHit = false; for (var j = zombies.length - 1; j >= 0; j--) { var zombie = zombies[j]; if (Math.abs(corn.x - zombie.x) < 50 && Math.abs(corn.y - zombie.y) < 50) { // Play zombie hit sound LK.getSound('zombieHit').play(); // Corn impact effect - splash effect with different color for butter var impactColor = corn.isButter ? 0xffff99 : 0x90EE90; tween(corn, { scaleX: 1.8, scaleY: 1.8, alpha: 0, tint: impactColor, rotation: Math.PI / 4 }, { duration: 200, easing: tween.easeOut }); if (zombie.takeDamage(corn.damage)) { zombiesKilled++; // Increment kill counter zombie.destroy(); zombies.splice(j, 1); } // Apply butter effect if this corn is butter if (corn.isButter) { zombie.butterSlowTime = 600; // 10 seconds at 60fps } corn.destroy(); corns.splice(i, 1); cornHit = true; break; } } if (!cornHit && corn.x > 2200) { corn.destroy(); corns.splice(i, 1); } } // Remove destroyed suns for (var k = suns.length - 1; k >= 0; k--) { if (suns[k].collected) { suns.splice(k, 1); } } // Update seed cooldowns for (var seedType in seedCooldowns) { if (seedCooldowns[seedType] > 0) { seedCooldowns[seedType]--; } } // Lawnmower activation - check if zombies reach the left side for (var m = zombies.length - 1; m >= 0; m--) { var zombie = zombies[m]; if (zombie.x < 250) { var zombieRow = Math.floor((zombie.y - gridStartY + cellSize / 2) / cellSize); if (zombieRow >= 0 && zombieRow < lawnmowers.length && lawnmowers[zombieRow] && !lawnmowers[zombieRow].activated) { var currentLawnmower = lawnmowers[zombieRow]; currentLawnmower.activated = true; // Add visual effect when lawnmower activates tween(currentLawnmower, { tint: 0xff0000 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(currentLawnmower, { tint: 0xffffff }, { duration: 200, easing: tween.easeIn }); } }); // Play lawnmower sound effect LK.getSound('shoot').play(); // Remove zombie that triggered the lawnmower zombie.destroy(); zombies.splice(m, 1); } } } // Remove lawnmowers that are off screen for (var n = lawnmowers.length - 1; n >= 0; n--) { if (lawnmowers[n].x > 2200) { lawnmowers.splice(n, 1); } } // Lawnmower vs zombie collision for (var p = 0; p < lawnmowers.length; p++) { var lawnmower = lawnmowers[p]; if (lawnmower && lawnmower.activated) { for (var q = zombies.length - 1; q >= 0; q--) { var zombie = zombies[q]; // Check if zombie is in the same row and within collision range var zombieRow = Math.floor((zombie.y - gridStartY + cellSize / 2) / cellSize); var lawnmowerRow = Math.floor((lawnmower.y - gridStartY + cellSize / 2) / cellSize); if (zombieRow === lawnmowerRow && Math.abs(lawnmower.x - zombie.x) < 100) { zombiesKilled++; // Increment kill counter // Zombie destruction with visual effect tween(zombie, { scaleX: 0, scaleY: 0, rotation: Math.PI * 2, alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { zombie.destroy(); } }); zombies.splice(q, 1); LK.getSound('explode').play(); } } } } // Update button visual states based on cooldowns sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5; peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5; wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5; potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5; carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5; doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5; repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5; threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5; spikeButton.alpha = isSeedReady('spikeweed') ? 1 : 0.5; // Fireball vs Plant collision (boss level only) if (currentLevel === 19 && fireballs && fireballs.length > 0) { for (var fb = fireballs.length - 1; fb >= 0; fb--) { var fireball = fireballs[fb]; if (fireball && fireball.x !== undefined) { var fireballHit = false; for (var pl = plants.length - 1; pl >= 0; pl--) { var plant = plants[pl]; if (Math.abs(fireball.x - plant.x) < 60 && Math.abs(fireball.y - plant.y) < 60) { if (plant.takeDamage(fireball.damage)) { plant.destroy(); plants.splice(pl, 1); } fireball.destroy(); fireballs.splice(fb, 1); fireballHit = true; break; } } if (!fireballHit && fireball.x < -100) { fireball.destroy(); fireballs.splice(fb, 1); } } } // Pea vs Robot Boss collision for (var peaIdx = peas.length - 1; peaIdx >= 0; peaIdx--) { var pea = peas[peaIdx]; if (robotBoss && Math.abs(pea.x - robotBoss.x) < 100 && Math.abs(pea.y - robotBoss.y) < 150) { if (robotBoss.takeDamage(pea.damage)) { // Boss defeated, handled in RobotBoss class } pea.destroy(); peas.splice(peaIdx, 1); } } } // Check win condition checkWinCondition(); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BowlingMinigame = Container.expand(function () {
var self = Container.call(this);
self.completed = false;
self.timeLeft = -1; // No time limit
self.score = 0;
self.targetScore = 1000; // Need to hit 1000 zombies
self.zombies = [];
self.walnuts = [];
self.walnutsAvailable = 0;
self.maxWalnuts = 5;
// Create background
var bg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 15,
scaleY: 20,
alpha: 0.4,
tint: 0x654321 // Brown background
});
self.addChild(bg);
// Title text
var titleText = new Text2('¡LANZA NUECES A 1000 ZOMBIES!', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 300;
self.addChild(titleText);
// Instructions text
var instructionText = new Text2('Sin límite de tiempo - Elimina 1000 zombies', {
size: 40,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 1024;
instructionText.y = 400;
self.addChild(instructionText);
// Timer text
var timerText = new Text2('Tiempo: 60', {
size: 60,
fill: 0xFFFF00
});
timerText.anchor.set(0.5, 0.5);
timerText.x = 1024;
timerText.y = 500;
self.addChild(timerText);
// Score text
var scoreText = new Text2('Zombies eliminados: 0/1000', {
size: 50,
fill: 0x00FF00
});
scoreText.anchor.set(0.5, 0.5);
scoreText.x = 1024;
scoreText.y = 600;
self.addChild(scoreText);
// Walnuts available text
self.walnutsText = new Text2('Nueces: 0', {
size: 40,
fill: 0xFFFFFF
});
self.walnutsText.anchor.set(0.5, 0.5);
self.walnutsText.x = 1024;
self.walnutsText.y = 700;
self.addChild(self.walnutsText);
// Create conveyor belt
var conveyorBelt = LK.getAsset('conveyorBelt', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2000,
scaleX: 1.5,
scaleY: 1.5
});
self.addChild(conveyorBelt);
// Conveyor belt mechanics
self.conveyorTimer = 0;
self.walnutGenerateRate = 180; // 3 seconds at 60fps
var selectedWalnut = null;
// Spawn zombies periodically
self.zombieTimer = 0;
self.zombieSpawnRate = 120; // 2 seconds at 60fps
self.spawnZombie = function () {
var zombie = LK.getAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5,
x: 2100,
y: 800 + Math.random() * 800,
scaleX: 0.8,
scaleY: 0.8
});
self.addChild(zombie);
zombie.speed = -1 - Math.random() * 2;
zombie.health = 40;
zombie.destroyed = false;
zombie.update = function () {
zombie.x += zombie.speed;
if (zombie.x < 200) {
// Zombie reached the left side
zombie.destroy();
for (var i = self.zombies.length - 1; i >= 0; i--) {
if (self.zombies[i] === zombie) {
self.zombies.splice(i, 1);
break;
}
}
}
};
// Zombies no longer need click interaction - walnuts will collide with them
self.zombies.push(zombie);
};
self.update = function () {
if (self.completed) return;
// No time limit - removed time decrease and timer update
timerText.setText('Sin límite de tiempo');
// Generate walnuts on conveyor belt
self.conveyorTimer++;
if (self.conveyorTimer >= self.walnutGenerateRate && self.walnutsAvailable < self.maxWalnuts) {
self.conveyorTimer = 0;
self.walnutsAvailable++;
self.walnutsText.setText('Nueces: ' + self.walnutsAvailable);
// Visual indication that walnut is available (show icon on conveyor)
var walnutIcon = LK.getAsset('wallnut', {
anchorX: 0.5,
anchorY: 0.5,
x: 900 + (self.walnutsAvailable - 1) * 60,
y: 2000,
scaleX: 0.8,
scaleY: 0.8
});
self.addChild(walnutIcon);
self.walnuts.push(walnutIcon);
// Fade in animation for walnut availability
tween(walnutIcon, {
alpha: 1,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeOut
});
}
// Spawn zombies
self.zombieTimer++;
if (self.zombieTimer >= self.zombieSpawnRate) {
self.zombieTimer = 0;
self.spawnZombie();
}
// Check win condition
if (self.score >= self.targetScore) {
self.endMinigame(true);
return;
}
// No time limit lose condition - removed
// Clean up off-screen zombies
for (var i = self.zombies.length - 1; i >= 0; i--) {
var zombie = self.zombies[i];
if (zombie.x < 100) {
zombie.destroy();
self.zombies.splice(i, 1);
}
}
};
self.endMinigame = function (success) {
self.completed = true;
var resultText = new Text2(success ? '¡EXCELENTE PUNTERÍA!' : '¡INTÉNTALO DE NUEVO!', {
size: 100,
fill: success ? 0x00FF00 : 0xFF0000
});
resultText.anchor.set(0.5, 0.5);
resultText.x = 1024;
resultText.y = 1400;
self.addChild(resultText);
var rewardText = new Text2('', {
size: 60,
fill: 0xFFFF00
});
rewardText.anchor.set(0.5, 0.5);
rewardText.x = 1024;
rewardText.y = 1550;
self.addChild(rewardText);
if (success) {
var reward = Math.floor(self.score * 15);
sunPoints += reward;
rewardText.setText('¡+' + reward + ' soles de recompensa!');
updateSunDisplay();
} else {
rewardText.setText('¡Sigue practicando tu puntería!');
}
var continueText = new Text2('Toca para continuar', {
size: 40,
fill: 0xFFFFFF
});
continueText.anchor.set(0.5, 0.5);
continueText.x = 1024;
continueText.y = 1700;
self.addChild(continueText);
// Make screen clickable to continue
self.down = function (x, y, obj) {
self.destroy();
currentMinigame = null;
// Continue to level 5
startLevel(5);
};
};
return self;
});
var ConveyorBelt = Container.expand(function () {
var self = Container.call(this);
var beltGraphics = self.attachAsset('conveyorBelt', {
anchorX: 0.5,
anchorY: 0.5
});
self.seedTimer = 0;
self.seedGenerateRate = 600; // 10 seconds at 60fps
// Available seeds for boss level - excluding sun producers (sunflower, sunshroom)
self.availableSeeds = ['wallnut', 'carnivorousplant', 'doomshroom', 'melonpulta', 'coltapulta', 'lanzamaiz'];
// Create text display for seed feedback
var seedText = new Text2('', {
size: 40,
fill: 0xFFFFFF
});
seedText.anchor.set(0.5, 0.5);
seedText.x = 0;
seedText.y = -60; // Above the conveyor belt
seedText.alpha = 0; // Start invisible
self.addChild(seedText);
// Create plant display on conveyor belt
var plantDisplay = null; // Will hold the current plant image
self.update = function () {
self.seedTimer++;
if (self.seedTimer >= self.seedGenerateRate) {
self.seedTimer = 0;
// Generate 1 pot and 2 random plants
var generatedSeeds = [];
// Always add pot first
generatedSeeds.push('pot');
seedCooldowns['pot'] = 0;
// Add 2 random plants from available seeds
for (var i = 0; i < 2; i++) {
var randomSeed = self.availableSeeds[Math.floor(Math.random() * self.availableSeeds.length)];
// Ensure no duplicates in the same batch
while (generatedSeeds.indexOf(randomSeed) !== -1) {
randomSeed = self.availableSeeds[Math.floor(Math.random() * self.availableSeeds.length)];
}
generatedSeeds.push(randomSeed);
// Reset cooldown for this seed type - properly reset it
seedCooldowns[randomSeed] = 0;
}
// Automatically select the first plant type for placement
selectedPlantType = generatedSeeds[0];
shovelSelected = false;
// Remove previous plant display if it exists
if (plantDisplay) {
plantDisplay.destroy();
plantDisplay = null;
}
// Create display for all 3 plants side by side
var plantDisplays = [];
for (var j = 0; j < generatedSeeds.length; j++) {
var plantSeed = generatedSeeds[j];
var display = LK.getAsset(plantSeed, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6,
alpha: 0
});
display.x = (j - 1) * 100; // Space them out: -100, 0, 100
display.y = 0;
self.addChild(display);
plantDisplays.push(display);
}
// Show clear text feedback of which seeds were given
var seedNames = [];
for (var k = 0; k < generatedSeeds.length; k++) {
var seedName = generatedSeeds[k].toUpperCase();
if (generatedSeeds[k] === 'carnivorousplant') seedName = 'CARNIVOROUS PLANT';
if (generatedSeeds[k] === 'doomshroom') seedName = 'DOOM SHROOM';
if (generatedSeeds[k] === 'melonpulta') seedName = 'MELON PULTA';
if (generatedSeeds[k] === 'coltapulta') seedName = 'COL TAPULTA';
if (generatedSeeds[k] === 'lanzamaiz') seedName = 'LANZA MAIZ';
if (generatedSeeds[k] === 'pot') seedName = 'POT';
seedNames.push(seedName);
}
seedText.setText('1 POT + 2 PLANTS: ' + seedNames.join(', ') + '!');
// Show plant images with fade in animation
for (var l = 0; l < plantDisplays.length; l++) {
tween(plantDisplays[l], {
alpha: 1,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 300,
easing: tween.easeOut
});
}
// Show text with fade in animation
tween(seedText, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Keep text visible for 3 seconds, then fade out
tween(seedText, {
alpha: 0,
scaleX: 1,
scaleY: 1
}, {
duration: 1500,
easing: tween.easeIn
});
}
});
// Fade out plant displays after 4 seconds
LK.setTimeout(function () {
for (var m = 0; m < plantDisplays.length; m++) {
if (plantDisplays[m]) {
tween(plantDisplays[m], {
alpha: 0,
scaleX: 0.4,
scaleY: 0.4
}, {
duration: 1000,
easing: tween.easeIn,
onFinish: function onFinish() {
if (plantDisplays[m]) {
plantDisplays[m].destroy();
}
}
});
}
}
}, 4000);
// Visual flash effect to show seeds were given
tween(self, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0x00ff00
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xffffff
}, {
duration: 300,
easing: tween.easeIn
});
}
});
}
};
return self;
});
var Corn = Container.expand(function () {
var self = Container.call(this);
self.speed = 8;
self.damage = 40;
self.isButter = Math.random() < 0.25; // 25% chance to be butter
var cornGraphics;
if (self.isButter) {
cornGraphics = self.attachAsset('butter', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
cornGraphics = self.attachAsset('corn', {
anchorX: 0.5,
anchorY: 0.5
});
}
self.update = function () {
self.x += self.speed;
if (self.x > 2200) {
self.destroy();
}
};
return self;
});
var Fireball = Container.expand(function () {
var self = Container.call(this);
var fireballGraphics = self.attachAsset('fireball', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -6;
self.damage = 50;
self.update = function () {
// Use trajectory-based movement if speed components are set, otherwise use default
if (self.speedX !== undefined && self.speedY !== undefined) {
self.x += self.speedX;
self.y += self.speedY;
} else {
self.x += self.speed;
}
self.rotation += 0.2; // Spinning effect
// Fireball trail effect
if (LK.ticks % 5 === 0) {
tween(self, {
tint: 0xff8c00
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
tint: 0xff4500
}, {
duration: 100,
easing: tween.easeIn
});
}
});
}
// Check if fireball is off-screen (expanded bounds for trajectory-based movement)
if (self.x < -100 || self.x > 2200 || self.y < -100 || self.y > 2800) {
self.destroy();
}
};
return self;
});
var Lawnmower = Container.expand(function () {
var self = Container.call(this);
var lawnmowerGraphics = self.attachAsset('lawnmower', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0; // Starts stationary
self.activated = false;
self.update = function () {
if (self.activated) {
self.x += 12; // Move fast when activated
if (self.x > 2200) {
self.destroy();
}
}
};
return self;
});
var LevelButton = Container.expand(function (levelNumber) {
var self = Container.call(this);
// Level button background
var buttonBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
self.addChild(buttonBg);
// Level number text
var levelText = new Text2(levelNumber.toString(), {
size: 60,
fill: 0x000000
});
levelText.anchor.set(0.5, 0.5);
self.addChild(levelText);
self.levelNumber = levelNumber;
self.down = function (x, y, obj) {
// Start the selected level
startLevel(self.levelNumber);
};
return self;
});
var LoadingScreen = Container.expand(function () {
var self = Container.call(this);
// Loading logo image
var loadingLogo = self.attachAsset('loadingLogo', {
anchorX: 0.5,
anchorY: 0.5
});
loadingLogo.x = 1024;
loadingLogo.y = 800;
// Loading bar background
var loadingBarBg = self.attachAsset('loadingBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
loadingBarBg.x = 1024;
loadingBarBg.y = 1400;
// Loading bar fill
var loadingBar = self.attachAsset('loadingBar', {
anchorX: 0,
anchorY: 0.5
});
loadingBar.x = 724; // Start at left edge of background
loadingBar.y = 1400;
loadingBar.width = 0; // Start with no fill
// Loading text
var loadingText = new Text2('CARGANDO...', {
size: 80,
fill: 0xFFFFFF
});
loadingText.anchor.set(0.5, 0.5);
loadingText.x = 1024;
loadingText.y = 1200;
self.addChild(loadingText);
// Progress text
var progressText = new Text2('0%', {
size: 60,
fill: 0xFFFFFF
});
progressText.anchor.set(0.5, 0.5);
progressText.x = 1024;
progressText.y = 1500;
self.addChild(progressText);
self.progress = 0;
self.maxProgress = 300; // 5 seconds at 60fps
self.currentProgress = 0;
self.update = function () {
if (self.currentProgress < self.maxProgress) {
self.currentProgress++;
self.progress = self.currentProgress / self.maxProgress;
// Update loading bar width
loadingBar.width = 600 * self.progress;
// Update progress text
var percentage = Math.floor(self.progress * 100);
progressText.setText(percentage + '%');
// Loading complete
if (self.currentProgress >= self.maxProgress) {
// Start menu music when loading completes
LK.playMusic('menuMusic', {
loop: true
});
// Transition to level selection
showLevelSelection();
}
}
};
return self;
});
var Minigame = Container.expand(function (type) {
var self = Container.call(this);
self.type = type;
self.completed = false;
self.timeLeft = 30; // 30 seconds for most minigames
self.score = 0;
self.gameObjects = [];
self.spawnTimer = 0;
// Create background
var bg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 15,
scaleY: 20,
alpha: 0.3,
tint: 0x000080
});
self.addChild(bg);
// Title text
var titleText = new Text2('', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 300;
self.addChild(titleText);
// Timer text
var timerText = new Text2('Tiempo: 30', {
size: 60,
fill: 0xFFFF00
});
timerText.anchor.set(0.5, 0.5);
timerText.x = 1024;
timerText.y = 400;
self.addChild(timerText);
// Score text
var scoreText = new Text2('Puntos: 0', {
size: 50,
fill: 0x00FF00
});
scoreText.anchor.set(0.5, 0.5);
scoreText.x = 1024;
scoreText.y = 500;
self.addChild(scoreText);
// Instructions text
var instructionText = new Text2('', {
size: 40,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 1024;
instructionText.y = 600;
self.addChild(instructionText);
if (type === 'sunCollector') {
titleText.setText('¡RECOLECTA SOLES!');
instructionText.setText('Toca los soles que caen para recolectarlos');
self.targetScore = 15;
} else if (type === 'zombieShooter') {
titleText.setText('¡DISPARA ZOMBIES!');
instructionText.setText('Toca zombies para eliminarlos');
self.targetScore = 20;
} else if (type === 'plantDefense') {
titleText.setText('¡PROTEGE LAS PLANTAS!');
instructionText.setText('Toca zombies antes de que lleguen a las plantas');
self.targetScore = 10;
} else if (type === 'speedPlanting') {
titleText.setText('¡PLANTA RÁPIDO!');
instructionText.setText('Planta en las casillas marcadas');
self.targetScore = 12;
self.timeLeft = 20; // Shorter time for planting game
}
self.update = function () {
if (self.completed) return;
self.timeLeft -= 1 / 60; // Decrease by 1/60 each frame (60fps)
timerText.setText('Tiempo: ' + Math.ceil(self.timeLeft));
scoreText.setText('Puntos: ' + self.score);
if (self.timeLeft <= 0) {
self.endMinigame();
return;
}
self.spawnTimer++;
if (self.type === 'sunCollector') {
if (self.spawnTimer % 90 === 0) {
// Every 1.5 seconds
self.spawnSun();
}
} else if (self.type === 'zombieShooter') {
if (self.spawnTimer % 120 === 0) {
// Every 2 seconds
self.spawnZombie();
}
} else if (self.type === 'plantDefense') {
if (self.spawnTimer % 180 === 0) {
// Every 3 seconds
self.spawnDefenseZombie();
}
} else if (self.type === 'speedPlanting') {
if (self.spawnTimer % 120 === 0) {
// Every 2 seconds
self.spawnPlantSpot();
}
}
// Clean up off-screen objects
for (var i = self.gameObjects.length - 1; i >= 0; i--) {
var obj = self.gameObjects[i];
if (obj && (obj.y > 2800 || obj.x < -100 || obj.x > 2200)) {
obj.destroy();
self.gameObjects.splice(i, 1);
}
}
};
self.spawnSun = function () {
var sun = LK.getAsset('sun', {
anchorX: 0.5,
anchorY: 0.5,
x: 400 + Math.random() * 1200,
y: 200,
scaleX: 1.5,
scaleY: 1.5
});
self.addChild(sun);
sun.speed = 2 + Math.random() * 3;
sun.down = function (x, y, obj) {
self.score++;
LK.getSound('collect').play();
sun.destroy();
for (var i = self.gameObjects.length - 1; i >= 0; i--) {
if (self.gameObjects[i] === sun) {
self.gameObjects.splice(i, 1);
break;
}
}
};
sun.update = function () {
sun.y += sun.speed;
};
self.gameObjects.push(sun);
};
self.spawnZombie = function () {
var zombie = LK.getAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5,
x: 2100,
y: 700 + Math.random() * 1000,
scaleX: 0.8,
scaleY: 0.8
});
self.addChild(zombie);
zombie.speed = -1 - Math.random() * 2;
zombie.down = function (x, y, obj) {
zombiesKilled++; // Increment kill counter
self.score++;
LK.getSound('explode').play();
// Death animation
tween(zombie, {
scaleX: 0,
scaleY: 0,
rotation: Math.PI,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
zombie.destroy();
}
});
for (var i = self.gameObjects.length - 1; i >= 0; i--) {
if (self.gameObjects[i] === zombie) {
self.gameObjects.splice(i, 1);
break;
}
}
};
zombie.update = function () {
zombie.x += zombie.speed;
};
self.gameObjects.push(zombie);
};
self.spawnDefenseZombie = function () {
var zombie = LK.getAsset('fastZombie', {
anchorX: 0.5,
anchorY: 0.5,
x: 1800,
y: 800 + Math.random() * 800,
scaleX: 0.7,
scaleY: 0.7
});
self.addChild(zombie);
zombie.speed = -3;
zombie.down = function (x, y, obj) {
zombiesKilled++; // Increment kill counter
self.score++;
LK.getSound('explode').play();
tween(zombie, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
zombie.destroy();
}
});
for (var i = self.gameObjects.length - 1; i >= 0; i--) {
if (self.gameObjects[i] === zombie) {
self.gameObjects.splice(i, 1);
break;
}
}
};
zombie.update = function () {
zombie.x += zombie.speed;
if (zombie.x < 400) {
// Reached plants area
self.score = Math.max(0, self.score - 2); // Lose points
zombie.destroy();
for (var i = self.gameObjects.length - 1; i >= 0; i--) {
if (self.gameObjects[i] === zombie) {
self.gameObjects.splice(i, 1);
break;
}
}
}
};
self.gameObjects.push(zombie);
};
self.spawnPlantSpot = function () {
var spot = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
x: 400 + Math.random() * 1200,
y: 800 + Math.random() * 800,
scaleX: 1.5,
scaleY: 1.5,
tint: 0x00FF00,
alpha: 0.8
});
self.addChild(spot);
spot.lifeTime = 300; // 5 seconds to click
spot.down = function (x, y, obj) {
self.score++;
LK.getSound('plant').play();
tween(spot, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
spot.destroy();
}
});
for (var i = self.gameObjects.length - 1; i >= 0; i--) {
if (self.gameObjects[i] === spot) {
self.gameObjects.splice(i, 1);
break;
}
}
};
spot.update = function () {
spot.lifeTime--;
if (spot.lifeTime <= 0) {
spot.destroy();
for (var i = self.gameObjects.length - 1; i >= 0; i--) {
if (self.gameObjects[i] === spot) {
self.gameObjects.splice(i, 1);
break;
}
}
}
};
self.gameObjects.push(spot);
};
self.endMinigame = function () {
self.completed = true;
var success = self.score >= self.targetScore;
var resultText = new Text2(success ? '¡ÉXITO!' : '¡FALLASTE!', {
size: 100,
fill: success ? 0x00FF00 : 0xFF0000
});
resultText.anchor.set(0.5, 0.5);
resultText.x = 1024;
resultText.y = 1000;
self.addChild(resultText);
var rewardText = new Text2('', {
size: 60,
fill: 0xFFFF00
});
rewardText.anchor.set(0.5, 0.5);
rewardText.x = 1024;
rewardText.y = 1150;
self.addChild(rewardText);
if (success) {
var reward = Math.floor(self.score * 10);
sunPoints += reward;
rewardText.setText('¡+' + reward + ' soles de recompensa!');
updateSunDisplay();
} else {
rewardText.setText('Intenta de nuevo la próxima vez');
}
var continueText = new Text2('Toca para continuar', {
size: 40,
fill: 0xFFFFFF
});
continueText.anchor.set(0.5, 0.5);
continueText.x = 1024;
continueText.y = 1300;
self.addChild(continueText);
// Make screen clickable to continue
self.down = function (x, y, obj) {
self.destroy();
currentMinigame = null;
// Continue to next level
startLevel(currentLevel);
};
};
return self;
});
var MinigameSelector = Container.expand(function () {
var self = Container.call(this);
// Background
var bg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 15,
scaleY: 20,
alpha: 0.5,
tint: 0x4B0082
});
self.addChild(bg);
// Title
var titleText = new Text2('¡MINIJUEGO!', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
self.addChild(titleText);
var subtitleText = new Text2('Elige un minijuego para ganar soles extra', {
size: 50,
fill: 0xFFFF00
});
subtitleText.anchor.set(0.5, 0.5);
subtitleText.x = 1024;
subtitleText.y = 500;
self.addChild(subtitleText);
// Minigame buttons - add defense planning for level 19
var minigames = [{
type: 'sunCollector',
name: 'Recolectar Soles',
desc: 'Recoge 15 soles'
}, {
type: 'zombieShooter',
name: 'Caza Zombies',
desc: 'Elimina 20 zombies'
}, {
type: 'plantDefense',
name: 'Defender Plantas',
desc: 'Protege las plantas'
}, {
type: 'speedPlanting',
name: 'Plantar Rápido',
desc: 'Planta en 12 spots'
}, {
type: 'yoZombie',
name: 'Yo Zombie',
desc: 'Destruye 50 plantas'
}];
// Add defense planning for level 19
if (currentLevel === 19) {
minigames.push({
type: 'defenseSwap',
name: 'Planea la Defensa',
desc: '10,000 soles - Sin setas solares'
});
}
for (var i = 0; i < minigames.length; i++) {
var mg = minigames[i];
var button = LK.getAsset('plantButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 300 + i % 4 * 200,
y: 900 + Math.floor(i / 4) * 200,
scaleX: 1.5,
scaleY: 1.5
});
self.addChild(button);
var nameText = new Text2(mg.name, {
size: 25,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0.5);
nameText.x = 300 + i % 4 * 200;
nameText.y = 1050 + Math.floor(i / 4) * 200;
self.addChild(nameText);
var descText = new Text2(mg.desc, {
size: 18,
fill: 0xFFFFFF
});
descText.anchor.set(0.5, 0.5);
descText.x = 300 + i % 4 * 200;
descText.y = 1100 + Math.floor(i / 4) * 200;
self.addChild(descText);
button.minigameType = mg.type;
button.down = function (x, y, obj) {
self.startMinigame(obj.minigameType);
};
}
// Skip button
var skipButton = LK.getAsset('restartButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1400,
scaleX: 1.2,
scaleY: 1.2
});
self.addChild(skipButton);
var skipText = new Text2('Saltar', {
size: 40,
fill: 0xFFFFFF
});
skipText.anchor.set(0.5, 0.5);
skipText.x = 1024;
skipText.y = 1500;
self.addChild(skipText);
skipButton.down = function (x, y, obj) {
self.destroy();
startLevel(currentLevel);
};
self.startMinigame = function (type) {
self.destroy();
if (type === 'defenseSwap') {
currentMinigame = new PlantDefenseMinigame();
} else if (type === 'yoZombie') {
currentMinigame = new YoZombieMinigame();
} else {
currentMinigame = new Minigame(type);
}
game.addChild(currentMinigame);
};
return self;
});
var Pea = Container.expand(function () {
var self = Container.call(this);
var peaGraphics = self.attachAsset('pea', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = 20;
self.update = function () {
self.x += self.speed;
// Spinning animation
self.rotation += 0.3;
// Trail effect with color change
if (LK.ticks % 3 === 0) {
tween(self, {
tint: 0x00ff00
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
tint: 0xffffff
}, {
duration: 100,
easing: tween.easeIn
});
}
});
}
if (self.x > 2200) {
self.destroy();
}
};
return self;
});
var Plant = Container.expand(function (type) {
var self = Container.call(this);
self.type = type;
self.health = 100;
self.maxHealth = 100;
self.shootCooldown = 0;
self.sunTimer = 0;
var plantGraphics;
if (type === 'sunflower') {
plantGraphics = self.attachAsset('sunflower', {
anchorX: 0.5,
anchorY: 0.5
});
self.sunGenerateRate = 300; // ticks between sun generation
} else if (type === 'peashooter') {
plantGraphics = self.attachAsset('peashooter', {
anchorX: 0.5,
anchorY: 0.5
});
self.shootRate = 90; // ticks between shots
} else if (type === 'wallnut') {
plantGraphics = self.attachAsset('wallnut', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 300;
self.maxHealth = 300;
} else if (type === 'potatomine') {
plantGraphics = self.attachAsset('potatomine', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 50;
self.maxHealth = 50;
self.exploded = false;
} else if (type === 'carnivorousplant') {
plantGraphics = self.attachAsset('carnivorousplant', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 200;
self.maxHealth = 200;
self.attackCooldown = 0;
self.attackRange = 100;
} else if (type === 'doomshroom') {
plantGraphics = self.attachAsset('doomshroom', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.exploded = false;
self.armingTime = 900; // 15 seconds to arm
self.currentArmingTime = 0;
} else if (type === 'repeater') {
plantGraphics = self.attachAsset('repeater', {
anchorX: 0.5,
anchorY: 0.5
});
self.shootRate = 90; // Same as peashooter
} else if (type === 'threepeater') {
plantGraphics = self.attachAsset('threepeater', {
anchorX: 0.5,
anchorY: 0.5
});
self.shootRate = 120; // Slightly slower due to triple shot
} else if (type === 'spikeweed') {
plantGraphics = self.attachAsset('spikeweed', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 300;
self.maxHealth = 300;
} else if (type === 'splitpeater') {
plantGraphics = self.attachAsset('splitpeater', {
anchorX: 0.5,
anchorY: 0.5
});
self.shootRate = 120; // Similar to threepeater
} else if (type === 'lilypad') {
plantGraphics = self.attachAsset('lilypad', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 50;
self.maxHealth = 50;
} else if (type === 'puffshroom') {
plantGraphics = self.attachAsset('puffshroom', {
anchorX: 0.5,
anchorY: 0.5
});
self.shootRate = 60; // Faster than peashooter but shorter range
self.range = 300; // Limited range
} else if (type === 'sunshroom') {
plantGraphics = self.attachAsset('sunshroom', {
anchorX: 0.5,
anchorY: 0.5
});
self.sunGenerateRate = 150; // Faster than sunflower but produces less
self.sunValue = 15; // Produces less sun than sunflower
} else if (type === 'fumeshroom') {
plantGraphics = self.attachAsset('fumeshroom', {
anchorX: 0.5,
anchorY: 0.5
});
self.shootRate = 120; // Slower but affects multiple zombies
} else if (type === 'gravebuster') {
plantGraphics = self.attachAsset('gravebuster', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 1; // Dies after one use
self.maxHealth = 1;
} else if (type === 'hypnoshroom') {
plantGraphics = self.attachAsset('hypnoshroom', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 50;
self.maxHealth = 50;
} else if (type === 'scaredyshroom') {
plantGraphics = self.attachAsset('scaredyshroom', {
anchorX: 0.5,
anchorY: 0.5
});
self.shootRate = 60;
} else if (type === 'iceshroom') {
plantGraphics = self.attachAsset('iceshroom', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 1;
self.maxHealth = 1;
} else if (type === 'melonpulta') {
plantGraphics = self.attachAsset('melonpulta', {
anchorX: 0.5,
anchorY: 0.5
});
self.shootRate = 180; // Slower but more powerful
self.projectileDamage = 80; // High damage
} else if (type === 'coltapulta') {
plantGraphics = self.attachAsset('coltapulta', {
anchorX: 0.5,
anchorY: 0.5
});
self.shootRate = 240; // Slower rate
self.projectileDamage = 60; // Medium damage
} else if (type === 'lanzamaiz') {
plantGraphics = self.attachAsset('lanzamaiz', {
anchorX: 0.5,
anchorY: 0.5
});
self.shootRate = 150; // Fast rate
self.projectileDamage = 40; // Lower damage but faster
} else if (type === 'pot') {
plantGraphics = self.attachAsset('pot', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 50;
self.maxHealth = 50;
}
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.destroy();
return true;
}
return false;
};
self.update = function () {
// Gentle swaying animation for all plants
if (LK.ticks % 240 === Math.floor(self.x) % 240) {
tween(self, {
rotation: 0.1
}, {
duration: 1200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
rotation: -0.1
}, {
duration: 1200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
rotation: 0
}, {
duration: 1200,
easing: tween.easeInOut
});
}
});
}
});
}
if (self.type === 'sunflower') {
self.sunTimer++;
if (self.sunTimer >= self.sunGenerateRate) {
self.sunTimer = 0;
var newSun = new Sun();
newSun.x = self.x + Math.random() * 40 - 20;
newSun.y = self.y + Math.random() * 40 - 20;
// Start invisible and small
newSun.alpha = 0;
newSun.scaleX = 0.3;
newSun.scaleY = 0.3;
game.addChild(newSun);
suns.push(newSun);
// Sun appearance animation with bounce
tween(newSun, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(newSun, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.bounceOut
});
}
});
}
} else if (self.type === 'peashooter') {
self.shootCooldown++;
if (self.shootCooldown >= self.shootRate) {
// Check if there's a zombie in this row
var myRow = Math.floor((self.y - 500) / 140);
var hasZombieInRow = false;
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var zombieRow = Math.floor((zombie.y - 500) / 140);
if (zombieRow === myRow && zombie.x > self.x) {
hasZombieInRow = true;
break;
}
}
if (hasZombieInRow) {
self.shootCooldown = 0;
// Fisheye shooting effect
tween(self, {
scaleX: 1.3,
scaleY: 0.7
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeIn
});
}
});
var pea = new Pea();
pea.x = self.x + 60;
pea.y = self.y;
game.addChild(pea);
peas.push(pea);
LK.getSound('shoot').play();
}
}
} else if (self.type === 'repeater') {
self.shootCooldown++;
if (self.shootCooldown >= self.shootRate) {
// Check if there's a zombie in this row
var myRow = Math.floor((self.y - 500) / 140);
var hasZombieInRow = false;
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var zombieRow = Math.floor((zombie.y - 500) / 140);
if (zombieRow === myRow && zombie.x > self.x) {
hasZombieInRow = true;
break;
}
}
if (hasZombieInRow) {
self.shootCooldown = 0;
// Fisheye shooting effect
tween(self, {
scaleX: 1.3,
scaleY: 0.7
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeIn
});
}
});
// Shoot two peas with slight delay
var pea1 = new Pea();
pea1.x = self.x + 60;
pea1.y = self.y;
game.addChild(pea1);
peas.push(pea1);
// Second pea with slight delay
LK.setTimeout(function () {
var pea2 = new Pea();
pea2.x = self.x + 60;
pea2.y = self.y;
game.addChild(pea2);
peas.push(pea2);
}, 150);
LK.getSound('shoot').play();
}
}
} else if (self.type === 'threepeater') {
self.shootCooldown++;
if (self.shootCooldown >= self.shootRate) {
// Check if there's a zombie in any of the three rows (current, above, below)
var myRow = Math.floor((self.y - 500) / 140);
var hasZombieInAnyRow = false;
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var zombieRow = Math.floor((zombie.y - 500) / 140);
if ((zombieRow === myRow || zombieRow === myRow - 1 || zombieRow === myRow + 1) && zombie.x > self.x) {
hasZombieInAnyRow = true;
break;
}
}
if (hasZombieInAnyRow) {
self.shootCooldown = 0;
// Fisheye shooting effect
tween(self, {
scaleX: 1.3,
scaleY: 0.7
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeIn
});
}
});
// Shoot three peas - center, above, below
var centerPea = new Pea();
centerPea.x = self.x + 60;
centerPea.y = self.y;
game.addChild(centerPea);
peas.push(centerPea);
// Above pea (if row exists)
if (myRow > 0) {
var abovePea = new Pea();
abovePea.x = self.x + 60;
abovePea.y = self.y - 140;
game.addChild(abovePea);
peas.push(abovePea);
}
// Below pea (if row exists)
if (myRow < gridRows - 1) {
var belowPea = new Pea();
belowPea.x = self.x + 60;
belowPea.y = self.y + 140;
game.addChild(belowPea);
peas.push(belowPea);
}
LK.getSound('shoot').play();
}
}
} else if (self.type === 'potatomine' && !self.exploded) {
// Check for zombies stepping on the mine
var myRow = Math.floor((self.y - 500) / 140);
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var zombieRow = Math.floor((zombie.y - 500) / 140);
if (zombieRow === myRow && Math.abs(self.x - zombie.x) < 80) {
// Explode!
self.exploded = true;
LK.getSound('explode').play();
// Create explosion visual effect
tween(self, {
scaleX: 3,
scaleY: 3,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
for (var k = plants.length - 1; k >= 0; k--) {
if (plants[k] === self) {
plants.splice(k, 1);
self.destroy();
break;
}
}
}
});
LK.effects.flashObject(self, 0xFF4500, 500);
// Damage all zombies in the area
for (var j = zombies.length - 1; j >= 0; j--) {
var targetZombie = zombies[j];
var distance = Math.sqrt(Math.pow(self.x - targetZombie.x, 2) + Math.pow(self.y - targetZombie.y, 2));
if (distance < 120) {
if (targetZombie.takeDamage(200)) {
zombiesKilled++; // Increment kill counter
targetZombie.destroy();
zombies.splice(j, 1);
}
}
}
break;
}
}
} else if (self.type === 'carnivorousplant') {
self.attackCooldown++;
if (self.attackCooldown >= 180) {
// Attack every 3 seconds
var myRow = Math.floor((self.y - 500) / 140);
var closestZombie = null;
var closestDistance = Infinity;
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var zombieRow = Math.floor((zombie.y - 500) / 140);
if (zombieRow === myRow) {
var distance = Math.abs(self.x - zombie.x);
if (distance < self.attackRange && distance < closestDistance) {
closestDistance = distance;
closestZombie = zombie;
}
}
}
if (closestZombie) {
self.attackCooldown = 0;
// Attack animation
tween(self, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeIn
});
}
});
if (closestZombie.takeDamage(100)) {
zombiesKilled++; // Increment kill counter
for (var j = zombies.length - 1; j >= 0; j--) {
if (zombies[j] === closestZombie) {
closestZombie.destroy();
zombies.splice(j, 1);
break;
}
}
}
}
}
} else if (self.type === 'doomshroom' && !self.exploded) {
self.currentArmingTime++;
if (self.currentArmingTime >= self.armingTime) {
// Doom shroom is now armed and explodes
self.exploded = true;
LK.getSound('explode').play();
// Create massive explosion visual effect
tween(self, {
scaleX: 8,
scaleY: 8,
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
for (var k = plants.length - 1; k >= 0; k--) {
if (plants[k] === self) {
plants.splice(k, 1);
self.destroy();
break;
}
}
}
});
LK.effects.flashScreen(0x4B0082, 1000);
// Damage all zombies on screen
for (var j = zombies.length - 1; j >= 0; j--) {
var targetZombie = zombies[j];
if (targetZombie.takeDamage(300)) {
zombiesKilled++; // Increment kill counter
targetZombie.destroy();
zombies.splice(j, 1);
}
}
}
} else if (self.type === 'spikeweed') {
// Damage zombies walking over spikeweed - but don't stop them
var myRow = Math.floor((self.y - 500) / 140);
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var zombieRow = Math.floor((zombie.y - 500) / 140);
if (zombieRow === myRow && Math.abs(self.x - zombie.x) < 70) {
// Only damage zombie if it hasn't been damaged by this spikeweed recently
if (!zombie.lastSpikeweeedDamageTime || LK.ticks - zombie.lastSpikeweeedDamageTime > 30) {
zombie.lastSpikeweeedDamageTime = LK.ticks;
if (zombie.takeDamage(20)) {
zombiesKilled++; // Increment kill counter
for (var j = zombies.length - 1; j >= 0; j--) {
if (zombies[j] === zombie) {
zombie.destroy();
zombies.splice(j, 1);
break;
}
}
}
// Damage spikeweed when used
if (self.takeDamage(1)) {
for (var k = plants.length - 1; k >= 0; k--) {
if (plants[k] === self) {
plants.splice(k, 1);
self.destroy();
break;
}
}
}
}
}
}
} else if (self.type === 'splitpeater') {
self.shootCooldown++;
if (self.shootCooldown >= self.shootRate) {
// Check if there's a zombie in front or behind
var myRow = Math.floor((self.y - 500) / 140);
var hasZombieInFront = false;
var hasZombieBehind = false;
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var zombieRow = Math.floor((zombie.y - 500) / 140);
if (zombieRow === myRow) {
if (zombie.x > self.x) {
hasZombieInFront = true;
} else if (zombie.x < self.x) {
hasZombieBehind = true;
}
}
}
if (hasZombieInFront || hasZombieBehind) {
self.shootCooldown = 0;
// Fisheye shooting effect
tween(self, {
scaleX: 1.3,
scaleY: 0.7
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeIn
});
}
});
// Shoot forward if zombie in front
if (hasZombieInFront) {
var forwardPea = new Pea();
forwardPea.x = self.x + 60;
forwardPea.y = self.y;
game.addChild(forwardPea);
peas.push(forwardPea);
}
// Shoot backward if zombie behind
if (hasZombieBehind) {
var backwardPea = new Pea();
backwardPea.x = self.x - 60;
backwardPea.y = self.y;
backwardPea.speed = -8; // Negative speed for backward movement
game.addChild(backwardPea);
peas.push(backwardPea);
}
LK.getSound('shoot').play();
}
}
} else if (self.type === 'puffshroom') {
self.shootCooldown++;
if (self.shootCooldown >= self.shootRate) {
var myRow = Math.floor((self.y - 500) / 140);
var hasZombieInRange = false;
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var zombieRow = Math.floor((zombie.y - 500) / 140);
if (zombieRow === myRow && zombie.x > self.x && zombie.x - self.x < self.range) {
hasZombieInRange = true;
break;
}
}
if (hasZombieInRange) {
self.shootCooldown = 0;
var pea = new Pea();
pea.x = self.x + 60;
pea.y = self.y;
game.addChild(pea);
peas.push(pea);
LK.getSound('shoot').play();
}
}
} else if (self.type === 'sunshroom') {
self.sunTimer++;
if (self.sunTimer >= self.sunGenerateRate) {
self.sunTimer = 0;
var newSun = new Sun();
newSun.value = self.sunValue;
newSun.x = self.x + Math.random() * 40 - 20;
newSun.y = self.y + Math.random() * 40 - 20;
game.addChild(newSun);
suns.push(newSun);
}
} else if (self.type === 'gravebuster') {
// Check for tombstones to destroy
var myRow = Math.floor((self.y - gridStartY + cellSize / 2) / cellSize);
var myCol = Math.floor((self.x - gridStartX + cellSize / 2) / cellSize);
for (var i = tombstones.length - 1; i >= 0; i--) {
var tombstone = tombstones[i];
var tombstoneRow = Math.floor((tombstone.y - gridStartY + cellSize / 2) / cellSize);
var tombstoneCol = Math.floor((tombstone.x - gridStartX + cellSize / 2) / cellSize);
if (tombstoneRow === myRow && tombstoneCol === myCol) {
// Destroy tombstone
tombstone.destroy();
tombstones.splice(i, 1);
// Destroy gravebuster after use
for (var j = plants.length - 1; j >= 0; j--) {
if (plants[j] === self) {
plants.splice(j, 1);
self.destroy();
break;
}
}
break;
}
}
} else if (self.type === 'scaredyshroom') {
self.shootCooldown++;
if (self.shootCooldown >= self.shootRate) {
var myRow = Math.floor((self.y - 500) / 140);
var hasZombieInRow = false;
var zombieTooClose = false;
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var zombieRow = Math.floor((zombie.y - 500) / 140);
if (zombieRow === myRow && zombie.x > self.x) {
hasZombieInRow = true;
if (zombie.x - self.x < 200) {
zombieTooClose = true;
}
break;
}
}
if (hasZombieInRow && !zombieTooClose) {
self.shootCooldown = 0;
var pea = new Pea();
pea.x = self.x + 60;
pea.y = self.y;
game.addChild(pea);
peas.push(pea);
LK.getSound('shoot').play();
}
}
} else if (self.type === 'melonpulta' || self.type === 'coltapulta') {
self.shootCooldown++;
if (self.shootCooldown >= self.shootRate) {
// Check if there's a zombie in this row
var myRow = Math.floor((self.y - 500) / 140);
var hasZombieInRow = false;
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var zombieRow = Math.floor((zombie.y - 500) / 140);
if (zombieRow === myRow && zombie.x > self.x) {
hasZombieInRow = true;
break;
}
}
if (hasZombieInRow) {
self.shootCooldown = 0;
// Catapult shooting animation
tween(self, {
scaleX: 1.3,
scaleY: 0.7
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeIn
});
}
});
var pea = new Pea();
pea.damage = self.projectileDamage; // Use custom damage
pea.x = self.x + 60;
pea.y = self.y;
game.addChild(pea);
peas.push(pea);
LK.getSound('shoot').play();
}
}
} else if (self.type === 'lanzamaiz') {
self.shootCooldown++;
if (self.shootCooldown >= self.shootRate) {
// Check if there's a zombie in this row
var myRow = Math.floor((self.y - 500) / 140);
var hasZombieInRow = false;
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var zombieRow = Math.floor((zombie.y - 500) / 140);
if (zombieRow === myRow && zombie.x > self.x) {
hasZombieInRow = true;
break;
}
}
if (hasZombieInRow) {
self.shootCooldown = 0;
// Catapult shooting animation
tween(self, {
scaleX: 1.3,
scaleY: 0.7
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeIn
});
}
});
var corn = new Corn();
corn.damage = self.projectileDamage; // Use custom damage
corn.x = self.x + 60;
corn.y = self.y;
game.addChild(corn);
corns.push(corn);
LK.getSound('shoot').play();
}
}
}
};
return self;
});
var PlantDefenseMinigame = Container.expand(function () {
var self = Container.call(this);
self.completed = false;
self.timeLeft = 60; // 60 seconds to plan defense
self.score = 0;
self.targetScore = 50; // Need to survive 50 zombies
self.zombies = [];
self.plants = [];
self.selectedPlantType = null;
self.planningPhase = true;
self.sunPoints = 10000; // Start with 10,000 suns
// Available plants (no sun mushrooms allowed)
self.availablePlants = ['wallnut', 'carnivorousplant', 'peashooter', 'repeater', 'threepeater', 'spikeweed', 'doomshroom', 'potatomine'];
// Create background
var bg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 15,
scaleY: 20,
alpha: 0.4,
tint: 0x1a1a2e // Dark night background
});
self.addChild(bg);
// Title text
var titleText = new Text2('¡PLANEA LA DEFENSA!', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 200;
self.addChild(titleText);
// Instructions text
var instructionText = new Text2('Tienes 60 segundos y 10,000 soles para preparar tu defensa', {
size: 40,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 1024;
instructionText.y = 280;
self.addChild(instructionText);
// Timer text
var timerText = new Text2('Tiempo de planificación: 60', {
size: 50,
fill: 0xFFFF00
});
timerText.anchor.set(0.5, 0.5);
timerText.x = 1024;
timerText.y = 340;
self.addChild(timerText);
// Sun display
var sunText = new Text2('Soles: 10000', {
size: 50,
fill: 0xFFFF00
});
sunText.anchor.set(0.5, 0.5);
sunText.x = 1024;
sunText.y = 400;
self.addChild(sunText);
// Create simple 8x5 grid for planting
self.gridStartX = 500;
self.gridStartY = 600;
self.gridCols = 8;
self.gridRows = 5;
self.cellSize = 120;
self.plantGrid = [];
// Initialize plant grid
for (var row = 0; row < self.gridRows; row++) {
self.plantGrid[row] = [];
for (var col = 0; col < self.gridCols; col++) {
self.plantGrid[row][col] = null;
}
}
// Draw grid cells
for (var row = 0; row < self.gridRows; row++) {
for (var col = 0; col < self.gridCols; col++) {
var gridCell = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
x: self.gridStartX + col * self.cellSize,
y: self.gridStartY + row * self.cellSize,
alpha: 0.3,
tint: 0x228B22
});
self.addChild(gridCell);
}
}
// Create plant selection buttons
self.plantButtons = [];
for (var i = 0; i < self.availablePlants.length; i++) {
var plantType = self.availablePlants[i];
var button = LK.getAsset(plantType, {
anchorX: 0.5,
anchorY: 0.5,
x: 200 + i % 4 * 150,
y: 1200 + Math.floor(i / 4) * 150,
scaleX: 0.6,
scaleY: 0.6
});
self.addChild(button);
self.plantButtons.push(button);
// Add click handler
button.plantType = plantType;
button.down = function (x, y, obj) {
if (self.planningPhase) {
self.selectedPlantType = obj.plantType;
// Visual feedback
for (var j = 0; j < self.plantButtons.length; j++) {
self.plantButtons[j].tint = 0xffffff;
}
obj.tint = 0x808080;
}
};
}
// Zombie spawn during battle phase
self.zombieSpawnTimer = 0;
self.zombiesSpawned = 0;
self.update = function () {
if (self.completed) return;
if (self.planningPhase) {
self.timeLeft -= 1 / 60;
timerText.setText('Tiempo de planificación: ' + Math.ceil(self.timeLeft));
sunText.setText('Soles: ' + self.sunPoints);
if (self.timeLeft <= 0) {
// Switch to battle phase
self.planningPhase = false;
self.timeLeft = 120; // 2 minutes to survive
titleText.setText('¡DEFIENDE!');
instructionText.setText('Sobrevive a 50 zombies');
timerText.setText('Tiempo restante: 120');
sunText.visible = false;
// Hide plant buttons
for (var i = 0; i < self.plantButtons.length; i++) {
self.plantButtons[i].visible = false;
}
}
} else {
// Battle phase
self.timeLeft -= 1 / 60;
timerText.setText('Tiempo restante: ' + Math.ceil(self.timeLeft));
// Spawn zombies
self.zombieSpawnTimer++;
if (self.zombieSpawnTimer >= 120 && self.zombiesSpawned < 50) {
// Every 2 seconds
self.zombieSpawnTimer = 0;
self.spawnZombie();
self.zombiesSpawned++;
}
// Check win condition
if (self.zombiesSpawned >= 50 && self.zombies.length === 0) {
self.endMinigame(true);
return;
}
// Check lose condition
if (self.timeLeft <= 0) {
self.endMinigame(false);
return;
}
// Update plant vs zombie combat
self.updateCombat();
}
};
self.spawnZombie = function () {
var zombie = LK.getAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5,
x: 1400,
y: self.gridStartY + Math.floor(Math.random() * self.gridRows) * self.cellSize,
scaleX: 0.8,
scaleY: 0.8
});
self.addChild(zombie);
zombie.health = 100;
zombie.speed = -1;
zombie.row = Math.floor((zombie.y - self.gridStartY + self.cellSize / 2) / self.cellSize);
self.zombies.push(zombie);
zombie.update = function () {
zombie.x += zombie.speed;
if (zombie.x < 400) {
// Zombie reached the left side - game over
self.endMinigame(false);
}
};
};
self.updateCombat = function () {
// Simple combat system
for (var i = self.zombies.length - 1; i >= 0; i--) {
var zombie = self.zombies[i];
var zombieDestroyed = false;
// Check for plant attacks
for (var j = 0; j < self.plants.length; j++) {
var plant = self.plants[j];
if (plant.row === zombie.row && plant.x < zombie.x && zombie.x - plant.x < 300) {
// Plant can attack zombie
if (!plant.lastAttack || LK.ticks - plant.lastAttack > 60) {
plant.lastAttack = LK.ticks;
zombie.health -= 25;
// Visual attack effect
tween(plant, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xff4444
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(plant, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0xffffff
}, {
duration: 100,
easing: tween.easeIn
});
}
});
if (zombie.health <= 0) {
zombiesKilled++;
zombie.destroy();
self.zombies.splice(i, 1);
self.score++;
zombieDestroyed = true;
break;
}
}
}
}
if (!zombieDestroyed) {
// Check if zombie reached end
if (zombie.x <= 400) {
zombie.destroy();
self.zombies.splice(i, 1);
// Penalty for letting zombie through
self.score = Math.max(0, self.score - 1);
}
}
}
};
self.endMinigame = function (success) {
self.completed = true;
var resultText = new Text2(success ? '¡DEFENSA EXITOSA!' : '¡DEFENSA FALLIDA!', {
size: 100,
fill: success ? 0x00FF00 : 0xFF0000
});
resultText.anchor.set(0.5, 0.5);
resultText.x = 1024;
resultText.y = 1366;
self.addChild(resultText);
var rewardText = new Text2('', {
size: 60,
fill: 0xFFFF00
});
rewardText.anchor.set(0.5, 0.5);
rewardText.x = 1024;
rewardText.y = 1500;
self.addChild(rewardText);
if (success) {
var reward = 500;
sunPoints += reward;
rewardText.setText('¡+' + reward + ' soles de recompensa!');
updateSunDisplay();
} else {
rewardText.setText('¡Mejora tu estrategia!');
}
var continueText = new Text2('Toca para continuar', {
size: 40,
fill: 0xFFFFFF
});
continueText.anchor.set(0.5, 0.5);
continueText.x = 1024;
continueText.y = 1650;
self.addChild(continueText);
self.down = function (x, y, obj) {
self.destroy();
currentMinigame = null;
startLevel(19);
};
};
// Handle plant placement during planning phase
self.down = function (x, y, obj) {
if (!self.planningPhase || !self.selectedPlantType) return;
// Check if click is on grid
var col = Math.floor((x - self.gridStartX + self.cellSize / 2) / self.cellSize);
var row = Math.floor((y - self.gridStartY + self.cellSize / 2) / self.cellSize);
if (col >= 0 && col < self.gridCols && row >= 0 && row < self.gridRows) {
// Check if position is empty and player has enough suns
var plantCost = plantCosts[self.selectedPlantType] || 50;
if (!self.plantGrid[row][col] && self.sunPoints >= plantCost) {
// Place plant
var plant = LK.getAsset(self.selectedPlantType, {
anchorX: 0.5,
anchorY: 0.5,
x: self.gridStartX + col * self.cellSize,
y: self.gridStartY + row * self.cellSize,
scaleX: 0.8,
scaleY: 0.8
});
self.addChild(plant);
plant.row = row;
plant.col = col;
self.plantGrid[row][col] = plant;
self.plants.push(plant);
self.sunPoints -= plantCost;
LK.getSound('plant').play();
}
}
};
return self;
});
var PlantSelector = Container.expand(function () {
var self = Container.call(this);
self.completed = false;
// Create background
var bg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 15,
scaleY: 20,
alpha: 0.6,
tint: 0x2F4F2F
});
self.addChild(bg);
// Title
var titleText = new Text2('¡ELIGE TUS PLANTAS!', {
size: 100,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 200;
self.addChild(titleText);
var subtitleText = new Text2('Selecciona hasta 8 plantas para este nivel', {
size: 50,
fill: 0xFFFF00
});
subtitleText.anchor.set(0.5, 0.5);
subtitleText.x = 1024;
subtitleText.y = 280;
self.addChild(subtitleText);
// All available plants - show all plants from all modes
self.allPlants = ['sunflower', 'peashooter', 'wallnut', 'potatomine', 'carnivorousplant', 'doomshroom', 'repeater', 'threepeater', 'spikeweed', 'splitpeater', 'lilypad', 'puffshroom', 'sunshroom', 'fumeshroom', 'gravebuster', 'hypnoshroom', 'scaredyshroom', 'iceshroom', 'melonpulta', 'coltapulta', 'lanzamaiz', 'pot'];
self.selectedPlants = [];
self.selectedDisplays = []; // Store the ordered display plants
self.maxPlants = 8;
// Selected plants display area at top
var selectedAreaText = new Text2('PLANTAS SELECCIONADAS:', {
size: 40,
fill: 0xFFFF00
});
selectedAreaText.anchor.set(0.5, 0.5);
selectedAreaText.x = 1024;
selectedAreaText.y = 350;
self.addChild(selectedAreaText);
// Create plant buttons in a grid showing ALL plants
var buttonsPerRow = 6;
var buttonSize = 100;
var startX = 1024 - buttonsPerRow * buttonSize / 2 + buttonSize / 2;
var startY = 700;
for (var i = 0; i < self.allPlants.length; i++) {
var plantType = self.allPlants[i];
var row = Math.floor(i / buttonsPerRow);
var col = i % buttonsPerRow;
var button = LK.getAsset(plantType, {
anchorX: 0.5,
anchorY: 0.5,
x: startX + col * buttonSize,
y: startY + row * buttonSize,
scaleX: 0.7,
scaleY: 0.7
});
self.addChild(button);
// Add cost label
var costText = new Text2(plantCosts[plantType].toString(), {
size: 16,
fill: 0xFFFFFF
});
costText.anchor.set(0.5, 0);
costText.x = startX + col * buttonSize;
costText.y = startY + row * buttonSize + 40;
self.addChild(costText);
button.plantType = plantType;
button.selected = false;
button.down = function (x, y, obj) {
// Use 'this' to refer to the button since obj might be undefined
var buttonRef = this;
if (buttonRef.selected) {
// Deselect - remove from selectedPlants and destroy corresponding display
buttonRef.selected = false;
buttonRef.tint = 0xffffff;
var index = self.selectedPlants.indexOf(buttonRef.plantType);
if (index > -1) {
self.selectedPlants.splice(index, 1);
// Remove and destroy the display element
if (self.selectedDisplays[index]) {
self.selectedDisplays[index].destroy();
}
self.selectedDisplays.splice(index, 1);
// Reposition remaining displays
self.repositionSelectedDisplays();
}
} else if (self.selectedPlants.length < self.maxPlants) {
// Select - add to selectedPlants and create display
buttonRef.selected = true;
buttonRef.tint = 0x00ff00;
self.selectedPlants.push(buttonRef.plantType);
// Create display element in the selected area
var selectedDisplay = LK.getAsset(buttonRef.plantType, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
self.addChild(selectedDisplay);
self.selectedDisplays.push(selectedDisplay);
self.repositionSelectedDisplays();
}
};
}
// Function to reposition selected plant displays in order
self.repositionSelectedDisplays = function () {
var displayStartX = 1024 - (self.selectedDisplays.length - 1) * 80 / 2;
var displayY = 450;
for (var i = 0; i < self.selectedDisplays.length; i++) {
if (self.selectedDisplays[i]) {
self.selectedDisplays[i].x = displayStartX + i * 80;
self.selectedDisplays[i].y = displayY;
}
}
};
// Selected count display
var countText = new Text2('Seleccionadas: 0/' + self.maxPlants, {
size: 40,
fill: 0xFFFFFF
});
countText.anchor.set(0.5, 0.5);
countText.x = 1024;
countText.y = 520;
self.addChild(countText);
// Continue button
var continueButton = LK.getAsset('plantButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1600,
scaleX: 2,
scaleY: 2
});
self.addChild(continueButton);
var continueText = new Text2('CONTINUAR', {
size: 40,
fill: 0xFFFFFF
});
continueText.anchor.set(0.5, 0.5);
continueText.x = 1024;
continueText.y = 1650;
self.addChild(continueText);
continueButton.down = function (x, y, obj) {
if (self.selectedPlants.length >= 3) {
// Minimum 3 plants required
self.completed = true;
self.destroy();
// Continue with level using selected plants
startLevelWithSelectedPlants(currentLevel, self.selectedPlants);
}
};
self.update = function () {
countText.setText('Seleccionadas: ' + self.selectedPlants.length + '/' + self.maxPlants);
continueButton.alpha = self.selectedPlants.length >= 3 ? 1 : 0.5;
};
return self;
});
var RobotBoss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('robotBoss', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 1000;
self.maxHealth = 1000;
self.attackTimer = 0;
self.attackRate = 240; // 4 seconds between attacks
self.mouthOpen = false;
self.takeDamage = function (damage) {
self.health -= damage;
// Flash red when taking damage
tween(self, {
tint: 0xff0000
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
tint: 0xffffff
}, {
duration: 200,
easing: tween.easeIn
});
}
});
if (self.health <= 0) {
// Boss death animation
tween(self, {
scaleX: 2,
scaleY: 2,
alpha: 0,
rotation: Math.PI
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
// Trigger win condition
if (gameState === 'playing') {
gameState = 'won';
LK.showYouWin();
}
}
});
return true;
}
return false;
};
self.update = function () {
self.attackTimer++;
if (self.attackTimer >= self.attackRate) {
self.attackTimer = 0;
// Randomly choose between fireball attack or zombie spawn
if (Math.random() < 0.6) {
// Show red circle where fireball will land at random position
var landingIndicators = [];
var fireballTargets = []; // Store target position for fireball
// Generate random target position within grid area
var targetX = gridStartX + Math.random() * (gridCols * cellSize);
var targetY = gridStartY + Math.random() * (gridRows * cellSize);
fireballTargets.push({
x: targetX,
y: targetY
});
var indicator = LK.getAsset('fireball', {
anchorX: 0.5,
anchorY: 0.5,
x: targetX,
y: targetY,
scaleX: 2,
scaleY: 2,
tint: 0xff0000,
alpha: 0.7
});
game.addChild(indicator);
landingIndicators.push(indicator);
// Open mouth animation for fireball attack
self.mouthOpen = true;
tween(self, {
scaleY: 1.3,
tint: 0xff4500
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Remove landing indicator
for (var k = 0; k < landingIndicators.length; k++) {
landingIndicators[k].destroy();
}
// Spit single fireball toward random target position from boss mouth
var fireball = new Fireball();
// Set initial position at boss mouth
fireball.x = self.x - 80; // Spawn from mouth area (further left from center)
fireball.y = self.y - 50; // Spawn from mouth height (upper part of boss)
// Calculate trajectory toward target
var targetX = fireballTargets[0].x;
var targetY = fireballTargets[0].y;
var deltaX = targetX - fireball.x;
var deltaY = targetY - fireball.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
// Set fireball direction and speed toward target
fireball.speedX = deltaX / distance * 8; // Horizontal speed component
fireball.speedY = deltaY / distance * 8; // Vertical speed component
game.addChild(fireball);
fireballs.push(fireball);
// Close mouth
tween(self, {
scaleY: 1,
tint: 0xffffff
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
self.mouthOpen = false;
}
});
}
});
} else {
// Spawn zombies including Zombistein
var zombieTypes = ['normal', 'fast', 'snailBucket', 'zombistein'];
var randomType = zombieTypes[Math.floor(Math.random() * zombieTypes.length)];
var newZombie = new Zombie(randomType);
newZombie.x = 2100;
newZombie.y = gridStartY + Math.floor(Math.random() * gridRows) * cellSize;
game.addChild(newZombie);
zombies.push(newZombie);
// Boss spawn effect
tween(self, {
tint: 0x8000ff
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
tint: 0xffffff
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
}
// Update boss health display
if (bossHealthDisplay) {
bossHealthDisplay.setText('Boss Health: ' + self.health + '/' + self.maxHealth);
}
};
return self;
});
var Sun = Container.expand(function () {
var self = Container.call(this);
var sunGraphics = self.attachAsset('sun', {
anchorX: 0.5,
anchorY: 0.5
});
self.value = 25;
self.collected = false;
self.lifeTimer = 0;
self.maxLifeTime = 300; // 5 seconds at 60 FPS
self.update = function () {
if (!self.collected) {
self.lifeTimer++;
// Gentle floating animation
if (self.lifeTimer % 120 === 0) {
tween(self, {
y: self.y - 15
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
y: self.y + 15
}, {
duration: 1000,
easing: tween.easeInOut
});
}
});
}
// Gentle pulsing
if (self.lifeTimer % 180 === 0) {
tween(self, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 800,
easing: tween.easeInOut
});
}
});
}
if (self.lifeTimer >= self.maxLifeTime) {
self.collected = true;
// Fade out animation before destroying
tween(self, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
self.destroy();
}
});
}
}
};
self.down = function (x, y, obj) {
if (!self.collected) {
self.collected = true;
sunPoints += self.value;
updateSunDisplay();
LK.getSound('collect').play();
// Collection animation - fly to sun display and shrink
var targetX = sunDisplay.x;
var targetY = sunDisplay.y;
tween(self, {
x: targetX,
y: targetY,
scaleX: 0.3,
scaleY: 0.3,
alpha: 0.8
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
// Final shrink and fade
tween(self, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
}
});
}
};
return self;
});
var Tombstone = Container.expand(function () {
var self = Container.call(this);
var tombstoneGraphics = self.attachAsset('tombstone', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.destroy();
return true;
}
return false;
};
return self;
});
var YoZombieMinigame = Container.expand(function () {
var self = Container.call(this);
self.completed = false;
self.timeLeft = -1; // No time limit
self.score = 0;
self.targetScore = 50; // Need to destroy 50 plants
self.zombies = [];
self.plants = [];
self.zombieTypes = ['normal', 'fast', 'snailBucket', 'cart'];
self.selectedZombieType = 'normal';
self.sunPoints = 200; // Start with suns for zombie spawning
// Zombie costs in suns
self.zombieCosts = {
normal: 25,
fast: 50,
snailBucket: 75,
cart: 100
};
// Create background
var bg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 15,
scaleY: 20,
alpha: 0.4,
tint: 0x4a0e4e // Dark purple background
});
self.addChild(bg);
// Title text
var titleText = new Text2('¡YO ZOMBIE!', {
size: 100,
fill: 0xFF0000
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 250;
self.addChild(titleText);
// Instructions text
var instructionText = new Text2('¡Lanza zombies para destruir 50 plantas!', {
size: 50,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 1024;
instructionText.y = 350;
self.addChild(instructionText);
// Timer text
var timerText = new Text2('Sin límite de tiempo', {
size: 60,
fill: 0xFFFF00
});
timerText.anchor.set(0.5, 0.5);
timerText.x = 1024;
timerText.y = 450;
self.addChild(timerText);
// Score text
var scoreText = new Text2('Plantas destruidas: 0/50', {
size: 50,
fill: 0x00FF00
});
scoreText.anchor.set(0.5, 0.5);
scoreText.x = 1024;
scoreText.y = 550;
self.addChild(scoreText);
// Sun display for zombies
var sunText = new Text2('Soles: 200', {
size: 50,
fill: 0xFFFF00
});
sunText.anchor.set(0.5, 0.5);
sunText.x = 1024;
sunText.y = 610;
self.addChild(sunText);
// Zombie selection buttons - positioned like plant seed bar
var zombieButtons = [];
for (var i = 0; i < self.zombieTypes.length; i++) {
var zombieType = self.zombieTypes[i];
var assetName = zombieType === 'normal' ? 'zombie' : zombieType === 'fast' ? 'fastZombie' : zombieType === 'snailBucket' ? 'snailBucket' : 'cartZombie';
var button = LK.getAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5,
x: 200 + i * 100,
y: 2100,
scaleX: 0.6,
scaleY: 0.6
});
self.addChild(button);
button.zombieType = zombieType;
button.selected = i === 0;
if (button.selected) button.tint = 0x00ff00;
button.down = function (x, y, obj) {
// Only allow selection if player has enough suns
var cost = self.zombieCosts[this.zombieType];
if (self.sunPoints >= cost) {
// Deselect all buttons
for (var j = 0; j < zombieButtons.length; j++) {
zombieButtons[j].selected = false;
zombieButtons[j].tint = 0xffffff;
}
// Select this button
this.selected = true;
this.tint = 0x00ff00;
self.selectedZombieType = this.zombieType;
}
};
zombieButtons.push(button);
// Add cost label positioned below button like plant costs
var costText = new Text2(self.zombieCosts[zombieType].toString(), {
size: 20,
fill: 0xFFFF00
});
costText.anchor.set(0.5, 0);
costText.x = 200 + i * 100;
costText.y = 2140;
self.addChild(costText);
}
// Spawn plants to attack
self.spawnPlants = function () {
var plantTypes = ['sunflower', 'peashooter', 'wallnut', 'potatomine'];
for (var i = 0; i < 60; i++) {
// Spawn 60 plants total
var plantType = plantTypes[Math.floor(Math.random() * plantTypes.length)];
var plant = LK.getAsset(plantType, {
anchorX: 0.5,
anchorY: 0.5,
x: 300 + Math.random() * 1400,
y: 800 + Math.random() * 1200,
scaleX: 0.8,
scaleY: 0.8
});
self.addChild(plant);
plant.health = 100;
plant.destroyed = false;
self.plants.push(plant);
}
};
// Spawn additional plants periodically during gameplay
self.plantSpawnTimer = 0;
self.plantSpawnRate = 300; // 5 seconds
self.spawnRandomPlants = function () {
var plantTypes = ['sunflower', 'peashooter', 'wallnut', 'potatomine'];
// Spawn 3-5 random plants
var plantsToSpawn = 3 + Math.floor(Math.random() * 3);
for (var i = 0; i < plantsToSpawn; i++) {
var plantType = plantTypes[Math.floor(Math.random() * plantTypes.length)];
var plant = LK.getAsset(plantType, {
anchorX: 0.5,
anchorY: 0.5,
x: 300 + Math.random() * 1400,
y: 800 + Math.random() * 1200,
scaleX: 0.8,
scaleY: 0.8
});
self.addChild(plant);
plant.health = 100;
plant.destroyed = false;
self.plants.push(plant);
}
};
self.update = function () {
if (self.completed) return;
// No time limit - removed time decrease
timerText.setText('Sin límite de tiempo');
scoreText.setText('Plantas destruidas: ' + self.score + '/50');
sunText.setText('Soles: ' + self.sunPoints);
// Check win condition
if (self.score >= self.targetScore) {
self.endMinigame(true);
return;
}
// Spawn random plants periodically
self.plantSpawnTimer++;
if (self.plantSpawnTimer >= self.plantSpawnRate) {
self.plantSpawnTimer = 0;
self.spawnRandomPlants();
}
// Add suns periodically so players can continue spawning zombies
if (LK.ticks % 300 === 0) {
// Every 5 seconds
self.sunPoints += 25;
}
// Update zombie vs plant combat
for (var i = self.zombies.length - 1; i >= 0; i--) {
var zombie = self.zombies[i];
if (zombie.destroyed) continue;
// Move zombie toward nearest plant
var nearestPlant = null;
var nearestDistance = Infinity;
for (var j = 0; j < self.plants.length; j++) {
var plant = self.plants[j];
if (!plant.destroyed) {
var distance = Math.sqrt(Math.pow(zombie.x - plant.x, 2) + Math.pow(zombie.y - plant.y, 2));
if (distance < nearestDistance) {
nearestDistance = distance;
nearestPlant = plant;
}
}
}
if (nearestPlant) {
// Move toward plant
var deltaX = nearestPlant.x - zombie.x;
var deltaY = nearestPlant.y - zombie.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance > 5) {
zombie.x += deltaX / distance * zombie.speed;
zombie.y += deltaY / distance * zombie.speed;
}
// Attack plant if close enough
if (distance < 70) {
// Only damage once per second
if (!nearestPlant.lastDamageTime || LK.ticks - nearestPlant.lastDamageTime > 60) {
nearestPlant.lastDamageTime = LK.ticks;
nearestPlant.health -= zombie.attackDamage;
if (nearestPlant.health <= 0 && !nearestPlant.destroyed) {
nearestPlant.destroyed = true;
self.score++;
LK.getSound('explode').play();
// Plant destruction animation
tween(nearestPlant, {
scaleX: 0,
scaleY: 0,
alpha: 0,
rotation: Math.PI
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
nearestPlant.destroy();
}
});
}
}
}
}
// Remove zombies that are off screen or destroyed
if (zombie.x < -100 || zombie.x > 2200 || zombie.y < -100 || zombie.y > 2800 || zombie.destroyed) {
if (!zombie.destroyed) {
zombie.destroy();
}
self.zombies.splice(i, 1);
}
}
};
self.endMinigame = function (success) {
self.completed = true;
var resultText = new Text2(success ? '¡ZOMBIES VICTORIOSOS!' : '¡LAS PLANTAS RESISTIERON!', {
size: 80,
fill: success ? 0x00FF00 : 0xFF0000
});
resultText.anchor.set(0.5, 0.5);
resultText.x = 1024;
resultText.y = 1400;
self.addChild(resultText);
var rewardText = new Text2('', {
size: 60,
fill: 0xFFFF00
});
rewardText.anchor.set(0.5, 0.5);
rewardText.x = 1024;
rewardText.y = 1550;
self.addChild(rewardText);
if (success) {
var reward = Math.floor(self.score * 20);
sunPoints += reward;
rewardText.setText('¡+' + reward + ' soles de recompensa!');
updateSunDisplay();
} else {
rewardText.setText('¡Entrena más a tus zombies!');
}
var continueText = new Text2('Toca para continuar', {
size: 40,
fill: 0xFFFFFF
});
continueText.anchor.set(0.5, 0.5);
continueText.x = 1024;
continueText.y = 1700;
self.addChild(continueText);
// Make screen clickable to continue
self.down = function (x, y, obj) {
self.destroy();
currentMinigame = null;
// Continue to level selection
showLevelSelection();
};
};
// Handle zombie spawning on click
self.down = function (x, y, obj) {
if (self.completed) return;
// Check if player has enough suns
var zombieCost = self.zombieCosts[self.selectedZombieType];
if (self.sunPoints < zombieCost) return;
// Deduct sun cost
self.sunPoints -= zombieCost;
// Spawn zombie at click position
var zombie = LK.getAsset(self.selectedZombieType === 'normal' ? 'zombie' : self.selectedZombieType === 'fast' ? 'fastZombie' : self.selectedZombieType === 'snailBucket' ? 'snailBucket' : 'cartZombie', {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: y,
scaleX: 0.8,
scaleY: 0.8
});
self.addChild(zombie);
// Set zombie properties based on type
if (self.selectedZombieType === 'fast') {
zombie.speed = 3;
zombie.health = 60;
zombie.attackDamage = 15;
} else if (self.selectedZombieType === 'snailBucket') {
zombie.speed = 1.5;
zombie.health = 250;
zombie.attackDamage = 30;
} else if (self.selectedZombieType === 'cart') {
zombie.speed = 2;
zombie.health = 300;
zombie.attackDamage = 50;
} else {
zombie.speed = 2;
zombie.health = 100;
zombie.attackDamage = 20;
}
zombie.destroyed = false;
self.zombies.push(zombie);
LK.getSound('plant').play();
};
// Initialize plants
self.spawnPlants();
return self;
});
var Zombie = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'normal';
self.health = 100;
self.maxHealth = 100;
self.speed = -1;
self.baseSpeed = -1; // Store original speed
self.attackDamage = 25;
self.attackCooldown = 0;
self.isAttacking = false;
self.butterSlowTime = 0; // Butter slow effect timer
self.isSlowed = false; // Track if zombie is slowed
var zombieGraphics;
if (self.type === 'fast') {
zombieGraphics = self.attachAsset('fastZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -2;
self.baseSpeed = -2;
self.health = 60;
self.maxHealth = 60;
} else if (self.type === 'snailBucket') {
zombieGraphics = self.attachAsset('snailBucket', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -0.5;
self.baseSpeed = -0.5;
self.health = 250;
self.maxHealth = 250;
} else if (self.type === 'miner') {
zombieGraphics = self.attachAsset('minerZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -1.2;
self.baseSpeed = -1.2;
self.health = 120;
self.maxHealth = 120;
self.underground = false;
self.surfaceTimer = 0;
self.canAttack = true; // Can attack and be attacked when above ground
} else if (self.type === 'cart') {
zombieGraphics = self.attachAsset('cartZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -0.7;
self.baseSpeed = -0.7;
self.health = 300;
self.maxHealth = 300;
self.cartHealth = 200; // Cart has its own health
self.cartDestroyed = false;
} else if (self.type === 'newspaper') {
zombieGraphics = self.attachAsset('newspaperZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -0.8;
self.baseSpeed = -0.8;
self.health = 150;
self.maxHealth = 150;
self.newspaperHealth = 75; // Newspaper has its own health
self.newspaperDestroyed = false;
self.enraged = false;
} else if (self.type === 'zombistein') {
zombieGraphics = self.attachAsset('zombistein', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.8,
scaleY: 1.8,
tint: 0x00ff00
});
self.speed = -0.5;
self.baseSpeed = -0.5;
self.health = 500;
self.maxHealth = 500;
self.electricAttack = true;
self.lastElectricAttack = 0;
} else {
zombieGraphics = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.baseSpeed = -1;
}
self.takeDamage = function (damage) {
// Cart zombie takes damage to cart first
if (self.type === 'cart' && !self.cartDestroyed) {
self.cartHealth -= damage;
if (self.cartHealth <= 0) {
self.cartDestroyed = true;
self.speed = -1.5; // Move faster without cart
zombieGraphics.tint = 0x888888;
}
return false; // Cart zombie doesn't die until cart is destroyed
} else if (self.type === 'newspaper' && !self.newspaperDestroyed) {
self.newspaperHealth -= damage;
if (self.newspaperHealth <= 0) {
self.newspaperDestroyed = true;
self.enraged = true;
self.speed = -2.5; // Move much faster when enraged
zombieGraphics.tint = 0xff4444; // Red tint when angry
}
return false; // Newspaper zombie doesn't die until newspaper is destroyed
} else {
// Miner zombie cannot be damaged when underground
if (self.type === 'miner' && self.underground) {
return false; // Cannot be damaged underground
}
self.health -= damage;
// Damage flash animation
tween(self, {
tint: 0xff4444
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
tint: 0xffffff
}, {
duration: 150,
easing: tween.easeIn
});
}
});
// Slight knockback effect
tween(self, {
x: self.x - 10
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
x: self.x + 10
}, {
duration: 200,
easing: tween.easeIn
});
}
});
if (self.health <= 0) {
// Type-specific death animations
if (self.type === 'fast') {
// Fast zombie spins rapidly while shrinking
tween(self, {
rotation: Math.PI * 4,
// Multiple spins
scaleX: 0.2,
scaleY: 0.2,
alpha: 0,
tint: 0xff4444
}, {
duration: 600,
easing: tween.easeOut
});
} else if (self.type === 'snailBucket') {
// Snail bucket zombie falls backward with bucket rolling
tween(self, {
rotation: -Math.PI / 2,
y: self.y + 30,
x: self.x - 20,
scaleX: 1.3,
scaleY: 0.6,
alpha: 0.3,
tint: 0x666666
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
// Bucket rolls away effect
tween(self, {
rotation: -Math.PI * 2,
x: self.x - 50,
alpha: 0
}, {
duration: 400,
easing: tween.easeIn
});
}
});
} else if (self.type === 'miner') {
// Miner zombie explodes underground effect
tween(self, {
scaleX: 2.0,
scaleY: 0.3,
alpha: 0,
tint: 0x8B4513,
// Brown dirt color
rotation: Math.PI
}, {
duration: 500,
easing: tween.easeOut
});
} else if (self.type === 'cart') {
// Cart zombie tips over with cart debris
tween(self, {
rotation: Math.PI / 3,
y: self.y + 40,
x: self.x - 30,
scaleX: 1.4,
scaleY: 0.7,
alpha: 0.4,
tint: 0x444444
}, {
duration: 700,
easing: tween.easeOut,
onFinish: function onFinish() {
// Final collapse
tween(self, {
scaleX: 0.5,
scaleY: 0.2,
alpha: 0
}, {
duration: 300,
easing: tween.easeIn
});
}
});
} else if (self.type === 'newspaper') {
// Newspaper zombie crumples like paper
tween(self, {
scaleX: 0.1,
scaleY: 1.8,
rotation: Math.PI / 4,
alpha: 0.6,
tint: 0xF5F5DC // Beige newspaper color
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
// Paper flutter effect
tween(self, {
scaleX: 0.05,
scaleY: 0.05,
rotation: Math.PI * 3,
y: self.y - 20,
alpha: 0
}, {
duration: 600,
easing: tween.easeIn
});
}
});
} else if (self.type === 'zombistein') {
// Zombistein electric death with sparks
tween(self, {
scaleX: 1.8,
scaleY: 1.8,
tint: 0x00FFFF,
// Electric blue
alpha: 0.8
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Electric explosion
tween(self, {
scaleX: 3.0,
scaleY: 0.2,
rotation: Math.PI,
alpha: 0,
tint: 0xFFFF00 // Yellow spark
}, {
duration: 500,
easing: tween.easeOut
});
}
});
} else {
// Default zombie death animation - dramatic fall and fade
tween(self, {
rotation: Math.PI / 2,
y: self.y + 50,
scaleX: 1.2,
scaleY: 0.8,
alpha: 0.7
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
// Second phase - complete fade out
tween(self, {
scaleX: 0.8,
scaleY: 0.5,
alpha: 0,
y: self.y + 30
}, {
duration: 400,
easing: tween.easeIn
});
}
});
}
return true;
}
}
return false;
};
self.update = function () {
// Handle butter slow effect
if (self.butterSlowTime > 0) {
self.butterSlowTime--;
if (!self.isSlowed) {
self.isSlowed = true;
self.speed = self.baseSpeed * 0.3; // Slow to 30% of original speed
// Visual indication of slowing with butter color
tween(self, {
tint: 0xFFFF99
}, {
duration: 200,
easing: tween.easeOut
});
}
} else if (self.isSlowed) {
// Restore normal speed
self.isSlowed = false;
self.speed = self.baseSpeed;
// Remove visual effect
tween(self, {
tint: 0xffffff
}, {
duration: 200,
easing: tween.easeOut
});
}
// Miner zombie behavior
if (self.type === 'miner') {
var lastColumnX = gridStartX; // X position of the last (leftmost) grid column
// Check if miner has reached or passed the last column
var hasReachedLastColumn = self.x <= lastColumnX;
if (!self.underground && self.x < 1500 && !hasReachedLastColumn) {
// Go underground when reaching middle of screen, but only if hasn't reached last column yet
self.underground = true;
self.canAttack = false; // Cannot attack when underground
self.alpha = 0.3; // Make more transparent when underground
self.speed = -2; // Move faster underground
}
if (self.underground) {
// When underground, cannot attack plants - just move through them
// Check if reached the last column (leftmost grid column) to surface and turn around
if (hasReachedLastColumn) {
// Surface at last column and turn around
self.underground = false;
self.canAttack = true; // Can attack again when surfaced
self.alpha = 1;
self.speed = 1.2; // Move right (positive speed) to attack from behind
self.surfaceTimer = 0;
}
}
// Once surfaced at last column, continue moving right and cannot go underground again
if (hasReachedLastColumn && !self.underground && self.speed > 0) {
// Ensure miner continues moving right and cannot go underground
self.canAttack = true;
self.alpha = 1;
}
}
// Zombistein electric attack behavior
if (self.type === 'zombistein' && LK.ticks - self.lastElectricAttack > 300) {
// Electric attack every 5 seconds
self.lastElectricAttack = LK.ticks;
// Damage all plants in a 200px radius
for (var plantIdx = 0; plantIdx < plants.length; plantIdx++) {
var plant = plants[plantIdx];
var distance = Math.sqrt(Math.pow(self.x - plant.x, 2) + Math.pow(self.y - plant.y, 2));
if (distance < 200) {
// Electric visual effect
tween(plant, {
tint: 0x00ffff
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(plant, {
tint: 0xffffff
}, {
duration: 200,
easing: tween.easeIn
});
}
});
if (plant.takeDamage(30)) {
for (var k = plants.length - 1; k >= 0; k--) {
if (plants[k] === plant) {
plants.splice(k, 1);
plant.destroy();
break;
}
}
}
}
}
}
// Cart zombie behavior
if (self.type === 'cart' && !self.cartDestroyed) {
// Check if cart should be destroyed (takes damage first before zombie)
if (self.cartHealth <= 0) {
self.cartDestroyed = true;
self.speed = -1.5; // Move faster without cart
// Change appearance to show cart is destroyed
zombieGraphics.tint = 0x888888;
}
}
if (!self.isAttacking) {
self.x += self.speed;
// Walking bob animation
if (LK.ticks % 60 === 0) {
tween(self, {
y: self.y - 5
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
y: self.y + 5
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
}
// Check if zombie reached the left side (but allow lawnmower to activate first)
if (self.x < 50) {
gameOver();
return;
}
// Check for plants to attack (ignore spikeweed)
var myRow = Math.floor((self.y - 500) / 140);
for (var i = 0; i < plants.length; i++) {
var plant = plants[i];
var plantRow = Math.floor((plant.y - 500) / 140);
// Ignore spikeweed plants - zombies walk over them
// Miner zombie cannot attack when underground
if (plantRow === myRow && Math.abs(self.x - plant.x) < 80 && plant.type !== 'spikeweed' && self.canAttack) {
// Cart zombie instantly destroys plants except spikeweed
if (self.type === 'cart') {
plant.takeDamage(9999); // Instant destruction
for (var plantIdx = plants.length - 1; plantIdx >= 0; plantIdx--) {
if (plants[plantIdx] === plant) {
plants.splice(plantIdx, 1);
break;
}
}
} else if (self.type === 'newspaper' && self.enraged) {
// Newspaper zombie can eat when enraged
plant.takeDamage(9999); // Instant destruction when enraged
for (var plantIdx = plants.length - 1; plantIdx >= 0; plantIdx--) {
if (plants[plantIdx] === plant) {
plants.splice(plantIdx, 1);
break;
}
}
} else {
self.isAttacking = true;
}
break;
}
}
} else {
// Attack mode
self.attackCooldown++;
if (self.attackCooldown >= 60) {
// Attack every second
self.attackCooldown = 0;
// Find plant to attack (ignore spikeweed)
var myRow = Math.floor((self.y - 500) / 140);
var targetPlant = null;
for (var i = 0; i < plants.length; i++) {
var plant = plants[i];
var plantRow = Math.floor((plant.y - 500) / 140);
// Ignore spikeweed plants - zombies walk over them
// Miner zombie cannot attack when underground
if (plantRow === myRow && Math.abs(self.x - plant.x) < 80 && plant.type !== 'spikeweed' && self.canAttack) {
// Cart zombie instantly destroys plants except spikeweed
if (self.type === 'cart') {
plant.takeDamage(9999); // Instant destruction
for (var plantIdx = plants.length - 1; plantIdx >= 0; plantIdx--) {
if (plants[plantIdx] === plant) {
plants.splice(plantIdx, 1);
break;
}
}
self.isAttacking = false; // Continue moving after instant destruction
targetPlant = null;
} else if (self.type === 'newspaper' && self.enraged) {
// Newspaper zombie can eat when enraged
plant.takeDamage(9999); // Instant destruction when enraged
for (var plantIdx = plants.length - 1; plantIdx >= 0; plantIdx--) {
if (plants[plantIdx] === plant) {
plants.splice(plantIdx, 1);
break;
}
}
self.isAttacking = false; // Continue moving after instant destruction
targetPlant = null;
} else {
targetPlant = plant;
}
break;
}
}
if (targetPlant) {
// Fisheye eating effect
tween(self, {
scaleX: 1.3,
scaleY: 0.7
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeIn
});
}
});
if (targetPlant.takeDamage(self.attackDamage)) {
// Plant destroyed, remove from array
for (var j = plants.length - 1; j >= 0; j--) {
if (plants[j] === targetPlant) {
plants.splice(j, 1);
break;
}
}
self.isAttacking = false;
}
} else {
self.isAttacking = false;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228B22
});
/****
* Game Code
****/
// Night plants (mushrooms)
// Game variables
var sunPoints = 150;
var currentWave = 1;
var maxWaves = 5;
var waveSpawnTimer = 0;
var zombiesInWave = 0;
var maxZombiesInWave = 30;
var zombiesKilled = 0; // Track total zombies killed
var selectedPlantType = null;
var shovelSelected = false;
var gameState = 'loading'; // 'loading', 'levelSelect', 'playing', 'won', 'lost', 'minigame'
var currentLevel = 1;
var loadingScreen = null;
var levelButtons = [];
var currentMinigame = null;
var minigameSelector = null;
// Seed recharge system - 3 seconds = 180 ticks at 60 FPS
var seedRechargeTime = 180;
var seedCooldowns = {
sunflower: 0,
peashooter: 0,
wallnut: 0,
potatomine: 0,
carnivorousplant: 0,
doomshroom: 0,
repeater: 0,
threepeater: 0,
spikeweed: 0,
splitpeater: 0,
lilypad: 0,
puffshroom: 0,
sunshroom: 0,
fumeshroom: 0,
gravebuster: 0,
hypnoshroom: 0,
scaredyshroom: 0,
iceshroom: 0,
melonpulta: 0,
coltapulta: 0,
lanzamaiz: 0,
pot: 0
};
// Game arrays
var plants = [];
var zombies = [];
var peas = [];
var corns = [];
var suns = [];
var lawnmowers = [];
var waterTiles = [];
var tombstones = [];
var conveyorBelt = null;
var robotBoss = null;
var fireballs = [];
// Grid setup
var gridStartX = 300;
var gridStartY = 350;
var gridCols = 12;
var gridRows = 12;
var cellSize = 140;
// Plant costs
var plantCosts = {
sunflower: 50,
peashooter: 100,
wallnut: 50,
potatomine: 25,
carnivorousplant: 150,
doomshroom: 125,
repeater: 200,
threepeater: 325,
spikeweed: 100,
splitpeater: 175,
lilypad: 25,
puffshroom: 0,
sunshroom: 25,
fumeshroom: 75,
gravebuster: 75,
hypnoshroom: 75,
scaredyshroom: 25,
iceshroom: 75,
melonpulta: 300,
coltapulta: 100,
lanzamaiz: 100,
pot: 25
};
// UI Elements
var sunDisplay = new Text2('Sun: ' + sunPoints, {
size: 60,
fill: 0xFFFF00
});
sunDisplay.anchor.set(0, 0);
LK.gui.topLeft.addChild(sunDisplay);
sunDisplay.x = 120;
sunDisplay.y = 20;
var waveDisplay = new Text2('Wave: ' + currentWave + '/' + maxWaves, {
size: 50,
fill: 0xFFFFFF
});
waveDisplay.anchor.set(0.5, 0);
LK.gui.top.addChild(waveDisplay);
waveDisplay.y = 20;
// Plant selection buttons arranged vertically at bottom of screen
var sunflowerButton = LK.getAsset('sunflower', {
anchorX: 0.5,
anchorY: 0.5,
x: 200,
y: 2100,
scaleX: 0.6,
scaleY: 0.6
});
game.addChild(sunflowerButton);
var peashooterButton = LK.getAsset('peashooter', {
anchorX: 0.5,
anchorY: 0.5,
x: 300,
y: 2100,
scaleX: 0.6,
scaleY: 0.6
});
game.addChild(peashooterButton);
var wallnutButton = LK.getAsset('wallnut', {
anchorX: 0.5,
anchorY: 0.5,
x: 400,
y: 2100,
scaleX: 0.6,
scaleY: 0.6
});
game.addChild(wallnutButton);
var potatomineButton = LK.getAsset('potatomine', {
anchorX: 0.5,
anchorY: 0.5,
x: 500,
y: 2100,
scaleX: 0.6,
scaleY: 0.6
});
game.addChild(potatomineButton);
var carnivorousplantButton = LK.getAsset('carnivorousplant', {
anchorX: 0.5,
anchorY: 0.5,
x: 600,
y: 2100,
scaleX: 0.6,
scaleY: 0.6
});
game.addChild(carnivorousplantButton);
var doomshroomButton = LK.getAsset('doomshroom', {
anchorX: 0.5,
anchorY: 0.5,
x: 700,
y: 2100,
scaleX: 0.6,
scaleY: 0.6
});
game.addChild(doomshroomButton);
var repeaterButton = LK.getAsset('repeater', {
anchorX: 0.5,
anchorY: 0.5,
x: 800,
y: 2100,
scaleX: 0.6,
scaleY: 0.6
});
game.addChild(repeaterButton);
var threepeaterButton = LK.getAsset('threepeater', {
anchorX: 0.5,
anchorY: 0.5,
x: 900,
y: 2100,
scaleX: 0.6,
scaleY: 0.6
});
game.addChild(threepeaterButton);
var spikeButton = LK.getAsset('spikeweed', {
anchorX: 0.5,
anchorY: 0.5,
x: 1000,
y: 2100,
scaleX: 0.6,
scaleY: 0.6
});
game.addChild(spikeButton);
var splitpeaterButton = LK.getAsset('splitpeater', {
anchorX: 0.5,
anchorY: 0.5,
x: 1100,
y: 2100,
scaleX: 0.6,
scaleY: 0.6
});
game.addChild(splitpeaterButton);
var lilypadButton = LK.getAsset('lilypad', {
anchorX: 0.5,
anchorY: 0.5,
x: 1200,
y: 2100,
scaleX: 0.6,
scaleY: 0.6
});
game.addChild(lilypadButton);
// Restart button and shovel moved to right side of screen
var restartButton = LK.getAsset('restartButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1800,
y: 120,
scaleX: 0.8,
scaleY: 0.8
});
game.addChild(restartButton);
var shovelButton = LK.getAsset('shovel', {
anchorX: 0.5,
anchorY: 0.5,
x: 1920,
y: 120,
scaleX: 0.6,
scaleY: 0.6
});
game.addChild(shovelButton);
// Plant cost labels - arranged horizontally at bottom next to buttons
var sunflowerCost = new Text2('50', {
size: 20,
fill: 0xFFFFFF
});
sunflowerCost.anchor.set(0.5, 0);
sunflowerCost.x = 200;
sunflowerCost.y = 2140;
game.addChild(sunflowerCost);
var peashooterCost = new Text2('100', {
size: 20,
fill: 0xFFFFFF
});
peashooterCost.anchor.set(0.5, 0);
peashooterCost.x = 300;
peashooterCost.y = 2140;
game.addChild(peashooterCost);
var wallnutCost = new Text2('50', {
size: 20,
fill: 0xFFFFFF
});
wallnutCost.anchor.set(0.5, 0);
wallnutCost.x = 400;
wallnutCost.y = 2140;
game.addChild(wallnutCost);
var potatomineeCost = new Text2('25', {
size: 20,
fill: 0xFFFFFF
});
potatomineeCost.anchor.set(0.5, 0);
potatomineeCost.x = 500;
potatomineeCost.y = 2140;
game.addChild(potatomineeCost);
var carnivorousplantCost = new Text2('150', {
size: 20,
fill: 0xFFFFFF
});
carnivorousplantCost.anchor.set(0.5, 0);
carnivorousplantCost.x = 600;
carnivorousplantCost.y = 2140;
game.addChild(carnivorousplantCost);
var doomshroomCost = new Text2('125', {
size: 20,
fill: 0xFFFFFF
});
doomshroomCost.anchor.set(0.5, 0);
doomshroomCost.x = 700;
doomshroomCost.y = 2140;
game.addChild(doomshroomCost);
var repeaterCost = new Text2('200', {
size: 20,
fill: 0xFFFFFF
});
repeaterCost.anchor.set(0.5, 0);
repeaterCost.x = 800;
repeaterCost.y = 2140;
game.addChild(repeaterCost);
var threepeaterCost = new Text2('325', {
size: 20,
fill: 0xFFFFFF
});
threepeaterCost.anchor.set(0.5, 0);
threepeaterCost.x = 900;
threepeaterCost.y = 2140;
game.addChild(threepeaterCost);
var spikeCost = new Text2('100', {
size: 20,
fill: 0xFFFFFF
});
spikeCost.anchor.set(0.5, 0);
spikeCost.x = 1000;
spikeCost.y = 2140;
game.addChild(spikeCost);
var splitpeaterCost = new Text2('175', {
size: 20,
fill: 0xFFFFFF
});
splitpeaterCost.anchor.set(0.5, 0);
splitpeaterCost.x = 1100;
splitpeaterCost.y = 2140;
game.addChild(splitpeaterCost);
var lilypadCost = new Text2('25', {
size: 20,
fill: 0xFFFFFF
});
lilypadCost.anchor.set(0.5, 0);
lilypadCost.x = 1200;
lilypadCost.y = 2140;
game.addChild(lilypadCost);
// Create separate catapult plant buttons for roof levels
var melonpultaButton = LK.getAsset('melonpulta', {
anchorX: 0.5,
anchorY: 0.5,
x: 800,
y: 2100,
scaleX: 0.6,
scaleY: 0.6
});
game.addChild(melonpultaButton);
melonpultaButton.visible = false; // Hidden by default
var coltapultaButton = LK.getAsset('coltapulta', {
anchorX: 0.5,
anchorY: 0.5,
x: 900,
y: 2100,
scaleX: 0.6,
scaleY: 0.6
});
game.addChild(coltapultaButton);
coltapultaButton.visible = false; // Hidden by default
var lanzamaizButton = LK.getAsset('lanzamaiz', {
anchorX: 0.5,
anchorY: 0.5,
x: 1100,
y: 2100,
scaleX: 0.6,
scaleY: 0.6
});
game.addChild(lanzamaizButton);
lanzamaizButton.visible = false; // Hidden by default
// Create cost labels for catapult plants
var melonpultaCost = new Text2('300', {
size: 20,
fill: 0xFFFFFF
});
melonpultaCost.anchor.set(0.5, 0);
melonpultaCost.x = 800;
melonpultaCost.y = 2140;
game.addChild(melonpultaCost);
melonpultaCost.visible = false; // Hidden by default
var coltapultaCost = new Text2('100', {
size: 20,
fill: 0xFFFFFF
});
coltapultaCost.anchor.set(0.5, 0);
coltapultaCost.x = 900;
coltapultaCost.y = 2140;
game.addChild(coltapultaCost);
coltapultaCost.visible = false; // Hidden by default
var lanzamaizCost = new Text2('100', {
size: 20,
fill: 0xFFFFFF
});
lanzamaizCost.anchor.set(0.5, 0);
lanzamaizCost.x = 1100;
lanzamaizCost.y = 2140;
game.addChild(lanzamaizCost);
lanzamaizCost.visible = false; // Hidden by default
// Create pot plant button for roof levels
var potButton = LK.getAsset('pot', {
anchorX: 0.5,
anchorY: 0.5,
x: 300,
y: 2100,
scaleX: 0.6,
scaleY: 0.6
});
game.addChild(potButton);
potButton.visible = false; // Hidden by default
// Create cost label for pot plant
var potCost = new Text2('25', {
size: 20,
fill: 0xFFFFFF
});
potCost.anchor.set(0.5, 0);
potCost.x = 300;
potCost.y = 2140;
game.addChild(potCost);
potCost.visible = false; // Hidden by default
// Boss health display
var bossHealthDisplay = new Text2('Boss Health: 1000/1000', {
size: 40,
fill: 0xFF0000
});
bossHealthDisplay.anchor.set(0, 1); // Bottom left anchor
bossHealthDisplay.x = 120;
bossHealthDisplay.y = 2600;
game.addChild(bossHealthDisplay);
bossHealthDisplay.visible = false; // Hidden by default
var restartText = new Text2('RESTART', {
size: 16,
fill: 0xFFFFFF
});
restartText.anchor.set(0.5, 0);
restartText.x = 1800;
restartText.y = 90;
game.addChild(restartText);
// Exit level button - positioned next to restart button
var exitLevelButton = LK.getAsset('restartButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1650,
y: 120,
scaleX: 0.8,
scaleY: 0.8
});
game.addChild(exitLevelButton);
var exitText = new Text2('EXIT', {
size: 16,
fill: 0xFFFFFF
});
exitText.anchor.set(0.5, 0);
exitText.x = 1650;
exitText.y = 90;
game.addChild(exitText);
// Draw grid
var gridTiles = [];
for (var row = 0; row < gridRows; row++) {
for (var col = 0; col < gridCols; col++) {
var gridCell = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
x: gridStartX + col * cellSize,
y: gridStartY + row * cellSize,
alpha: 0.3
});
game.addChild(gridCell);
gridTiles.push(gridCell);
}
}
// Add decorative bushes on the right to cover zombie spawn area - more bushes arranged at different depths
var bushes = LK.getAsset('bushes', {
anchorX: 0.5,
anchorY: 0.5,
x: 2000,
y: 600,
scaleX: 1.8,
scaleY: 1.8
});
game.addChild(bushes);
var bushes2 = LK.getAsset('bushes', {
anchorX: 0.5,
anchorY: 0.5,
x: 2000,
y: 900,
scaleX: 1.6,
scaleY: 1.6
});
game.addChild(bushes2);
var bushes3 = LK.getAsset('bushes', {
anchorX: 0.5,
anchorY: 0.5,
x: 2000,
y: 1200,
scaleX: 1.7,
scaleY: 1.7
});
game.addChild(bushes3);
var bushes4 = LK.getAsset('bushes', {
anchorX: 0.5,
anchorY: 0.5,
x: 2000,
y: 1500,
scaleX: 1.5,
scaleY: 1.5
});
game.addChild(bushes4);
var bushes5 = LK.getAsset('bushes', {
anchorX: 0.5,
anchorY: 0.5,
x: 2000,
y: 1800,
scaleX: 1.4,
scaleY: 1.4
});
game.addChild(bushes5);
var bushes6 = LK.getAsset('bushes', {
anchorX: 0.5,
anchorY: 0.5,
x: 1900,
y: 750,
scaleX: 1.3,
scaleY: 1.3
});
game.addChild(bushes6);
var bushes7 = LK.getAsset('bushes', {
anchorX: 0.5,
anchorY: 0.5,
x: 1900,
y: 1350,
scaleX: 1.2,
scaleY: 1.2
});
game.addChild(bushes7);
var bushes8 = LK.getAsset('bushes', {
anchorX: 0.5,
anchorY: 0.5,
x: 1950,
y: 450,
scaleX: 1.6,
scaleY: 1.6
});
game.addChild(bushes8);
var bushes9 = LK.getAsset('bushes', {
anchorX: 0.5,
anchorY: 0.5,
x: 1950,
y: 1050,
scaleX: 1.4,
scaleY: 1.4
});
game.addChild(bushes9);
var bushes10 = LK.getAsset('bushes', {
anchorX: 0.5,
anchorY: 0.5,
x: 1950,
y: 1650,
scaleX: 1.5,
scaleY: 1.5
});
game.addChild(bushes10);
var bushes11 = LK.getAsset('bushes', {
anchorX: 0.5,
anchorY: 0.5,
x: 1850,
y: 600,
scaleX: 1.1,
scaleY: 1.1
});
game.addChild(bushes11);
var bushes12 = LK.getAsset('bushes', {
anchorX: 0.5,
anchorY: 0.5,
x: 1850,
y: 1200,
scaleX: 1.0,
scaleY: 1.0
});
game.addChild(bushes12);
var bushes13 = LK.getAsset('bushes', {
anchorX: 0.5,
anchorY: 0.5,
x: 1850,
y: 1800,
scaleX: 1.3,
scaleY: 1.3
});
game.addChild(bushes13);
// Initialize lawnmowers
for (var row = 0; row < gridRows; row++) {
var lawnmower = new Lawnmower();
lawnmower.x = 220;
lawnmower.y = gridStartY + row * cellSize;
game.addChild(lawnmower);
lawnmowers.push(lawnmower);
}
// Move all bushes to front to render above zombies
bushes.parent.removeChild(bushes);
game.addChild(bushes);
bushes2.parent.removeChild(bushes2);
game.addChild(bushes2);
bushes3.parent.removeChild(bushes3);
game.addChild(bushes3);
bushes4.parent.removeChild(bushes4);
game.addChild(bushes4);
bushes5.parent.removeChild(bushes5);
game.addChild(bushes5);
bushes6.parent.removeChild(bushes6);
game.addChild(bushes6);
bushes7.parent.removeChild(bushes7);
game.addChild(bushes7);
bushes8.parent.removeChild(bushes8);
game.addChild(bushes8);
bushes9.parent.removeChild(bushes9);
game.addChild(bushes9);
bushes10.parent.removeChild(bushes10);
game.addChild(bushes10);
bushes11.parent.removeChild(bushes11);
game.addChild(bushes11);
bushes12.parent.removeChild(bushes12);
game.addChild(bushes12);
bushes13.parent.removeChild(bushes13);
game.addChild(bushes13);
// Initialize loading screen
if (gameState === 'loading') {
loadingScreen = new LoadingScreen();
game.addChild(loadingScreen);
// Hide all game elements initially
hideGameElements();
}
function updateGridTiles(levelNumber) {
// Destroy existing grid tiles
for (var i = gridTiles.length - 1; i >= 0; i--) {
gridTiles[i].destroy();
}
gridTiles = [];
// Determine tile type based on level
var tileAsset = 'gridCell';
if (levelNumber > 15) {
// Roof levels
if (levelNumber === 19) {
tileAsset = 'roofTileNight'; // Night roof
} else {
tileAsset = 'roofTileDay'; // Day roof
}
} else if (levelNumber > 5 && levelNumber <= 10 || levelNumber === 19) {
// Night levels (6-10)
tileAsset = 'grassTileNight';
} else {
// Day levels (1-5) and pool levels (11-15) use day grass
tileAsset = 'grassTileDay';
}
// Create new grid with appropriate tiles
for (var row = 0; row < gridRows; row++) {
for (var col = 0; col < gridCols; col++) {
var gridCell = LK.getAsset(tileAsset, {
anchorX: 0.5,
anchorY: 0.5,
x: gridStartX + col * cellSize,
y: gridStartY + row * cellSize,
alpha: 0.3
});
game.addChild(gridCell);
gridTiles.push(gridCell);
}
}
}
function updateSunDisplay() {
sunDisplay.setText('Sun: ' + sunPoints);
}
function canAffordPlant(plantType) {
return sunPoints >= plantCosts[plantType];
}
function isSeedReady(plantType) {
return seedCooldowns[plantType] <= 0;
}
function canPlant(plantType) {
return canAffordPlant(plantType) && isSeedReady(plantType);
}
function getGridPosition(x, y) {
var col = Math.floor((x - gridStartX + cellSize / 2) / cellSize);
var row = Math.floor((y - gridStartY + cellSize / 2) / cellSize);
if (col >= 0 && col < gridCols && row >= 0 && row < gridRows) {
return {
row: row,
col: col,
x: gridStartX + col * cellSize,
y: gridStartY + row * cellSize
};
}
return null;
}
function isGridOccupied(row, col) {
for (var i = 0; i < plants.length; i++) {
var plant = plants[i];
var plantRow = Math.floor((plant.y - gridStartY + cellSize / 2) / cellSize);
var plantCol = Math.floor((plant.x - gridStartX + cellSize / 2) / cellSize);
if (plantRow === row && plantCol === col) {
return true;
}
}
return false;
}
function isPositionOnWater(row, col) {
// Check if position is on water (pool levels 11-15, rows 4-8)
var isPoolLevel = currentLevel > 10;
if (isPoolLevel && row >= 4 && row <= 8) {
return true;
}
return false;
}
function canPlantOnPosition(plantType, row, col) {
// Check what's already at this position
var hasLilypad = false;
var hasPot = false;
var isOccupied = false;
for (var i = 0; i < plants.length; i++) {
var plant = plants[i];
var plantRow = Math.floor((plant.y - gridStartY + cellSize / 2) / cellSize);
var plantCol = Math.floor((plant.x - gridStartX + cellSize / 2) / cellSize);
if (plantRow === row && plantCol === col) {
if (plant.type === 'lilypad') {
hasLilypad = true;
} else if (plant.type === 'pot') {
hasPot = true;
} else {
isOccupied = true; // Other plant types occupy space completely
}
}
}
// Check if this is a roof level
var isRoofLevel = currentLevel > 15;
// Check if this is a water position
var isWaterPos = isPositionOnWater(row, col);
// Handle roof level rules
if (isRoofLevel) {
if (plantType === 'pot') {
return !isOccupied && !hasPot; // Pot needs empty space
} else {
return hasPot && !isOccupied; // Other plants need pot and no other plants
}
}
// Handle water level rules
if (isWaterPos) {
if (plantType === 'lilypad') {
return !isOccupied && !hasLilypad; // Lilypad needs empty water
} else {
return hasLilypad && !isOccupied; // Other plants need lilypad and no other plants
}
}
// Handle regular ground rules
if (plantType === 'lilypad') {
return false; // Lilypad can only be on water
}
return !isOccupied && !hasLilypad && !hasPot; // Regular plants need completely empty space
}
function spawnTombstones() {
// Spawn 3-5 tombstones randomly on the grid for night levels
var tombstoneCount = 3 + Math.floor(Math.random() * 3);
// Define vertical middle line at column 6 (middle of 12 columns)
var middleColumn = Math.floor(gridCols / 2);
for (var i = 0; i < tombstoneCount; i++) {
var attempts = 0;
var placed = false;
while (!placed && attempts < 20) {
var randomRow = Math.floor(Math.random() * gridRows);
// Only spawn tombstones to the right of the middle line (behind middle)
var randomCol = middleColumn + Math.floor(Math.random() * (gridCols - middleColumn - 1));
// Check if position is already occupied
var occupied = false;
for (var j = 0; j < tombstones.length; j++) {
var tombstoneRow = Math.floor((tombstones[j].y - gridStartY + cellSize / 2) / cellSize);
var tombstoneCol = Math.floor((tombstones[j].x - gridStartX + cellSize / 2) / cellSize);
if (tombstoneRow === randomRow && tombstoneCol === randomCol) {
occupied = true;
break;
}
}
if (!occupied) {
var tombstone = new Tombstone();
tombstone.x = gridStartX + randomCol * cellSize;
tombstone.y = gridStartY + randomRow * cellSize;
game.addChild(tombstone);
tombstones.push(tombstone);
placed = true;
}
attempts++;
}
}
}
function spawnZombie() {
var rand = Math.random();
var zombieType = 'normal';
if (rand < 0.15) {
zombieType = 'fast';
} else if (rand < 0.25) {
zombieType = 'snailBucket';
} else if (rand < 0.35) {
zombieType = 'miner';
} else if (rand < 0.45) {
zombieType = 'cart';
} else if (rand < 0.55) {
zombieType = 'newspaper';
}
var zombie = new Zombie(zombieType);
zombie.x = 2100;
zombie.y = gridStartY + Math.floor(Math.random() * gridRows) * cellSize;
// Start with spawn effect
zombie.alpha = 0;
zombie.scaleX = 1.5;
zombie.scaleY = 1.5;
zombie.tint = 0x88ff88;
game.addChild(zombie);
zombies.push(zombie);
// Spawn animation
tween(zombie, {
alpha: 1,
scaleX: 1.0,
scaleY: 1.0,
tint: 0xffffff
}, {
duration: 600,
easing: tween.easeOut
});
}
function gameOver() {
if (gameState === 'playing') {
gameState = 'lost';
LK.showGameOver();
}
}
function checkWinCondition() {
if (currentWave > maxWaves && zombies.length === 0) {
if (gameState === 'playing') {
gameState = 'won';
// Victory celebration animation for all plants
for (var i = 0; i < plants.length; i++) {
var plant = plants[i];
var delay = i * 100; // Stagger the animations
LK.setTimeout(function (capturedPlant) {
return function () {
tween(capturedPlant, {
scaleX: 1.3,
scaleY: 1.3,
rotation: 0.5
}, {
duration: 400,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(capturedPlant, {
scaleX: 1.0,
scaleY: 1.0,
rotation: 0
}, {
duration: 400,
easing: tween.bounceOut
});
}
});
};
}(plant), delay);
}
// Award bonus sun for completing level
var levelBonus = currentLevel * 5;
sunPoints += levelBonus;
updateSunDisplay();
LK.showYouWin();
}
}
}
function showLevelSelection() {
gameState = 'levelSelect';
// Stop background music when returning to level selection
LK.stopMusic();
// Start menu music for level selection
LK.playMusic('menuMusic', {
loop: true
});
// Clear loading screen
if (loadingScreen) {
loadingScreen.destroy();
loadingScreen = null;
}
// Hide all game elements
hideGameElements();
// Create day level buttons (1-5)
var dayButtonSpacing = 150;
var dayStartX = 1024 - 2 * dayButtonSpacing;
for (var i = 1; i <= 5; i++) {
var levelButton = new LevelButton(i);
levelButton.x = dayStartX + (i - 1) * dayButtonSpacing;
levelButton.y = 600;
game.addChild(levelButton);
levelButtons.push(levelButton);
}
// Create night level buttons (6-10)
var nightButtonSpacing = 150;
var nightStartX = 1024 - 2 * nightButtonSpacing;
for (var j = 6; j <= 10; j++) {
var nightLevelButton = new LevelButton(j);
nightLevelButton.x = nightStartX + (j - 6) * nightButtonSpacing;
nightLevelButton.y = 900;
// Tint night buttons with dark blue
nightLevelButton.tint = 0x000080;
game.addChild(nightLevelButton);
levelButtons.push(nightLevelButton);
}
// Create pool level buttons (11-15)
var poolButtonSpacing = 150;
var poolStartX = 1024 - 2 * poolButtonSpacing;
for (var k = 11; k <= 15; k++) {
var poolLevelButton = new LevelButton(k);
poolLevelButton.x = poolStartX + (k - 11) * poolButtonSpacing;
poolLevelButton.y = 1200;
// Tint pool buttons with cyan/blue for water theme
poolLevelButton.tint = 0x00FFFF;
game.addChild(poolLevelButton);
levelButtons.push(poolLevelButton);
}
// Create roof level buttons (16-18) - day roof levels
var roofButtonSpacing = 150;
var roofStartX = 1024 - 1 * roofButtonSpacing;
for (var r = 16; r <= 18; r++) {
var roofLevelButton = new LevelButton(r);
roofLevelButton.x = roofStartX + (r - 16) * roofButtonSpacing;
roofLevelButton.y = 1500;
// Tint roof buttons with brown/gray for roof theme
roofLevelButton.tint = 0x8B4513;
game.addChild(roofLevelButton);
levelButtons.push(roofLevelButton);
}
// Create night roof boss fight button (19)
var bossLevelButton = new LevelButton(19);
bossLevelButton.x = 1024;
bossLevelButton.y = 1800;
// Tint boss button with dark red for boss theme
bossLevelButton.tint = 0x8B0000;
// Make boss button bigger to indicate it's special
bossLevelButton.scaleX = 1.5;
bossLevelButton.scaleY = 1.5;
game.addChild(bossLevelButton);
levelButtons.push(bossLevelButton);
// Create tombstone button for minigames
var minigameTombstoneButton = LK.getAsset('tombstone', {
anchorX: 0.5,
anchorY: 0.5,
x: 1424,
y: 1800,
scaleX: 1.2,
scaleY: 1.2
});
game.addChild(minigameTombstoneButton);
levelButtons.push(minigameTombstoneButton);
// Add minigames text label
var minigamesText = new Text2('MINIJUEGOS', {
size: 40,
fill: 0xFFFFFF
});
minigamesText.anchor.set(0.5, 0.5);
minigamesText.x = 1424;
minigamesText.y = 1900;
game.addChild(minigamesText);
levelButtons.push(minigamesText);
// Add click handler for minigame tombstone
minigameTombstoneButton.down = function (x, y, obj) {
// Clear level selection elements
for (var i = levelButtons.length - 1; i >= 0; i--) {
levelButtons[i].destroy();
}
levelButtons = [];
// Show minigame selector
gameState = 'minigame';
minigameSelector = new MinigameSelector();
game.addChild(minigameSelector);
};
}
function hideDayPlants() {
sunflowerButton.visible = false;
peashooterButton.visible = false;
wallnutButton.visible = false;
potatomineButton.visible = false;
carnivorousplantButton.visible = false;
doomshroomButton.visible = false;
repeaterButton.visible = false;
threepeaterButton.visible = false;
spikeButton.visible = false;
splitpeaterButton.visible = false;
lilypadButton.visible = false;
melonpultaButton.visible = false;
coltapultaButton.visible = false;
lanzamaizButton.visible = false;
sunflowerCost.visible = false;
peashooterCost.visible = false;
wallnutCost.visible = false;
potatomineeCost.visible = false;
carnivorousplantCost.visible = false;
doomshroomCost.visible = false;
repeaterCost.visible = false;
threepeaterCost.visible = false;
spikeCost.visible = false;
splitpeaterCost.visible = false;
lilypadCost.visible = false;
melonpultaCost.visible = false;
coltapultaCost.visible = false;
lanzamaizCost.visible = false;
potButton.visible = false;
potCost.visible = false;
}
function showDayPlants() {
sunflowerButton.visible = true;
peashooterButton.visible = true;
wallnutButton.visible = true;
potatomineButton.visible = true;
carnivorousplantButton.visible = true;
doomshroomButton.visible = true;
repeaterButton.visible = true;
threepeaterButton.visible = true;
spikeButton.visible = true;
splitpeaterButton.visible = true;
lilypadButton.visible = true;
sunflowerCost.visible = true;
peashooterCost.visible = true;
wallnutCost.visible = true;
potatomineeCost.visible = true;
carnivorousplantCost.visible = true;
doomshroomCost.visible = true;
repeaterCost.visible = true;
threepeaterCost.visible = true;
spikeCost.visible = true;
splitpeaterCost.visible = true;
lilypadCost.visible = true;
}
// Night plant buttons - store globally to manage them
var nightPlantButtons = [];
var nightPlantCosts = [];
function createNightPlantButtons() {
// Create night plant buttons
var puffshroomButton = LK.getAsset('puffshroom', {
anchorX: 0.5,
anchorY: 0.5,
x: 200,
y: 2100,
scaleX: 0.8,
scaleY: 0.8
});
game.addChild(puffshroomButton);
nightPlantButtons.push(puffshroomButton);
var sunshroomButton = LK.getAsset('sunshroom', {
anchorX: 0.5,
anchorY: 0.5,
x: 300,
y: 2100,
scaleX: 0.8,
scaleY: 0.8
});
game.addChild(sunshroomButton);
nightPlantButtons.push(sunshroomButton);
var fumeshroomButton = LK.getAsset('fumeshroom', {
anchorX: 0.5,
anchorY: 0.5,
x: 400,
y: 2100,
scaleX: 0.8,
scaleY: 0.8
});
game.addChild(fumeshroomButton);
nightPlantButtons.push(fumeshroomButton);
var gravebusterButton = LK.getAsset('gravebuster', {
anchorX: 0.5,
anchorY: 0.5,
x: 500,
y: 2100,
scaleX: 0.8,
scaleY: 0.8
});
game.addChild(gravebusterButton);
nightPlantButtons.push(gravebusterButton);
var hypnoshroomButton = LK.getAsset('hypnoshroom', {
anchorX: 0.5,
anchorY: 0.5,
x: 600,
y: 2100,
scaleX: 0.8,
scaleY: 0.8
});
game.addChild(hypnoshroomButton);
nightPlantButtons.push(hypnoshroomButton);
var scaredyshroomButton = LK.getAsset('scaredyshroom', {
anchorX: 0.5,
anchorY: 0.5,
x: 700,
y: 2100,
scaleX: 0.8,
scaleY: 0.8
});
game.addChild(scaredyshroomButton);
nightPlantButtons.push(scaredyshroomButton);
var iceshroomButton = LK.getAsset('iceshroom', {
anchorX: 0.5,
anchorY: 0.5,
x: 800,
y: 2100,
scaleX: 0.8,
scaleY: 0.8
});
game.addChild(iceshroomButton);
nightPlantButtons.push(iceshroomButton);
// Create night plant cost labels
var puffshroomCost = new Text2('0', {
size: 20,
fill: 0xFFFFFF
});
puffshroomCost.anchor.set(0.5, 0);
puffshroomCost.x = 200;
puffshroomCost.y = 2140;
game.addChild(puffshroomCost);
nightPlantCosts.push(puffshroomCost);
var sunshroomCost = new Text2('25', {
size: 20,
fill: 0xFFFFFF
});
sunshroomCost.anchor.set(0.5, 0);
sunshroomCost.x = 300;
sunshroomCost.y = 2140;
game.addChild(sunshroomCost);
nightPlantCosts.push(sunshroomCost);
var fumeshroomCost = new Text2('75', {
size: 20,
fill: 0xFFFFFF
});
fumeshroomCost.anchor.set(0.5, 0);
fumeshroomCost.x = 400;
fumeshroomCost.y = 2140;
game.addChild(fumeshroomCost);
nightPlantCosts.push(fumeshroomCost);
var gravebusterCost = new Text2('75', {
size: 20,
fill: 0xFFFFFF
});
gravebusterCost.anchor.set(0.5, 0);
gravebusterCost.x = 500;
gravebusterCost.y = 2140;
game.addChild(gravebusterCost);
nightPlantCosts.push(gravebusterCost);
var hypnoshroomCost = new Text2('75', {
size: 20,
fill: 0xFFFFFF
});
hypnoshroomCost.anchor.set(0.5, 0);
hypnoshroomCost.x = 600;
hypnoshroomCost.y = 2140;
game.addChild(hypnoshroomCost);
nightPlantCosts.push(hypnoshroomCost);
var scaredyshroomCost = new Text2('25', {
size: 20,
fill: 0xFFFFFF
});
scaredyshroomCost.anchor.set(0.5, 0);
scaredyshroomCost.x = 700;
scaredyshroomCost.y = 2140;
game.addChild(scaredyshroomCost);
nightPlantCosts.push(scaredyshroomCost);
var iceshroomCost = new Text2('75', {
size: 20,
fill: 0xFFFFFF
});
iceshroomCost.anchor.set(0.5, 0);
iceshroomCost.x = 800;
iceshroomCost.y = 2140;
game.addChild(iceshroomCost);
nightPlantCosts.push(iceshroomCost);
// Add event handlers for night plant buttons
puffshroomButton.down = function (x, y, obj) {
if (canPlant('puffshroom')) {
selectedPlantType = 'puffshroom';
shovelSelected = false;
}
};
sunshroomButton.down = function (x, y, obj) {
if (canPlant('sunshroom')) {
selectedPlantType = 'sunshroom';
shovelSelected = false;
}
};
fumeshroomButton.down = function (x, y, obj) {
if (canPlant('fumeshroom')) {
selectedPlantType = 'fumeshroom';
shovelSelected = false;
}
};
gravebusterButton.down = function (x, y, obj) {
if (canPlant('gravebuster')) {
selectedPlantType = 'gravebuster';
shovelSelected = false;
}
};
hypnoshroomButton.down = function (x, y, obj) {
if (canPlant('hypnoshroom')) {
selectedPlantType = 'hypnoshroom';
shovelSelected = false;
}
};
scaredyshroomButton.down = function (x, y, obj) {
if (canPlant('scaredyshroom')) {
selectedPlantType = 'scaredyshroom';
shovelSelected = false;
}
};
iceshroomButton.down = function (x, y, obj) {
if (canPlant('iceshroom')) {
selectedPlantType = 'iceshroom';
shovelSelected = false;
}
};
}
function cleanupNightPlantButtons() {
// Destroy night plant buttons
for (var i = nightPlantButtons.length - 1; i >= 0; i--) {
if (nightPlantButtons[i]) {
nightPlantButtons[i].destroy();
}
}
nightPlantButtons = [];
// Destroy night plant cost labels
for (var j = nightPlantCosts.length - 1; j >= 0; j--) {
if (nightPlantCosts[j]) {
nightPlantCosts[j].destroy();
}
}
nightPlantCosts = [];
}
function hideGameElements() {
// Hide all UI elements
sunDisplay.alpha = 0;
waveDisplay.alpha = 0;
// Hide all plant buttons
sunflowerButton.alpha = 0;
peashooterButton.alpha = 0;
wallnutButton.alpha = 0;
potatomineButton.alpha = 0;
carnivorousplantButton.alpha = 0;
doomshroomButton.alpha = 0;
repeaterButton.alpha = 0;
threepeaterButton.alpha = 0;
spikeButton.alpha = 0;
splitpeaterButton.alpha = 0;
restartButton.alpha = 0;
shovelButton.alpha = 0;
// Hide cost labels
sunflowerCost.alpha = 0;
peashooterCost.alpha = 0;
wallnutCost.alpha = 0;
potatomineeCost.alpha = 0;
carnivorousplantCost.alpha = 0;
doomshroomCost.alpha = 0;
repeaterCost.alpha = 0;
threepeaterCost.alpha = 0;
spikeCost.alpha = 0;
splitpeaterCost.alpha = 0;
lilypadCost.alpha = 0;
restartText.alpha = 0;
// Hide lilypad button
lilypadButton.alpha = 0;
// Hide exit button
exitLevelButton.alpha = 0;
exitText.alpha = 0;
// Hide tombstone button - check if it exists first
if (tombstoneButton) {
tombstoneButton.alpha = 0;
}
if (tombstoneText) {
tombstoneText.alpha = 0;
}
// Hide boss health display
bossHealthDisplay.visible = false;
}
function showGameElements() {
// Show all UI elements
sunDisplay.alpha = 1;
waveDisplay.alpha = 1;
// Show all plant buttons
sunflowerButton.alpha = 1;
peashooterButton.alpha = 1;
wallnutButton.alpha = 1;
potatomineButton.alpha = 1;
carnivorousplantButton.alpha = 1;
doomshroomButton.alpha = 1;
repeaterButton.alpha = 1;
threepeaterButton.alpha = 1;
spikeButton.alpha = 1;
splitpeaterButton.alpha = 1;
restartButton.alpha = 1;
shovelButton.alpha = 1;
// Show cost labels
sunflowerCost.alpha = 1;
peashooterCost.alpha = 1;
wallnutCost.alpha = 1;
potatomineeCost.alpha = 1;
carnivorousplantCost.alpha = 1;
doomshroomCost.alpha = 1;
repeaterCost.alpha = 1;
threepeaterCost.alpha = 1;
spikeCost.alpha = 1;
splitpeaterCost.alpha = 1;
lilypadCost.alpha = 1;
restartText.alpha = 1;
// Show lilypad button
lilypadButton.alpha = 1;
// Show exit button
exitLevelButton.alpha = 1;
exitText.alpha = 1;
// Show tombstone button - check if it exists first
if (tombstoneButton) {
tombstoneButton.alpha = 1;
}
if (tombstoneText) {
tombstoneText.alpha = 1;
}
}
function startLevelWithSelectedPlants(levelNumber, selectedPlants) {
currentLevel = levelNumber;
gameState = 'playing';
// Show game elements
showGameElements();
// Stop menu music and start background music for all levels
LK.stopMusic();
LK.playMusic('backgroundMusic', {
loop: true
});
// Reset game based on level with selected plants
resetGameForLevel(levelNumber);
// Hide all plant buttons first
hideDayPlants();
// For boss fight (level 19), allow conveyor belt plant placement but no manual selection
if (levelNumber === 19) {
// Boss fight - conveyor belt will provide plants, no manual selection needed
// But still allow placement from conveyor belt seeds
return;
}
// Show only selected plants and create their buttons dynamically
for (var i = 0; i < selectedPlants.length && i < 8; i++) {
var plantType = selectedPlants[i];
var xPos = 200 + i * 100;
var yPos = 2100;
// Get the corresponding button for this plant type
var button = null;
var costLabel = null;
if (plantType === 'sunflower') {
button = sunflowerButton;
costLabel = sunflowerCost;
} else if (plantType === 'peashooter') {
button = peashooterButton;
costLabel = peashooterCost;
} else if (plantType === 'wallnut') {
button = wallnutButton;
costLabel = wallnutCost;
} else if (plantType === 'potatomine') {
button = potatomineButton;
costLabel = potatomineeCost;
} else if (plantType === 'carnivorousplant') {
button = carnivorousplantButton;
costLabel = carnivorousplantCost;
} else if (plantType === 'doomshroom') {
button = doomshroomButton;
costLabel = doomshroomCost;
} else if (plantType === 'repeater') {
button = repeaterButton;
costLabel = repeaterCost;
} else if (plantType === 'threepeater') {
button = threepeaterButton;
costLabel = threepeaterCost;
} else if (plantType === 'spikeweed') {
button = spikeButton;
costLabel = spikeCost;
} else if (plantType === 'splitpeater') {
button = splitpeaterButton;
costLabel = splitpeaterCost;
} else if (plantType === 'lilypad') {
button = lilypadButton;
costLabel = lilypadCost;
} else if (plantType === 'melonpulta') {
button = melonpultaButton;
costLabel = melonpultaCost;
} else if (plantType === 'coltapulta') {
button = coltapultaButton;
costLabel = coltapultaCost;
} else if (plantType === 'lanzamaiz') {
button = lanzamaizButton;
costLabel = lanzamaizCost;
} else if (plantType === 'pot') {
button = potButton;
costLabel = potCost;
} else if (plantType === 'puffshroom' || plantType === 'sunshroom' || plantType === 'fumeshroom' || plantType === 'gravebuster' || plantType === 'hypnoshroom' || plantType === 'scaredyshroom' || plantType === 'iceshroom') {
// Handle night plants ONLY if they were actually selected - create temporary buttons for selected night plants
var nightButton = LK.getAsset(plantType, {
anchorX: 0.5,
anchorY: 0.5,
x: xPos,
y: yPos,
scaleX: 0.6,
scaleY: 0.6
});
game.addChild(nightButton);
nightButton.plantType = plantType;
nightButton.visible = true;
nightButton.alpha = 1;
// Create closure to capture the plantType correctly
(function (capturedPlantType) {
nightButton.down = function (x, y, obj) {
if (canPlant(capturedPlantType)) {
selectedPlantType = capturedPlantType;
shovelSelected = false;
// Update button visual state
nightButton.tint = 0x808080;
// Reset other buttons' visual states
for (var btnIdx = 0; btnIdx < nightPlantButtons.length; btnIdx++) {
if (nightPlantButtons[btnIdx] !== nightButton) {
nightPlantButtons[btnIdx].tint = 0xffffff;
}
}
}
};
})(plantType);
// Store the button for later management
nightPlantButtons.push(nightButton);
var nightCostLabel = new Text2(plantCosts[plantType].toString(), {
size: 20,
fill: 0xFFFFFF
});
nightCostLabel.anchor.set(0.5, 0);
nightCostLabel.x = xPos;
nightCostLabel.y = yPos + 40;
nightCostLabel.visible = true;
nightCostLabel.alpha = 1;
game.addChild(nightCostLabel);
nightPlantCosts.push(nightCostLabel);
// Skip to next iteration since we handled the night plant
continue;
}
// Position and show the selected plant button
if (button && costLabel) {
button.x = xPos;
button.y = yPos;
button.visible = true;
button.alpha = 1;
costLabel.x = xPos;
costLabel.y = yPos + 40;
costLabel.visible = true;
costLabel.alpha = 1;
}
}
}
function startLevel(levelNumber) {
currentLevel = levelNumber;
// Show plant selector for all levels
gameState = 'plantSelector';
// Clear level selection elements
for (var i = levelButtons.length - 1; i >= 0; i--) {
levelButtons[i].destroy();
}
levelButtons = [];
// Hide game elements
hideGameElements();
// Show plant selector
var plantSelector = new PlantSelector();
game.addChild(plantSelector);
}
function resetGameForLevel(levelNumber) {
// Clean up any existing night plant buttons first
cleanupNightPlantButtons();
// Reset all game variables
sunPoints = 150;
currentWave = 1;
zombiesKilled = 0; // Reset kill counter
// Special case for level 5: set to 3 waves with reasonable zombie count
if (levelNumber === 5) {
maxZombiesInWave = 30; // Manageable number of zombies per wave
maxWaves = 3; // Exactly 3 waves
} else {
maxZombiesInWave = 1000; // Set to 1000 zombies per wave
maxWaves = 3 + levelNumber; // More waves for higher levels
}
waveSpawnTimer = 0;
zombiesInWave = 0;
selectedPlantType = null;
shovelSelected = false;
// Set mode based on level number
var isNightMode = levelNumber > 5 && levelNumber <= 10 || levelNumber === 19;
var isPoolMode = levelNumber > 10 && levelNumber <= 15;
var isRoofMode = levelNumber > 15;
if (isRoofMode && levelNumber === 19) {
// Night roof boss fight - defense planning mode with 10,000 suns
game.setBackgroundColor(0x0F0F23); // Very dark night background
sunPoints = 10000; // Start with 10,000 suns for defense planning
hideDayPlants();
// Show only specific plants for roof levels (no sun mushrooms)
peashooterButton.visible = true;
wallnutButton.visible = true;
carnivorousplantButton.visible = true;
doomshroomButton.visible = true;
peashooterCost.visible = true;
wallnutCost.visible = true;
carnivorousplantCost.visible = true;
doomshroomCost.visible = true;
// Show catapult plants for night roof boss level
melonpultaButton.visible = true;
coltapultaButton.visible = true;
lanzamaizButton.visible = true;
melonpultaCost.visible = true;
coltapultaCost.visible = true;
lanzamaizCost.visible = true;
// Show pot plant for roof levels
potButton.visible = true;
potCost.visible = true;
// Show boss health display
bossHealthDisplay.visible = true;
maxZombiesInWave = 50 + (levelNumber - 1) * 15; // Much harder boss fight
maxWaves = 5 + levelNumber; // Many waves for boss
} else if (isRoofMode) {
// Day roof levels - only allow sunflower, doomshroom, wallnut, carnivorousplant, melonpulta, coltapulta, lanzamaiz
game.setBackgroundColor(0x696969); // Gray roof background
sunPoints = 125; // Medium-low sun for roof levels
hideDayPlants();
// Show only specific plants for roof levels
sunflowerButton.visible = true;
wallnutButton.visible = true;
carnivorousplantButton.visible = true;
doomshroomButton.visible = true;
sunflowerCost.visible = true;
wallnutCost.visible = true;
carnivorousplantCost.visible = true;
doomshroomCost.visible = true;
// Show catapult plants for day roof levels
melonpultaButton.visible = true;
coltapultaButton.visible = true;
lanzamaizButton.visible = true;
melonpultaCost.visible = true;
coltapultaCost.visible = true;
lanzamaizCost.visible = true;
// Show pot plant for roof levels
potButton.visible = true;
potCost.visible = true;
} else if (isPoolMode) {
game.setBackgroundColor(0x006994); // Blue pool background
sunPoints = 100; // Medium sun for pool levels
showDayPlants(); // Pool levels use day plants
} else if (isNightMode) {
game.setBackgroundColor(0x1a1a2e); // Dark blue night background
sunPoints = 75; // Start with less sun at night
hideDayPlants(); // Hide day plants
createNightPlantButtons(); // Show night plants
} else {
game.setBackgroundColor(0x228B22); // Green day background
showDayPlants(); // Show day plants
// Hide boss health display for non-boss levels
bossHealthDisplay.visible = false;
}
// Clear all arrays and destroy objects
for (var i = plants.length - 1; i >= 0; i--) {
plants[i].destroy();
}
plants = [];
for (var j = zombies.length - 1; j >= 0; j--) {
zombies[j].destroy();
}
zombies = [];
for (var k = peas.length - 1; k >= 0; k--) {
peas[k].destroy();
}
peas = [];
for (var c = corns.length - 1; c >= 0; c--) {
corns[c].destroy();
}
corns = [];
for (var l = suns.length - 1; l >= 0; l--) {
suns[l].destroy();
}
suns = [];
for (var m = lawnmowers.length - 1; m >= 0; m--) {
lawnmowers[m].destroy();
}
lawnmowers = [];
for (var t = tombstones.length - 1; t >= 0; t--) {
tombstones[t].destroy();
}
tombstones = [];
// Clear boss-specific objects
if (conveyorBelt) {
conveyorBelt.destroy();
conveyorBelt = null;
}
if (robotBoss) {
robotBoss.destroy();
robotBoss = null;
}
for (var f = fireballs.length - 1; f >= 0; f--) {
fireballs[f].destroy();
}
fireballs = [];
// Create boss-specific objects for level 19
if (isRoofMode && levelNumber === 19) {
// Create conveyor belt for boss level
conveyorBelt = new ConveyorBelt();
conveyorBelt.x = 1024; // Center of screen
conveyorBelt.y = 200; // Top area
game.addChild(conveyorBelt);
// Create robot boss
robotBoss = new RobotBoss();
robotBoss.x = 1800; // Right side of screen
robotBoss.y = 1000; // Middle vertically
game.addChild(robotBoss);
}
// Spawn tombstones for night levels after clearing existing ones (but not for boss fight)
if (isNightMode && levelNumber !== 19) {
spawnTombstones();
}
// Reset seed cooldowns
for (var seedType in seedCooldowns) {
seedCooldowns[seedType] = 0;
}
// Reinitialize lawnmowers
for (var row = 0; row < gridRows; row++) {
var lawnmower = new Lawnmower();
lawnmower.x = 220;
lawnmower.y = gridStartY + row * cellSize;
game.addChild(lawnmower);
lawnmowers.push(lawnmower);
}
// Add water tiles for pool levels (11-15)
if (isPoolMode) {
// Add water in the middle rows (approximately rows 4-8) for all columns
for (var waterRow = 4; waterRow <= 8; waterRow++) {
for (var waterCol = 0; waterCol < gridCols; waterCol++) {
var waterTile = LK.getAsset('water', {
anchorX: 0.5,
anchorY: 0.5,
x: gridStartX + waterCol * cellSize,
y: gridStartY + waterRow * cellSize,
alpha: 0.7
});
game.addChild(waterTile);
waterTiles.push(waterTile);
}
}
}
// Update grid tiles for all levels
updateGridTiles(levelNumber);
// Update bushes visibility based on level type
if (isRoofMode) {
// Hide bushes for roof levels
bushes.visible = false;
bushes2.visible = false;
bushes3.visible = false;
bushes4.visible = false;
bushes5.visible = false;
bushes6.visible = false;
bushes7.visible = false;
bushes8.visible = false;
bushes9.visible = false;
bushes10.visible = false;
bushes11.visible = false;
bushes12.visible = false;
bushes13.visible = false;
} else {
// Show bushes for non-roof levels
bushes.visible = true;
bushes2.visible = true;
bushes3.visible = true;
bushes4.visible = true;
bushes5.visible = true;
bushes6.visible = true;
bushes7.visible = true;
bushes8.visible = true;
bushes9.visible = true;
bushes10.visible = true;
bushes11.visible = true;
bushes12.visible = true;
bushes13.visible = true;
}
// Add 3 vertical lines of pots for roof levels
if (isRoofMode) {
// Create 3 vertical lines of pots at the first 3 columns from the left (0, 1, 2)
var potColumns = [0, 1, 2];
for (var potColIndex = 0; potColIndex < potColumns.length; potColIndex++) {
var potCol = potColumns[potColIndex];
for (var potRow = 0; potRow < gridRows; potRow++) {
var potPlant = new Plant('pot');
potPlant.x = gridStartX + potCol * cellSize;
potPlant.y = gridStartY + potRow * cellSize;
game.addChild(potPlant);
plants.push(potPlant);
}
}
}
// Update displays
updateSunDisplay();
waveDisplay.setText('Wave: ' + currentWave + '/' + maxWaves);
}
// Button event handlers
sunflowerButton.down = function (x, y, obj) {
if (canPlant('sunflower')) {
selectedPlantType = 'sunflower';
shovelSelected = false;
// Button press animation
tween(sunflowerButton, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(sunflowerButton, {
scaleX: 0.6,
scaleY: 0.6
}, {
duration: 100,
easing: tween.bounceOut
});
}
});
sunflowerButton.alpha = 1;
sunflowerButton.tint = 0x808080;
peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5;
peashooterButton.tint = 0xffffff;
wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5;
wallnutButton.tint = 0xffffff;
potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5;
potatomineButton.tint = 0xffffff;
carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5;
carnivorousplantButton.tint = 0xffffff;
doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5;
doomshroomButton.tint = 0xffffff;
repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5;
repeaterButton.tint = 0xffffff;
threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5;
threepeaterButton.tint = 0xffffff;
spikeButton.alpha = isSeedReady('spikeweed') ? 1 : 0.5;
spikeButton.tint = 0xffffff;
splitpeaterButton.alpha = isSeedReady('splitpeater') ? 1 : 0.5;
splitpeaterButton.tint = 0xffffff;
shovelButton.alpha = 1;
shovelButton.tint = 0xffffff;
}
};
peashooterButton.down = function (x, y, obj) {
if (canPlant('peashooter')) {
selectedPlantType = 'peashooter';
shovelSelected = false;
peashooterButton.alpha = 1;
peashooterButton.tint = 0x808080;
sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5;
sunflowerButton.tint = 0xffffff;
wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5;
wallnutButton.tint = 0xffffff;
potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5;
potatomineButton.tint = 0xffffff;
carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5;
carnivorousplantButton.tint = 0xffffff;
doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5;
doomshroomButton.tint = 0xffffff;
repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5;
repeaterButton.tint = 0xffffff;
threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5;
threepeaterButton.tint = 0xffffff;
shovelButton.alpha = 1;
shovelButton.tint = 0xffffff;
}
};
wallnutButton.down = function (x, y, obj) {
if (canPlant('wallnut')) {
selectedPlantType = 'wallnut';
shovelSelected = false;
wallnutButton.alpha = 1;
wallnutButton.tint = 0x808080;
sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5;
sunflowerButton.tint = 0xffffff;
peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5;
peashooterButton.tint = 0xffffff;
potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5;
potatomineButton.tint = 0xffffff;
carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5;
carnivorousplantButton.tint = 0xffffff;
doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5;
doomshroomButton.tint = 0xffffff;
repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5;
repeaterButton.tint = 0xffffff;
threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5;
threepeaterButton.tint = 0xffffff;
shovelButton.alpha = 1;
shovelButton.tint = 0xffffff;
}
};
potatomineButton.down = function (x, y, obj) {
if (canPlant('potatomine')) {
selectedPlantType = 'potatomine';
shovelSelected = false;
potatomineButton.alpha = 1;
potatomineButton.tint = 0x808080;
sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5;
sunflowerButton.tint = 0xffffff;
peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5;
peashooterButton.tint = 0xffffff;
wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5;
wallnutButton.tint = 0xffffff;
carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5;
carnivorousplantButton.tint = 0xffffff;
doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5;
doomshroomButton.tint = 0xffffff;
repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5;
repeaterButton.tint = 0xffffff;
threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5;
threepeaterButton.tint = 0xffffff;
shovelButton.alpha = 1;
shovelButton.tint = 0xffffff;
}
};
carnivorousplantButton.down = function (x, y, obj) {
if (canPlant('carnivorousplant')) {
selectedPlantType = 'carnivorousplant';
shovelSelected = false;
carnivorousplantButton.alpha = 1;
carnivorousplantButton.tint = 0x808080;
sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5;
sunflowerButton.tint = 0xffffff;
peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5;
peashooterButton.tint = 0xffffff;
wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5;
wallnutButton.tint = 0xffffff;
potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5;
potatomineButton.tint = 0xffffff;
doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5;
doomshroomButton.tint = 0xffffff;
repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5;
repeaterButton.tint = 0xffffff;
threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5;
threepeaterButton.tint = 0xffffff;
shovelButton.alpha = 1;
shovelButton.tint = 0xffffff;
}
};
doomshroomButton.down = function (x, y, obj) {
if (canPlant('doomshroom')) {
selectedPlantType = 'doomshroom';
shovelSelected = false;
doomshroomButton.alpha = 1;
doomshroomButton.tint = 0x808080;
sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5;
sunflowerButton.tint = 0xffffff;
peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5;
peashooterButton.tint = 0xffffff;
wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5;
wallnutButton.tint = 0xffffff;
potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5;
potatomineButton.tint = 0xffffff;
carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5;
carnivorousplantButton.tint = 0xffffff;
repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5;
repeaterButton.tint = 0xffffff;
threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5;
threepeaterButton.tint = 0xffffff;
shovelButton.alpha = 1;
shovelButton.tint = 0xffffff;
}
};
repeaterButton.down = function (x, y, obj) {
if (canPlant('repeater')) {
selectedPlantType = 'repeater';
shovelSelected = false;
repeaterButton.alpha = 1;
repeaterButton.tint = 0x808080;
sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5;
sunflowerButton.tint = 0xffffff;
peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5;
peashooterButton.tint = 0xffffff;
wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5;
wallnutButton.tint = 0xffffff;
potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5;
potatomineButton.tint = 0xffffff;
carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5;
carnivorousplantButton.tint = 0xffffff;
doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5;
doomshroomButton.tint = 0xffffff;
threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5;
threepeaterButton.tint = 0xffffff;
shovelButton.alpha = 1;
shovelButton.tint = 0xffffff;
}
};
threepeaterButton.down = function (x, y, obj) {
if (canPlant('threepeater')) {
selectedPlantType = 'threepeater';
shovelSelected = false;
threepeaterButton.alpha = 1;
threepeaterButton.tint = 0x808080;
sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5;
sunflowerButton.tint = 0xffffff;
peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5;
peashooterButton.tint = 0xffffff;
wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5;
wallnutButton.tint = 0xffffff;
potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5;
potatomineButton.tint = 0xffffff;
carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5;
carnivorousplantButton.tint = 0xffffff;
doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5;
doomshroomButton.tint = 0xffffff;
repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5;
repeaterButton.tint = 0xffffff;
shovelButton.alpha = 1;
shovelButton.tint = 0xffffff;
}
};
spikeButton.down = function (x, y, obj) {
if (canPlant('spikeweed')) {
selectedPlantType = 'spikeweed';
shovelSelected = false;
spikeButton.alpha = 1;
spikeButton.tint = 0x808080;
sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5;
sunflowerButton.tint = 0xffffff;
peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5;
peashooterButton.tint = 0xffffff;
wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5;
wallnutButton.tint = 0xffffff;
potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5;
potatomineButton.tint = 0xffffff;
carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5;
carnivorousplantButton.tint = 0xffffff;
doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5;
doomshroomButton.tint = 0xffffff;
repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5;
repeaterButton.tint = 0xffffff;
threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5;
threepeaterButton.tint = 0xffffff;
splitpeaterButton.alpha = isSeedReady('splitpeater') ? 1 : 0.5;
splitpeaterButton.tint = 0xffffff;
shovelButton.alpha = 1;
shovelButton.tint = 0xffffff;
}
};
splitpeaterButton.down = function (x, y, obj) {
if (canPlant('splitpeater')) {
selectedPlantType = 'splitpeater';
shovelSelected = false;
splitpeaterButton.alpha = 1;
splitpeaterButton.tint = 0x808080;
sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5;
sunflowerButton.tint = 0xffffff;
peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5;
peashooterButton.tint = 0xffffff;
wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5;
wallnutButton.tint = 0xffffff;
potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5;
potatomineButton.tint = 0xffffff;
carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5;
carnivorousplantButton.tint = 0xffffff;
doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5;
doomshroomButton.tint = 0xffffff;
repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5;
repeaterButton.tint = 0xffffff;
threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5;
threepeaterButton.tint = 0xffffff;
spikeButton.alpha = isSeedReady('spikeweed') ? 1 : 0.5;
spikeButton.tint = 0xffffff;
shovelButton.alpha = 1;
shovelButton.tint = 0xffffff;
}
};
restartButton.down = function (x, y, obj) {
// Reset all game variables
sunPoints = 150;
currentWave = 1;
maxZombiesInWave = 30;
waveSpawnTimer = 0;
zombiesInWave = 0;
selectedPlantType = null;
shovelSelected = false;
gameState = 'playing';
// Start background music
LK.playMusic('backgroundMusic', {
loop: true
});
// Clear all arrays and destroy objects
for (var i = plants.length - 1; i >= 0; i--) {
plants[i].destroy();
}
plants = [];
for (var j = zombies.length - 1; j >= 0; j--) {
zombies[j].destroy();
}
zombies = [];
for (var k = peas.length - 1; k >= 0; k--) {
peas[k].destroy();
}
peas = [];
for (var c = corns.length - 1; c >= 0; c--) {
corns[c].destroy();
}
corns = [];
for (var l = suns.length - 1; l >= 0; l--) {
suns[l].destroy();
}
suns = [];
for (var m = lawnmowers.length - 1; m >= 0; m--) {
lawnmowers[m].destroy();
}
lawnmowers = [];
for (var n = waterTiles.length - 1; n >= 0; n--) {
waterTiles[n].destroy();
}
waterTiles = [];
for (var t = tombstones.length - 1; t >= 0; t--) {
tombstones[t].destroy();
}
tombstones = [];
// Reset seed cooldowns
for (var seedType in seedCooldowns) {
seedCooldowns[seedType] = 0;
}
// Reinitialize lawnmowers
for (var row = 0; row < gridRows; row++) {
var lawnmower = new Lawnmower();
lawnmower.x = 220;
lawnmower.y = gridStartY + row * cellSize;
game.addChild(lawnmower);
lawnmowers.push(lawnmower);
}
// Update displays
updateSunDisplay();
waveDisplay.setText('Wave: ' + currentWave + '/' + maxWaves);
// Reset button states
sunflowerButton.alpha = 1;
sunflowerButton.tint = 0xffffff;
peashooterButton.alpha = 1;
peashooterButton.tint = 0xffffff;
wallnutButton.alpha = 1;
wallnutButton.tint = 0xffffff;
potatomineButton.alpha = 1;
potatomineButton.tint = 0xffffff;
carnivorousplantButton.alpha = 1;
carnivorousplantButton.tint = 0xffffff;
doomshroomButton.alpha = 1;
doomshroomButton.tint = 0xffffff;
repeaterButton.alpha = 1;
repeaterButton.tint = 0xffffff;
threepeaterButton.alpha = 1;
threepeaterButton.tint = 0xffffff;
spikeButton.alpha = 1;
spikeButton.tint = 0xffffff;
splitpeaterButton.alpha = 1;
splitpeaterButton.tint = 0xffffff;
shovelButton.alpha = 1;
shovelButton.tint = 0xffffff;
};
lilypadButton.down = function (x, y, obj) {
if (canPlant('lilypad')) {
selectedPlantType = 'lilypad';
shovelSelected = false;
lilypadButton.alpha = 1;
lilypadButton.tint = 0x808080;
sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5;
sunflowerButton.tint = 0xffffff;
peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5;
peashooterButton.tint = 0xffffff;
wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5;
wallnutButton.tint = 0xffffff;
potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5;
potatomineButton.tint = 0xffffff;
carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5;
carnivorousplantButton.tint = 0xffffff;
doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5;
doomshroomButton.tint = 0xffffff;
repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5;
repeaterButton.tint = 0xffffff;
threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5;
threepeaterButton.tint = 0xffffff;
spikeButton.alpha = isSeedReady('spikeweed') ? 1 : 0.5;
spikeButton.tint = 0xffffff;
splitpeaterButton.alpha = isSeedReady('splitpeater') ? 1 : 0.5;
splitpeaterButton.tint = 0xffffff;
shovelButton.alpha = 1;
shovelButton.tint = 0xffffff;
}
};
shovelButton.down = function (x, y, obj) {
shovelSelected = true;
selectedPlantType = null;
shovelButton.alpha = 1;
shovelButton.tint = 0x808080;
sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5;
sunflowerButton.tint = 0xffffff;
peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5;
peashooterButton.tint = 0xffffff;
wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5;
wallnutButton.tint = 0xffffff;
potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5;
potatomineButton.tint = 0xffffff;
carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5;
carnivorousplantButton.tint = 0xffffff;
doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5;
doomshroomButton.tint = 0xffffff;
repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5;
repeaterButton.tint = 0xffffff;
threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5;
threepeaterButton.tint = 0xffffff;
spikeButton.alpha = 1;
spikeButton.tint = 0xffffff;
lilypadButton.alpha = 1;
lilypadButton.tint = 0xffffff;
};
// Add event handlers for catapult plant buttons
melonpultaButton.down = function (x, y, obj) {
if (canPlant('melonpulta')) {
selectedPlantType = 'melonpulta';
shovelSelected = false;
melonpultaButton.alpha = 1;
melonpultaButton.tint = 0x808080;
// Reset other buttons
sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5;
sunflowerButton.tint = 0xffffff;
wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5;
wallnutButton.tint = 0xffffff;
carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5;
carnivorousplantButton.tint = 0xffffff;
doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5;
doomshroomButton.tint = 0xffffff;
coltapultaButton.alpha = isSeedReady('coltapulta') ? 1 : 0.5;
coltapultaButton.tint = 0xffffff;
lanzamaizButton.alpha = isSeedReady('lanzamaiz') ? 1 : 0.5;
lanzamaizButton.tint = 0xffffff;
shovelButton.alpha = 1;
shovelButton.tint = 0xffffff;
}
};
coltapultaButton.down = function (x, y, obj) {
if (canPlant('coltapulta')) {
selectedPlantType = 'coltapulta';
shovelSelected = false;
coltapultaButton.alpha = 1;
coltapultaButton.tint = 0x808080;
// Reset other buttons
sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5;
sunflowerButton.tint = 0xffffff;
wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5;
wallnutButton.tint = 0xffffff;
carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5;
carnivorousplantButton.tint = 0xffffff;
doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5;
doomshroomButton.tint = 0xffffff;
melonpultaButton.alpha = isSeedReady('melonpulta') ? 1 : 0.5;
melonpultaButton.tint = 0xffffff;
lanzamaizButton.alpha = isSeedReady('lanzamaiz') ? 1 : 0.5;
lanzamaizButton.tint = 0xffffff;
shovelButton.alpha = 1;
shovelButton.tint = 0xffffff;
}
};
lanzamaizButton.down = function (x, y, obj) {
if (canPlant('lanzamaiz')) {
selectedPlantType = 'lanzamaiz';
shovelSelected = false;
lanzamaizButton.alpha = 1;
lanzamaizButton.tint = 0x808080;
// Reset other buttons
sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5;
sunflowerButton.tint = 0xffffff;
wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5;
wallnutButton.tint = 0xffffff;
carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5;
carnivorousplantButton.tint = 0xffffff;
doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5;
doomshroomButton.tint = 0xffffff;
melonpultaButton.alpha = isSeedReady('melonpulta') ? 1 : 0.5;
melonpultaButton.tint = 0xffffff;
coltapultaButton.alpha = isSeedReady('coltapulta') ? 1 : 0.5;
coltapultaButton.tint = 0xffffff;
shovelButton.alpha = 1;
shovelButton.tint = 0xffffff;
}
};
// Add event handler for pot plant button
potButton.down = function (x, y, obj) {
if (canPlant('pot')) {
selectedPlantType = 'pot';
shovelSelected = false;
potButton.alpha = 1;
potButton.tint = 0x808080;
// Reset other buttons
sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5;
sunflowerButton.tint = 0xffffff;
wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5;
wallnutButton.tint = 0xffffff;
carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5;
carnivorousplantButton.tint = 0xffffff;
doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5;
doomshroomButton.tint = 0xffffff;
melonpultaButton.alpha = isSeedReady('melonpulta') ? 1 : 0.5;
melonpultaButton.tint = 0xffffff;
coltapultaButton.alpha = isSeedReady('coltapulta') ? 1 : 0.5;
coltapultaButton.tint = 0xffffff;
lanzamaizButton.alpha = isSeedReady('lanzamaiz') ? 1 : 0.5;
lanzamaizButton.tint = 0xffffff;
shovelButton.alpha = 1;
shovelButton.tint = 0xffffff;
}
};
// Create tombstone button for minigames - positioned next to exit button
var tombstoneButton = LK.getAsset('tombstone', {
anchorX: 0.5,
anchorY: 0.5,
x: 1500,
y: 120,
scaleX: 0.6,
scaleY: 0.6
});
game.addChild(tombstoneButton);
var tombstoneText = new Text2('MINIGAMES', {
size: 16,
fill: 0xFFFFFF
});
tombstoneText.anchor.set(0.5, 0);
tombstoneText.x = 1500;
tombstoneText.y = 90;
game.addChild(tombstoneText);
// Tombstone button event handler - shows minigame selector
tombstoneButton.down = function (x, y, obj) {
gameState = 'minigame';
// Hide game elements
hideGameElements();
// Show minigame selector
minigameSelector = new MinigameSelector();
game.addChild(minigameSelector);
};
exitLevelButton.down = function (x, y, obj) {
// Clear all game objects and return to level selection
// Clear plants first to ensure they visually disappear
for (var i = plants.length - 1; i >= 0; i--) {
plants[i].destroy();
}
plants = [];
for (var j = zombies.length - 1; j >= 0; j--) {
zombies[j].destroy();
}
zombies = [];
for (var k = peas.length - 1; k >= 0; k--) {
peas[k].destroy();
}
peas = [];
for (var c = corns.length - 1; c >= 0; c--) {
corns[c].destroy();
}
corns = [];
for (var l = suns.length - 1; l >= 0; l--) {
suns[l].destroy();
}
suns = [];
for (var m = lawnmowers.length - 1; m >= 0; m--) {
lawnmowers[m].destroy();
}
lawnmowers = [];
for (var n = waterTiles.length - 1; n >= 0; n--) {
waterTiles[n].destroy();
}
waterTiles = [];
for (var t = tombstones.length - 1; t >= 0; t--) {
tombstones[t].destroy();
}
tombstones = [];
// Clear seed cooldowns for night mode
for (var seedType in seedCooldowns) {
seedCooldowns[seedType] = 0;
}
// Stop background music
LK.stopMusic();
// Return to level selection
showLevelSelection();
};
// Game event handlers
game.down = function (x, y, obj) {
// Special handling for bowling minigame
if (gameState === 'minigame' && currentMinigame && currentMinigame.walnutsAvailable > 0) {
// Check if click position is on a grass tile
var gridPos = getGridPosition(x, y);
if (!gridPos) {
return; // Not on grid, can't place walnut
}
// Place walnut that will roll toward zombies
var walnut = LK.getAsset('wallnut', {
anchorX: 0.5,
anchorY: 0.5,
x: gridPos.x,
// Use grid position instead of click position
y: gridPos.y,
// Use grid position instead of click position
scaleX: 1.0,
scaleY: 1.0
});
game.addChild(walnut);
// Set walnut physics for rolling
walnut.speedX = 6; // Move right toward zombies
walnut.speedY = 0;
walnut.damage = 50;
walnut.destroyed = false;
walnut.bouncedUp = false; // Track if walnut has bounced upward
// Decrease available walnuts
currentMinigame.walnutsAvailable--;
// Update walnut physics and collision
walnut.update = function () {
if (walnut.destroyed) return;
walnut.x += walnut.speedX;
walnut.y += walnut.speedY;
walnut.rotation += 0.2; // Rolling animation
// Check collision with zombies
for (var i = currentMinigame.zombies.length - 1; i >= 0; i--) {
var zombie = currentMinigame.zombies[i];
if (Math.abs(walnut.x - zombie.x) < 60 && Math.abs(walnut.y - zombie.y) < 60) {
// Hit zombie
if (!zombie.destroyed) {
zombie.destroyed = true;
zombiesKilled++; // Increment kill counter
currentMinigame.score++;
// Update score display
var scoreText = currentMinigame.children.find(function (child) {
return child.text && child.text.indexOf('Zombies eliminados:') >= 0;
});
if (scoreText) {
scoreText.setText('Zombies eliminados: ' + currentMinigame.score + '/1000');
}
LK.getSound('explode').play();
// Zombie death animation
tween(zombie, {
scaleX: 0,
scaleY: 0,
rotation: Math.PI,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
zombie.destroy();
}
});
// Remove zombie from array
currentMinigame.zombies.splice(i, 1);
// Reset bounced up state when killing a zombie - allows new bounce direction
walnut.bouncedUp = false;
// Bounce walnut to the right and up/down
walnut.speedX = Math.abs(walnut.speedX) * 0.8; // Always bounce right, reduce speed slightly
// Random bounce direction: either up or down
if (Math.random() < 0.5) {
walnut.speedY = -Math.abs(walnut.speedY || 2) * 1.2; // Bounce up
walnut.bouncedUp = true; // Mark that walnut bounced upward
} else {
walnut.speedY = Math.abs(walnut.speedY || 2) * 1.2; // Bounce down
walnut.bouncedUp = false; // Mark that walnut bounced downward
}
// Visual bounce effect
tween(walnut, {
scaleX: 1.3,
scaleY: 0.7
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(walnut, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeIn
});
}
});
break;
}
}
}
// Add gravity effect to Y movement
if (walnut.speedY !== 0) {
walnut.speedY += 0.1; // Gravity pulls down
}
// Bounce off left boundary only
if (walnut.x < 300) {
walnut.speedX = Math.abs(walnut.speedX); // Bounce right
}
// Disappear when hitting bushes (right side around x=1900-2000)
if (walnut.x > 1900) {
walnut.destroy();
return;
}
if (walnut.y < gridStartY) {
walnut.speedY = Math.abs(walnut.speedY); // Bounce down
}
if (walnut.y > gridStartY + gridRows * cellSize) {
// Only allow upward bounce if walnut hasn't already bounced up, or allow if it bounced up before
if (!walnut.bouncedUp) {
walnut.speedY = -Math.abs(walnut.speedY * 0.7); // Bounce up with energy loss
walnut.bouncedUp = true; // Mark that it bounced upward
} else {
// If already bounced up, don't bounce down - let it fall off screen
walnut.speedY = Math.abs(walnut.speedY); // Continue downward
}
}
// Remove walnut if it moves too slowly (comes to rest)
if (Math.abs(walnut.speedX) < 0.5 && Math.abs(walnut.speedY) < 0.5) {
walnut.destroy();
}
};
return;
}
if (shovelSelected) {
// Remove plant functionality
var gridPos = getGridPosition(x, y);
if (gridPos) {
for (var i = plants.length - 1; i >= 0; i--) {
var plant = plants[i];
var plantRow = Math.floor((plant.y - gridStartY + cellSize / 2) / cellSize);
var plantCol = Math.floor((plant.x - gridStartX + cellSize / 2) / cellSize);
if (plantRow === gridPos.row && plantCol === gridPos.col) {
plant.destroy();
plants.splice(i, 1);
shovelSelected = false;
// Reset button alphas and tints
sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5;
sunflowerButton.tint = 0xffffff;
peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5;
peashooterButton.tint = 0xffffff;
wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5;
wallnutButton.tint = 0xffffff;
potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5;
potatomineButton.tint = 0xffffff;
carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5;
carnivorousplantButton.tint = 0xffffff;
doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5;
doomshroomButton.tint = 0xffffff;
repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5;
repeaterButton.tint = 0xffffff;
threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5;
threepeaterButton.tint = 0xffffff;
shovelButton.alpha = 1;
shovelButton.tint = 0xffffff;
break;
}
}
}
} else if (selectedPlantType) {
var gridPos = getGridPosition(x, y);
if (gridPos && canPlantOnPosition(selectedPlantType, gridPos.row, gridPos.col)) {
// For conveyor belt seeds (boss level), don't check sun cost or cooldown
var isBossLevel = currentLevel === 19;
var canPlaceFromBelt = isBossLevel && selectedPlantType && seedCooldowns[selectedPlantType] === 0;
var canPlaceNormal = !isBossLevel && canPlant(selectedPlantType);
if (canPlaceFromBelt || canPlaceNormal) {
// Only deduct sun points for non-boss levels
if (!isBossLevel) {
sunPoints -= plantCosts[selectedPlantType];
updateSunDisplay();
// Start seed recharge for normal levels
seedCooldowns[selectedPlantType] = seedRechargeTime;
}
var newPlant = new Plant(selectedPlantType);
newPlant.x = gridPos.x;
newPlant.y = gridPos.y;
// Start small and grow
newPlant.scaleX = 0.1;
newPlant.scaleY = 0.1;
newPlant.alpha = 0.5;
game.addChild(newPlant);
plants.push(newPlant);
// Growing animation
tween(newPlant, {
scaleX: 1.0,
scaleY: 1.0,
alpha: 1.0
}, {
duration: 500,
easing: tween.bounceOut
});
LK.getSound('plant').play();
selectedPlantType = null;
// Reset button alphas and tints based on cooldown status
sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5;
sunflowerButton.tint = 0xffffff;
peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5;
peashooterButton.tint = 0xffffff;
wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5;
wallnutButton.tint = 0xffffff;
potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5;
potatomineButton.tint = 0xffffff;
carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5;
carnivorousplantButton.tint = 0xffffff;
doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5;
doomshroomButton.tint = 0xffffff;
repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5;
repeaterButton.tint = 0xffffff;
threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5;
threepeaterButton.tint = 0xffffff;
shovelButton.alpha = 1;
shovelButton.tint = 0xffffff;
}
}
}
};
// Main game update loop
game.update = function () {
if (gameState === 'loading') {
// Loading screen handles its own updates
return;
} else if (gameState === 'levelSelect') {
// Level selection screen - no game updates needed
return;
} else if (gameState === 'minigame') {
// Minigame handles its own updates - but ensure minigame exists
if (currentMinigame && currentMinigame.update) {
// Let minigame handle its own update
}
return;
} else if (gameState === 'plantSelector') {
// Plant selector handles its own updates
return;
} else if (gameState !== 'playing') {
return;
}
// Wave management
waveSpawnTimer++;
if (currentWave <= maxWaves) {
// Only spawn zombies if not boss level (level 19) - boss will spawn them
if (currentLevel !== 19 && waveSpawnTimer >= 1500 && zombiesInWave < maxZombiesInWave) {
// Spawn 1 zombie every 25 seconds (1500 ticks)
spawnZombie();
zombiesInWave++;
waveSpawnTimer = 0;
} else if (zombiesInWave >= maxZombiesInWave && zombies.length === 0) {
// Wave completed
currentWave++;
zombiesInWave = 0;
maxZombiesInWave += 5; // Increase difficulty more
waveDisplay.setText('Wave: ' + currentWave + '/' + maxWaves);
}
}
// Pea vs Zombie collision
for (var i = peas.length - 1; i >= 0; i--) {
var pea = peas[i];
var peaHit = false;
for (var j = zombies.length - 1; j >= 0; j--) {
var zombie = zombies[j];
if (Math.abs(pea.x - zombie.x) < 50 && Math.abs(pea.y - zombie.y) < 50) {
// Play zombie hit sound
LK.getSound('zombieHit').play();
// Pea impact effect - flash and small explosion
tween(pea, {
scaleX: 2.0,
scaleY: 2.0,
alpha: 0,
tint: 0xffff00
}, {
duration: 150,
easing: tween.easeOut
});
if (zombie.takeDamage(pea.damage)) {
zombiesKilled++; // Increment kill counter
zombie.destroy();
zombies.splice(j, 1);
}
pea.destroy();
peas.splice(i, 1);
peaHit = true;
break;
}
}
if (!peaHit && pea.x > 2200) {
pea.destroy();
peas.splice(i, 1);
}
}
// Corn vs Zombie collision
for (var i = corns.length - 1; i >= 0; i--) {
var corn = corns[i];
var cornHit = false;
for (var j = zombies.length - 1; j >= 0; j--) {
var zombie = zombies[j];
if (Math.abs(corn.x - zombie.x) < 50 && Math.abs(corn.y - zombie.y) < 50) {
// Play zombie hit sound
LK.getSound('zombieHit').play();
// Corn impact effect - splash effect with different color for butter
var impactColor = corn.isButter ? 0xffff99 : 0x90EE90;
tween(corn, {
scaleX: 1.8,
scaleY: 1.8,
alpha: 0,
tint: impactColor,
rotation: Math.PI / 4
}, {
duration: 200,
easing: tween.easeOut
});
if (zombie.takeDamage(corn.damage)) {
zombiesKilled++; // Increment kill counter
zombie.destroy();
zombies.splice(j, 1);
}
// Apply butter effect if this corn is butter
if (corn.isButter) {
zombie.butterSlowTime = 600; // 10 seconds at 60fps
}
corn.destroy();
corns.splice(i, 1);
cornHit = true;
break;
}
}
if (!cornHit && corn.x > 2200) {
corn.destroy();
corns.splice(i, 1);
}
}
// Remove destroyed suns
for (var k = suns.length - 1; k >= 0; k--) {
if (suns[k].collected) {
suns.splice(k, 1);
}
}
// Update seed cooldowns
for (var seedType in seedCooldowns) {
if (seedCooldowns[seedType] > 0) {
seedCooldowns[seedType]--;
}
}
// Lawnmower activation - check if zombies reach the left side
for (var m = zombies.length - 1; m >= 0; m--) {
var zombie = zombies[m];
if (zombie.x < 250) {
var zombieRow = Math.floor((zombie.y - gridStartY + cellSize / 2) / cellSize);
if (zombieRow >= 0 && zombieRow < lawnmowers.length && lawnmowers[zombieRow] && !lawnmowers[zombieRow].activated) {
var currentLawnmower = lawnmowers[zombieRow];
currentLawnmower.activated = true;
// Add visual effect when lawnmower activates
tween(currentLawnmower, {
tint: 0xff0000
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(currentLawnmower, {
tint: 0xffffff
}, {
duration: 200,
easing: tween.easeIn
});
}
});
// Play lawnmower sound effect
LK.getSound('shoot').play();
// Remove zombie that triggered the lawnmower
zombie.destroy();
zombies.splice(m, 1);
}
}
}
// Remove lawnmowers that are off screen
for (var n = lawnmowers.length - 1; n >= 0; n--) {
if (lawnmowers[n].x > 2200) {
lawnmowers.splice(n, 1);
}
}
// Lawnmower vs zombie collision
for (var p = 0; p < lawnmowers.length; p++) {
var lawnmower = lawnmowers[p];
if (lawnmower && lawnmower.activated) {
for (var q = zombies.length - 1; q >= 0; q--) {
var zombie = zombies[q];
// Check if zombie is in the same row and within collision range
var zombieRow = Math.floor((zombie.y - gridStartY + cellSize / 2) / cellSize);
var lawnmowerRow = Math.floor((lawnmower.y - gridStartY + cellSize / 2) / cellSize);
if (zombieRow === lawnmowerRow && Math.abs(lawnmower.x - zombie.x) < 100) {
zombiesKilled++; // Increment kill counter
// Zombie destruction with visual effect
tween(zombie, {
scaleX: 0,
scaleY: 0,
rotation: Math.PI * 2,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
zombie.destroy();
}
});
zombies.splice(q, 1);
LK.getSound('explode').play();
}
}
}
}
// Update button visual states based on cooldowns
sunflowerButton.alpha = isSeedReady('sunflower') ? 1 : 0.5;
peashooterButton.alpha = isSeedReady('peashooter') ? 1 : 0.5;
wallnutButton.alpha = isSeedReady('wallnut') ? 1 : 0.5;
potatomineButton.alpha = isSeedReady('potatomine') ? 1 : 0.5;
carnivorousplantButton.alpha = isSeedReady('carnivorousplant') ? 1 : 0.5;
doomshroomButton.alpha = isSeedReady('doomshroom') ? 1 : 0.5;
repeaterButton.alpha = isSeedReady('repeater') ? 1 : 0.5;
threepeaterButton.alpha = isSeedReady('threepeater') ? 1 : 0.5;
spikeButton.alpha = isSeedReady('spikeweed') ? 1 : 0.5;
// Fireball vs Plant collision (boss level only)
if (currentLevel === 19 && fireballs && fireballs.length > 0) {
for (var fb = fireballs.length - 1; fb >= 0; fb--) {
var fireball = fireballs[fb];
if (fireball && fireball.x !== undefined) {
var fireballHit = false;
for (var pl = plants.length - 1; pl >= 0; pl--) {
var plant = plants[pl];
if (Math.abs(fireball.x - plant.x) < 60 && Math.abs(fireball.y - plant.y) < 60) {
if (plant.takeDamage(fireball.damage)) {
plant.destroy();
plants.splice(pl, 1);
}
fireball.destroy();
fireballs.splice(fb, 1);
fireballHit = true;
break;
}
}
if (!fireballHit && fireball.x < -100) {
fireball.destroy();
fireballs.splice(fb, 1);
}
}
}
// Pea vs Robot Boss collision
for (var peaIdx = peas.length - 1; peaIdx >= 0; peaIdx--) {
var pea = peas[peaIdx];
if (robotBoss && Math.abs(pea.x - robotBoss.x) < 100 && Math.abs(pea.y - robotBoss.y) < 150) {
if (robotBoss.takeDamage(pea.damage)) {
// Boss defeated, handled in RobotBoss class
}
pea.destroy();
peas.splice(peaIdx, 1);
}
}
}
// Check win condition
checkWinCondition();
};
Fullscreen modern App Store landscape banner, 16:9, high definition, for a game titled "Plant Defense: Zombie Wave" and with the description "Strategic tower defense game where players place plants to stop zombie waves from crossing their garden lawn.". No text on banner!
un lanzaguisantes. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
un girasol. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
un sol. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
guisante. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
La imagen muestra a una planta del videojuego "Plants vs. Zombies". Es una "Potato Mine" (Papa Mina), un tipo de planta explosiva. Características de la imagen: Forma: Tiene una forma redondeada y abultada, como la de una patata, que sobresale del suelo. Color: Es de color beige o marrón claro. Textura: Parece tener una superficie algo irregular, sugiriendo la textura de una patata o tierra. Cara: Posee dos ojos grandes y negros, de forma ovalada, y una pequeña boca sonriente con dos dientes frontales visibles. Esto le da una expresión amigable. Elemento superior: De la parte superior de la "patata" emerge un tallo gris delgado que sostiene una esfera roja brillante con un pequeño punto blanco en el centro. Esta esfera se asemeja al detonador de una mina o a una palanca de encendido. Base: Está rodeada por terrones de tierra o rocas pequeñas de color marrón oscuro, dando la impresión de que acaba de brotar del suelo o está parcialmente enterrada.. In-Game asset. 2d. High contrast. No shadows
Forma general: La planta tiene un cuerpo principal redondeado, similar a una cabeza, de donde emerge un tallo. Color: El cuerpo principal es de un vibrante color púrpura, salpicado con algunas manchas más oscuras del mismo tono. Boca: Posee una boca enorme y abierta que domina gran parte de su "cabeza". El borde de la boca es de un color verde lima brillante. Dientes: En el interior de la boca, se pueden ver numerosos dientes grandes, afilados y puntiagudos, de color blanco o crema, que sugieren su naturaleza carnívora. La encía visible dentro de la boca es de un tono rosa rojizo. Cuernos/Espinas: En la parte superior de su cabeza, tiene tres o cuatro protuberancias cónicas y afiladas, de color verde claro o blanquecino, que parecen espinas o cuernos. Tallo y hojas: Un tallo largo y curvado de color púrpura oscuro se extiende desde la parte inferior de la cabeza. Varias hojas verdes, con una forma ondulada o dentada, brotan del tallo, tanto cerca de la cabeza como en la base.. In-Game asset. 2d. High contrast. No shadows
Forma: Es una nuez grande y ovalada, con las dos mitades características de una nuez bien definidas y una hendidura central que las separa. Color: Predomina un color marrón dorado, típico de una nuez madura, con algunos matices más oscuros que sugieren textura y profundidad. Textura: La superficie parece rugosa y con imperfecciones, imitando la cáscara dura de una nuez real. Cara: A pesar de ser una nuez, tiene rasgos faciales humanoides en su parte frontal: Ojos: Dos ojos grandes, redondos y saltones con pupilas pequeñas y negras centradas. Los ojos tienen un borde blanco alrededor. Boca: Una pequeña línea horizontal que representa una boca sencilla y recta, dándole una expresión neutral o ligeramente sonriente, pero con una connotación de firmeza o determinación. Expresión: La combinación de los ojos grandes y la boca simple le otorgan una expresión de sorpresa o de estar alerta, lo que encaja con su función de muro defensivo. Fondo: El fondo es cuadriculado, lo que indica que. In-Game asset. 2d. High contrast. No shadows
un zombie de pvz. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
un zombie de pvz. In-Game asset. 2d. High contrast. No shadows
repetidora de pvz. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
una tripitidora de pvz 2 mirando a la derecha. In-Game asset. 2d. High contrast. No shadows
pala. In-Game asset. 2d. High contrast. No shadows
pincho roca pvz. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Forma general: Es una planta con un tallo central del que brotan hojas y, en la parte superior, tiene dos "cabezas" o vainas de guisante. Color: Predomina un color verde claro brillante en todo el cuerpo de la planta, con algunas sombras que le dan volumen y realismo. Las hojas y el tallo son de un verde ligeramente más oscuro. Cabezas/Vainas: Tiene dos vainas de guisante esféricas unidas, dispuestas una al lado de la otra. Cada una de ellas actúa como un cañón: Orificios de disparo: Ambas vainas tienen una abertura frontal redonda y oscura, que es por donde disparan los guisantes. Ojos: Cada vaina tiene dos pequeños ojos oscuros, similares a cuentas, que le dan una expresión curiosa o atenta. Hojas: En la base de las vainas y a lo largo del tallo, hay varias hojas grandes y ovaladas de color verde oscuro, con nervaduras visibles. Estilo artístico: La imagen tiene un estilo más renderizado y detallado que las ilustraciones típicas del juego, con un sombreado suave que le da una a. In-Game asset. 2d. High contrast. No shadows
lapida estilo PVZ. In-Game asset. 2d. High contrast. No shadows
podadora pvz. In-Game asset. 2d. High contrast. No shadows
El Zomboni es un zombi de tamaño considerable, vestido con un uniforme de trabajador y conduciendo un vehículo grande y robusto. La máquina suele ser de color azul o similar, con un rodillo en la parte trasera que es lo que esparce el hielo. Su aspecto es bastante imponente, señalando que no es un zombi común.. In-Game asset. 2d. High contrast. No shadows
petaseta de pvz en negro y gris con ojos rojos
arbustos grandes. In-Game asset. 2d. High contrast. No shadows
casilla de cesped en imagen completa con flores. In-Game asset. 2d. High contrast. No shadows
valla. In-Game asset. 2d. High contrast. No shadows
zombie minero pvz. In-Game asset. 2d. High contrast. No shadows
zombie leyendo un periodico con traje en cuerpo completo de pvz. In-Game asset. 2d. High contrast. No shadows
Uniforme Completo: Viste un uniforme de fútbol americano bastante completo, lo que le da una apariencia robusta y protegida. Casco de Fútbol: Lleva un casco de fútbol americano de color rojo brillante, adornado con dibujos de calaveras blancas y huesos cruzados, que son un distintivo de los zombis. Este casco no es solo estético, sino que le proporciona una defensa significativa en su cabeza. Hombreras y Camiseta: Tiene unas grandes hombreras blancas y una camiseta roja con el mismo patrón de calaveras y huesos en el pecho. Pantalones y Guantes: Sus pantalones son de color claro (crema o blanco roto) y lleva guantes rojos. Botas de Fútbol: Calza grandes botas de fútbol americano de color gris oscuro o negro. Boca Abierta y Cerebro: Típicamente, como muchos zombis, tiene la boca abierta, mostrando su deseo de cerebros. En tu imagen, parece tener algo verde (quizás una hoja o parte de una planta) sobresaliendo de su boca, lo que podría indicar que ya ha estado masticando algo.. In-Game asset. 2d. High contrast. No shadows
nenufar pvz. In-Game asset. 2d. High contrast. No shadows
Sombrero Púrpura: Tiene un sombrero grande y redondo de color púrpura oscuro o violeta, con manchas o puntos de un púrpura ligeramente más claro. Tallo Verde Claro: Su tallo es corto y robusto, de un color verde claro o menta. Ojos Grandes y Boquilla: Posee dos ojos grandes y redondos, y una "boca" o boquilla prominente en el frente, de color verde oscuro, desde donde expulsa sus proyectiles.. In-Game asset. 2d. High contrast. No shadows
Sombrero Anaranjado/Amarillo: Tiene un sombrero grande y redondo de color anaranjado o amarillo brillante, con manchas de un tono más oscuro o marrón-anaranjado. Los puntos grandes y luminosos en su sombrero le dan un aspecto amigable y sugieren su función solar. Tallo Beige/Crema: Su tallo es corto y regordete, de un color beige o crema claro. Cara Amigable: Posee dos ojos pequeños y redondos, y una pequeña sonrisa, dándole una expresión dulce y pasiva.. In-Game asset. 2d. High contrast. No shadows
Sombrero Iridiscente/Colorido: Su sombrero es la característica más llamativa, mostrando una gama de colores suaves y pastel que se mezclan como un arcoíris o un efecto iridiscente (rosas, azules, verdes claros, morados). Esto sugiere su naturaleza "hipnótica" o alucinógena. Tallo Púrpura Claro/Blanco: Su tallo es de un color púrpura muy claro o casi blanco. Ojos en Espiral/Hipnóticos: Sus "ojos" son dos grandes espirales rojas o rosadas, lo que claramente representa su capacidad de hipnotizar. Boca Triste/Somnolienta: Tiene una pequeña boca que parece un poco triste o somnolienta, lo que contrasta con la intensidad de sus ojos espirales.. In-Game asset. 2d. High contrast. No shadows
Cabeza (sombrero del hongo): Es grande, redonda y de color morado con manchas más oscuras. En el frente tiene una estructura que parece una boquilla o cañón, lo que da la impresión de que puede disparar algo. Cuerpo: Pequeño y de color verde claro, con una expresión facial seria o enojada. Tiene ojos grandes y cejas inclinadas hacia abajo, lo que resalta su actitud agresiva o decidida. Estilo: Es un diseño al estilo de caricatura o videojuego, similar a los personajes de Plants vs. Zombies.. In-Game asset. 2d. High contrast. No shadows
Cuerpo principal: Tiene forma de tronco cortado, de color gris oscuro con textura agrietada, lo que le da un aspecto envejecido o pétreo. La parte inferior tiene bordes irregulares que simulan raíces o madera desgastada. Ojos: Tiene dos ojos brillantes de color amarillo verdoso, con una expresión amenazante o intimidante. No tiene boca visible. Parte superior: Le crecen enredaderas verdes con hojas que sobresalen por arriba, dándole un toque vegetal y natural, como si la criatura estuviera conectada a la naturaleza o corrompida por ella. Estilo general: El diseño es oscuro y misterioso, probablemente representa a un enemigo o criatura poderosa en un juego tipo Plants vs. Zombies.. In-Game asset. 2d. High contrast. No shadows
Cristales de Hielo en la Parte Superior: Su característica más distintiva es la corona de cristales de hielo afilados y translúcidos de color azul brillante que cubren la parte superior de su cuerpo. Estos cristales le dan una apariencia gélida y punzante. Cuerpo Translúcido Azul/Turquesa: Su cuerpo principal es una masa translúcida de color azul claro o turquesa, que se asemeja al hielo o a un cuerpo gelatinoso congelado. Ojos Fruncidos y Fríos: Tiene dos ojos pequeños, oscuros y con las cejas fruncidas, lo que le da una expresión seria y gélida, a juego con su habilidad. Base Escarchada: La base de su cuerpo a menudo tiene un contorno escarchado o irregular, reforzando su temática de hielo.. In-Game asset. 2d. High contrast. No shadows
una lechuga. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
maseta mirando hacia arriba. In-Game asset. 2d. High contrast. No shadows
Cuerpo Robusto y Verde: El Zombot es una mole robótica de gran tamaño, con un cuerpo predominantemente de color verde oscuro, que le da un aspecto industrial y amenazante. Sus "hombros" y parte del torso están cubiertos por una armadura de color óxido o marrón rojizo, con remaches visibles. Cabeza de Zombi Grande: La cabeza del robot es grande y cuadrada, también de color verde, con mandíbulas inferiores prominentes que se abren para revelar dientes afilados. Tiene dos grandes ojos amarillos brillantes que emiten una luz siniestra, lo que lo hace parecer una versión robótica y gigantesca de un zombi común. Múltiples Mangueras y Cables: Varias mangueras o tuberías metálicas segmentadas sobresalen de sus hombros y otras partes del cuerpo, conectándose a sus brazos y cabeza, lo que sugiere un complejo sistema mecánico y de fluido. Brazos y Piernas Robóticas Grandes: Sus brazos son enormes y fornidos, con grandes manos que terminan en "dedos" gruesos y redondeados, capaces de infligir u. In-Game asset. 2d. High contrast. No shadows
Melón como Cuerpo: La planta en sí parece ser un melón grande y redondo, con las características rayas verdes oscuras y claras de una sandía. Cara y Hojas: Tiene dos ojos pequeños y una expresión que denota seriedad o concentración, propia de una planta de ataque. En su base, tiene hojas verdes que la asientan en el suelo. Brazos de Enredadera con Melón: De la parte superior de su cuerpo, salen dos enredaderas o tallos verdes y enrollados que sostienen un melón más pequeño. Este melón es el "proyectil" que lanza. El mecanismo sugiere que es una especie de honda o catapulta natural.. In-Game asset. 2d. High contrast. No shadows
casilla negra. In-Game asset. 2d. High contrast. No shadows
bola de cañon con fuego. In-Game asset. 2d. High contrast. No shadows
mantequilla. In-Game asset. 2d. High contrast. No shadows
grano de mais. In-Game asset. 2d. High contrast. No shadows
Un zombie grande ey fuerte eue sostiene un palo de electricidad. In-Game asset. 2d. High contrast. No shadows
Un botón de continuar al estilo lápida. In-Game asset. 2d. High contrast. No shadows
Tronco enojado con llamas arriba. In-Game asset. 2d. High contrast. No shadows