/**** * Plugins ****/ var storage = LK.import("@upit/storage.v1"); var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var BomberZombie = Container.expand(function () { var self = Container.call(this); var body = self.attachAsset('bomberZombieBody', { anchorX: 0.5, anchorY: 1 }); body.y = 25; var head = self.attachAsset('bomberZombieHead', { anchorX: 0.5, anchorY: 1 }); head.y = -10; // Add explosive belt around waist var belt = self.attachAsset('explosiveBelt', { anchorX: 0.5, anchorY: 0.5 }); belt.x = 0; belt.y = 15; // Add multiple bomb indicators on the belt for (var i = 0; i < 3; i++) { var bombIndicator = self.attachAsset('bombIndicator', { anchorX: 0.5, anchorY: 0.5 }); bombIndicator.x = (i - 1) * 25; // Spread across belt bombIndicator.y = 15; // Add blinking animation to bomb indicators var delay = i * 200; // Stagger the blinking LK.setTimeout(function () { var blinkTimer = LK.setInterval(function () { bombIndicator.alpha = bombIndicator.alpha > 0.5 ? 0.3 : 1.0; }, 800); }, delay); } // Add fuse wire coming from belt var fuse = self.attachAsset('fuseWire', { anchorX: 0.5, anchorY: 1 }); fuse.x = 30; fuse.y = 5; fuse.rotation = Math.PI / 6; // Slight angle // Add sparking effect to fuse tip var fuseTip = self.attachAsset('bombIndicator', { anchorX: 0.5, anchorY: 0.5 }); fuseTip.x = 35; fuseTip.y = -5; fuseTip.scaleX = 0.5; fuseTip.scaleY = 0.5; // Animate fuse tip sparking LK.setInterval(function () { fuseTip.alpha = Math.random() > 0.5 ? 1.0 : 0.2; fuseTip.tint = Math.random() > 0.5 ? 0xFFFF00 : 0xFF4500; }, 300); self.health = 80; // Lower health than regular zombie - easy to kill but dangerous self.speed = 0.4; // Slightly faster than regular zombie self.gridRow = 0; self.isEating = false; self.eatDamage = 8; self.hasAttackedHouse = false; self.hasExploded = false; self.update = function () { // Handle freeze effect if (self.isFrozen && LK.ticks > self.frozenUntil) { self.speed = self.originalSpeed; self.isFrozen = false; // Remove visual freeze effect but keep original orange tint if (self.body) { self.body.tint = 0xFF4500; } if (self.head) { self.head.tint = 0xFF4500; } } if (!self.isEating && !self.hasExploded) { self.x -= self.speed; } // Explode when health drops to 0 or below if (self.health <= 0 && !self.hasExploded) { self.hasExploded = true; // Explosive death - damages plants in 2x2 area around bomber for (var i = plants.length - 1; i >= 0; i--) { var plant = plants[i]; var distance = Math.sqrt(Math.pow(self.x - plant.x, 2) + Math.pow(self.y - plant.y, 2)); if (distance < 150) { // Explosion radius plant.health -= 40; // Significant damage to nearby plants if (plant.health <= 0) { plant.destroy(); plants.splice(i, 1); } } } // Flash explosion effect LK.effects.flashScreen(0xFF4500, 500); // Remove self for (var z = zombies.length - 1; z >= 0; z--) { if (zombies[z] === self) { zombies.splice(z, 1); break; } } self.destroy(); return; } // Check if reached house if (self.x <= 150 && !self.hasAttackedHouse && !self.hasExploded) { houseHealth -= 20; // Extra damage when reaching house healthText.setText('House Health: ' + houseHealth); self.hasAttackedHouse = true; // Explode at house for extra damage self.hasExploded = true; LK.effects.flashScreen(0xFF4500, 500); self.destroy(); for (var z = zombies.length - 1; z >= 0; z--) { if (zombies[z] === self) { zombies.splice(z, 1); break; } } if (houseHealth <= 0) { LK.showGameOver(); } } // Check collision with plants if (!self.hasExploded) { for (var i = plants.length - 1; i >= 0; i--) { var plant = plants[i]; if (plant.gridRow === self.gridRow && Math.abs(self.x - plant.x) < 50) { self.isEating = true; // WallNut plants block zombies completely if (plant instanceof WallNut) { // Initialize damage timer if not set if (self.lastDamageTime === undefined) { self.lastDamageTime = LK.ticks; } // Deal damage every 35 ticks while touching WallNut if (LK.ticks - self.lastDamageTime >= 35) { plant.health -= 8; self.lastDamageTime = LK.ticks; } // Zombie cannot move past WallNut until it's destroyed if (plant.health > 0) { // Push zombie back slightly to prevent overlap self.x = plant.x + 55; } } else { // Initialize damage timer if not set if (self.lastDamageTime === undefined) { self.lastDamageTime = LK.ticks; } // Deal damage every 50 ticks while touching other plants if (LK.ticks - self.lastDamageTime >= 50) { plant.health -= 20; // Bomber zombie destroys plants with explosive damage self.lastDamageTime = LK.ticks; } } if (plant.health <= 0) { plant.destroy(); plants.splice(i, 1); self.isEating = false; self.lastDamageTime = undefined; // Reset damage timer when plant is destroyed } break; } } // Reset damage timer when not eating if (!self.isEating) { self.lastDamageTime = undefined; } } }; return self; }); var Bullet = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('bulletCore', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 5; self.damage = 50; self.row = 0; self.update = function () { self.x += self.speed; // Check collision with zombies for (var i = zombies.length - 1; i >= 0; i--) { var zombie = zombies[i]; if (zombie.gridRow === self.row && Math.abs(self.x - zombie.x) < 30) { // Handle ShieldZombie shield mechanics if (zombie instanceof ShieldZombie && zombie.hasShield) { zombie.shieldHealth -= self.damage; // Shield takes damage instead of zombie } else { zombie.health -= self.damage; } if (zombie.health <= 0) { zombie.destroy(); zombies.splice(i, 1); } self.destroy(); for (var j = bullets.length - 1; j >= 0; j--) { if (bullets[j] === self) { bullets.splice(j, 1); break; } } return; } } // Remove if off screen if (self.x > 2048) { self.destroy(); for (var k = bullets.length - 1; k >= 0; k--) { if (bullets[k] === self) { bullets.splice(k, 1); break; } } } }; return self; }); var CherryBomb = Container.expand(function () { var self = Container.call(this); var body = self.attachAsset('cherryImage', { anchorX: 0.5, anchorY: 0.5 }); self.health = 50; self.gridRow = 0; self.gridCol = 0; self.fuseTime = 180; // 3 seconds at 60fps self.planted = false; self.update = function () { if (!self.planted) { self.planted = true; self.plantedTick = LK.ticks; } // Check if any zombie is touching the cherry bomb var shouldExplode = false; for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var distance = Math.sqrt(Math.pow(self.x - zombie.x, 2) + Math.pow(self.y - zombie.y, 2)); if (distance < 50) { // Touch distance - much smaller than explosion radius shouldExplode = true; break; } } // Explode after fuse time OR when touched by zombie if (shouldExplode || LK.ticks - self.plantedTick > self.fuseTime) { LK.getSound('cherybomb').play(); // Play cherrybomb sound when exploding // Deal damage to all zombies in 3x3 area around bomb for (var i = zombies.length - 1; i >= 0; i--) { var zombie = zombies[i]; var distance = Math.sqrt(Math.pow(self.x - zombie.x, 2) + Math.pow(self.y - zombie.y, 2)); if (distance < 200) { // Check zombie type for damage calculation if (zombie instanceof TankZombie) { // Deal 75% percentage damage to tank zombies var damageAmount = Math.floor(zombie.health * 0.75); zombie.health -= damageAmount; } else { // Instantly kill all other zombie types (regular, fast, shield, jumper) zombie.health = 0; } // Destroy zombie if health drops to or below 0 if (zombie.health <= 0) { zombie.destroy(); zombies.splice(i, 1); } } } // Remove self for (var j = plants.length - 1; j >= 0; j--) { if (plants[j] === self) { plants.splice(j, 1); break; } } self.destroy(); } }; return self; }); var DoublePeashooter = Container.expand(function () { var self = Container.call(this); var body = self.attachAsset('placedDoublePeaBody', { anchorX: 0.5, anchorY: 0.5 }); var cannon1 = self.attachAsset('peashooterCannon', { anchorX: 0, anchorY: 0.3 }); cannon1.x = 25; cannon1.y = -10; var cannon2 = self.attachAsset('peashooterCannon', { anchorX: 0, anchorY: 0.7 }); cannon2.x = 25; cannon2.y = 10; self.health = 100; self.lastShot = 0; self.shootDelay = 100; // Faster than regular peashooter self.gridRow = 0; self.gridCol = 0; self.shoot = function () { // Shoot two bullets var bullet1 = new Bullet(); bullet1.x = self.x + 50; bullet1.y = self.y - 10; bullet1.row = self.gridRow; bullets.push(bullet1); game.addChild(bullet1); var bullet2 = new Bullet(); bullet2.x = self.x + 50; bullet2.y = self.y + 10; bullet2.row = self.gridRow; bullets.push(bullet2); game.addChild(bullet2); LK.getSound('peasshooter').play(); // Play peashooter sound when shooting }; self.update = function () { if (LK.ticks - self.lastShot > self.shootDelay) { var zombieInRow = false; for (var i = 0; i < zombies.length; i++) { if (zombies[i].gridRow === self.gridRow && zombies[i].x > self.x) { zombieInRow = true; break; } } if (zombieInRow) { self.shoot(); self.lastShot = LK.ticks; } } }; return self; }); var FastZombie = Container.expand(function () { var self = Container.call(this); var body = self.attachAsset('zombieBody', { anchorX: 0.5, anchorY: 1 }); body.y = 25; body.tint = 0xFF6666; // Reddish tint to distinguish from regular zombie var head = self.attachAsset('zombieHead', { anchorX: 0.5, anchorY: 1 }); head.y = -10; head.tint = 0xFF6666; // Reddish tint to distinguish from regular zombie self.health = 100; // Lower health than regular zombie self.speed = 0.8; // Faster than regular zombie self.gridRow = 0; self.isEating = false; self.eatDamage = 15; // More damage when eating self.hasAttackedHouse = false; self.update = function () { // Handle freeze effect if (self.isFrozen && LK.ticks > self.frozenUntil) { self.speed = self.originalSpeed; self.isFrozen = false; // Remove visual freeze effect but keep original red tint if (self.body) { self.body.tint = 0xFF6666; } if (self.head) { self.head.tint = 0xFF6666; } } if (!self.isEating) { self.x -= self.speed; } // Check if reached house - zombie damages house once with 15 health if (self.x <= 150 && !self.hasAttackedHouse) { houseHealth -= 15; healthText.setText('House Health: ' + houseHealth); self.hasAttackedHouse = true; // Destroy zombie after attacking house self.destroy(); for (var z = zombies.length - 1; z >= 0; z--) { if (zombies[z] === self) { zombies.splice(z, 1); break; } } // Check game over condition if (houseHealth <= 0) { LK.showGameOver(); } } // Check collision with plants for (var i = plants.length - 1; i >= 0; i--) { var plant = plants[i]; if (plant.gridRow === self.gridRow && Math.abs(self.x - plant.x) < 50) { self.isEating = true; // WallNut plants block zombies completely if (plant instanceof WallNut) { // Initialize damage timer if not set if (self.lastDamageTime === undefined) { self.lastDamageTime = LK.ticks; } // Deal damage every 25 ticks (faster than regular zombie) while touching WallNut if (LK.ticks - self.lastDamageTime >= 25) { plant.health -= 15; // Fast zombie damages wallnut more self.lastDamageTime = LK.ticks; } // Zombie cannot move past WallNut until it's destroyed if (plant.health > 0) { // Push zombie back slightly to prevent overlap self.x = plant.x + 55; } } else { // Initialize damage timer if not set if (self.lastDamageTime === undefined) { self.lastDamageTime = LK.ticks; } // Deal damage every 45 ticks while touching other plants if (LK.ticks - self.lastDamageTime >= 45) { plant.health -= 15; // Fast zombie destroys plants quickly self.lastDamageTime = LK.ticks; } } if (plant.health <= 0) { plant.destroy(); plants.splice(i, 1); self.isEating = false; self.lastDamageTime = undefined; // Reset damage timer when plant is destroyed } break; } } // Reset damage timer when not eating if (!self.isEating) { self.lastDamageTime = undefined; } }; return self; }); var IceBullet = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('iceBulletCore', { anchorX: 0.5, anchorY: 0.5 }); // Add ice crystal effects around the main bullet for (var i = 0; i < 4; i++) { var crystal = self.attachAsset('iceCrystal', { anchorX: 0.5, anchorY: 0.5 }); var angle = i / 4 * Math.PI * 2; crystal.x = Math.cos(angle) * 8; crystal.y = Math.sin(angle) * 8; crystal.rotation = angle; crystal.scaleX = 0.3; crystal.scaleY = 0.4; crystal.alpha = 0.8; // Add sparkle animation to crystals tween(crystal, { alpha: 0.3 }, { duration: 300, easing: tween.easeInOut }); tween(crystal, { alpha: 0.8 }, { duration: 300, easing: tween.easeInOut }); } self.speed = 6; // Slightly faster than regular bullet self.damage = 40; self.row = 0; self.update = function () { self.x += self.speed; // Check collision with zombies for (var i = zombies.length - 1; i >= 0; i--) { var zombie = zombies[i]; if (zombie.gridRow === self.row && Math.abs(self.x - zombie.x) < 30) { // Handle ShieldZombie shield mechanics if (zombie instanceof ShieldZombie && zombie.hasShield) { zombie.shieldHealth -= self.damage; // Shield takes damage instead of zombie } else { zombie.health -= self.damage; } // Apply freeze effect - slow down zombie for 3 seconds if (!zombie.isFrozen) { zombie.originalSpeed = zombie.speed; zombie.speed = zombie.speed * 0.3; // Slow to 30% speed zombie.isFrozen = true; zombie.frozenUntil = LK.ticks + 180; // 3 seconds at 60fps // Visual freeze effect if (zombie.body) { zombie.body.tint = 0x87CEEB; } if (zombie.head) { zombie.head.tint = 0x87CEEB; } } if (zombie.health <= 0) { zombie.destroy(); zombies.splice(i, 1); } self.destroy(); for (var j = bullets.length - 1; j >= 0; j--) { if (bullets[j] === self) { bullets.splice(j, 1); break; } } return; } } // Remove if off screen if (self.x > 2048) { self.destroy(); for (var k = bullets.length - 1; k >= 0; k--) { if (bullets[k] === self) { bullets.splice(k, 1); break; } } } }; return self; }); var IcePeashooter = Container.expand(function () { var self = Container.call(this); var body = self.attachAsset('icePeashooterBody', { anchorX: 0.5, anchorY: 0.5 }); // Add ice crystals around the body for visual effect for (var i = 0; i < 6; i++) { var crystal = self.attachAsset('iceCrystal', { anchorX: 0.5, anchorY: 0.5 }); var angle = i / 6 * Math.PI * 2; crystal.x = Math.cos(angle) * 30; crystal.y = Math.sin(angle) * 30; crystal.rotation = angle; crystal.scaleX = 0.6; crystal.scaleY = 0.8; } var cannon = self.attachAsset('peashooterCannon', { anchorX: 0, anchorY: 0.5 }); cannon.x = 25; cannon.y = 0; cannon.tint = 0x87CEEB; // Light blue tint for ice effect self.health = 100; self.lastShot = 0; self.shootDelay = 120; // Faster than regular peashooter self.gridRow = 0; self.gridCol = 0; self.shoot = function () { var bullet = new IceBullet(); bullet.x = self.x + 50; bullet.y = self.y; bullet.row = self.gridRow; bullets.push(bullet); game.addChild(bullet); LK.getSound('peasshooter').play(); // Play peashooter sound when shooting }; self.update = function () { if (LK.ticks - self.lastShot > self.shootDelay) { // Check if there's a zombie in this row var zombieInRow = false; for (var i = 0; i < zombies.length; i++) { if (zombies[i].gridRow === self.gridRow && zombies[i].x > self.x) { zombieInRow = true; break; } } if (zombieInRow) { self.shoot(); self.lastShot = LK.ticks; } } }; return self; }); var JumperZombie = Container.expand(function () { var self = Container.call(this); var body = self.attachAsset('zombieBody', { anchorX: 0.5, anchorY: 1 }); body.y = 25; body.tint = 0x00FF00; // Green tint for jumper zombie var head = self.attachAsset('zombieHead', { anchorX: 0.5, anchorY: 1 }); head.y = -10; head.tint = 0x00FF00; self.health = 120; self.speed = 0.5; self.gridRow = 0; self.isEating = false; self.eatDamage = 12; self.hasAttackedHouse = false; self.canJump = true; self.update = function () { // Handle freeze effect if (self.isFrozen && LK.ticks > self.frozenUntil) { self.speed = self.originalSpeed; self.isFrozen = false; // Remove visual freeze effect but keep original green tint if (self.body) { self.body.tint = 0x00FF00; } if (self.head) { self.head.tint = 0x00FF00; } } if (!self.isEating) { self.x -= self.speed; } // Jump over first plant encountered if (self.canJump) { for (var i = 0; i < plants.length; i++) { var plant = plants[i]; if (plant.gridRow === self.gridRow && Math.abs(self.x - plant.x) < 80 && self.x > plant.x) { self.x = plant.x - 80; // Jump past the plant self.canJump = false; break; } } } // Check if reached house if (self.x <= 150 && !self.hasAttackedHouse) { houseHealth -= 12; healthText.setText('House Health: ' + houseHealth); self.hasAttackedHouse = true; self.destroy(); for (var z = zombies.length - 1; z >= 0; z--) { if (zombies[z] === self) { zombies.splice(z, 1); break; } } if (houseHealth <= 0) { LK.showGameOver(); } } // Check collision with plants (only if can't jump) if (!self.canJump) { for (var i = plants.length - 1; i >= 0; i--) { var plant = plants[i]; if (plant.gridRow === self.gridRow && Math.abs(self.x - plant.x) < 50) { self.isEating = true; // WallNut plants block zombies completely if (plant instanceof WallNut) { // Initialize damage timer if not set if (self.lastDamageTime === undefined) { self.lastDamageTime = LK.ticks; } // Deal damage every 30 ticks while touching WallNut if (LK.ticks - self.lastDamageTime >= 30) { plant.health -= 12; // Jumper zombie damages wallnut self.lastDamageTime = LK.ticks; } // Zombie cannot move past WallNut until it's destroyed if (plant.health > 0) { // Push zombie back slightly to prevent overlap self.x = plant.x + 55; } } else { // Initialize damage timer if not set if (self.lastDamageTime === undefined) { self.lastDamageTime = LK.ticks; } // Deal damage every 60 ticks while touching other plants if (LK.ticks - self.lastDamageTime >= 60) { plant.health -= 15; // Jumper zombie destroys plants with high damage self.lastDamageTime = LK.ticks; } } if (plant.health <= 0) { plant.destroy(); plants.splice(i, 1); self.isEating = false; self.lastDamageTime = undefined; // Reset damage timer when plant is destroyed } break; } } // Reset damage timer when not eating if (!self.isEating && !self.canJump) { self.lastDamageTime = undefined; } } }; return self; }); // Event handlers var LevelButton = Container.expand(function () { var self = Container.call(this); var buttonBg = self.attachAsset('levelButton', { anchorX: 0.5, anchorY: 0.5 }); self.levelNumber = 1; self.setLevelNumber = function (number) { self.levelNumber = number; self.levelText.setText(number.toString()); }; self.isUnlocked = true; self.stars = 0; self.maxStars = 3; // Create level number text self.levelText = new Text2(self.levelNumber.toString(), { size: 50, fill: 0xFFFFFF }); self.levelText.anchor.set(0.5, 0.5); self.levelText.y = -30; self.addChild(self.levelText); // Create stars display self.starContainers = []; for (var i = 0; i < self.maxStars; i++) { var star = LK.getAsset('starShape', { anchorX: 0.5, anchorY: 0.5 }); star.x = (i - 1) * 50; star.y = 30; star.tint = 0x666666; // Dark by default self.addChild(star); self.starContainers.push(star); } self.setStars = function (starCount) { self.stars = starCount; for (var i = 0; i < self.maxStars; i++) { if (i < starCount) { self.starContainers[i].tint = 0xFFD700; // Golden } else { self.starContainers[i].tint = 0x666666; // Dark } } }; self.setUnlocked = function (unlocked) { self.isUnlocked = unlocked; if (unlocked) { buttonBg.tint = 0xFFFFFF; self.levelText.tint = 0xFFFFFF; } else { buttonBg.tint = 0x666666; self.levelText.tint = 0x666666; } }; self.down = function (x, y, obj) { if (self.isUnlocked) { LK.getSound('buttonClick').play(); // Start the selected level startLevel(self.levelNumber); } else { // Play a different sound or show feedback for locked levels console.log('Level ' + self.levelNumber + ' is locked. Complete previous levels first.'); } }; return self; }); // Plant classes var Peashooter = Container.expand(function () { var self = Container.call(this); var body = self.attachAsset('peashooterBody', { anchorX: 0.5, anchorY: 0.5 }); var cannon = self.attachAsset('peashooterCannon', { anchorX: 0, anchorY: 0.5 }); cannon.x = 25; cannon.y = 0; self.health = 100; self.lastShot = 0; self.shootDelay = 150; // 2.5 seconds at 60fps self.gridRow = 0; self.gridCol = 0; self.shoot = function () { var bullet = new Bullet(); bullet.x = self.x + 50; bullet.y = self.y; bullet.row = self.gridRow; bullets.push(bullet); game.addChild(bullet); LK.getSound('peasshooter').play(); // Play peashooter sound when shooting }; self.update = function () { if (LK.ticks - self.lastShot > self.shootDelay) { // Check if there's a zombie in this row var zombieInRow = false; for (var i = 0; i < zombies.length; i++) { if (zombies[i].gridRow === self.gridRow && zombies[i].x > self.x) { zombieInRow = true; break; } } if (zombieInRow) { self.shoot(); self.lastShot = LK.ticks; } } }; return self; }); var ShieldZombie = Container.expand(function () { var self = Container.call(this); var body = self.attachAsset('zombieBody', { anchorX: 0.5, anchorY: 1 }); body.y = 25; body.tint = 0x8B4513; // Brown tint for shield zombie var head = self.attachAsset('zombieHead', { anchorX: 0.5, anchorY: 1 }); head.y = -10; head.tint = 0x8B4513; // Create shield visual with rim, main body, and center decoration var shieldRim = self.attachAsset('shieldRim', { anchorX: 0.5, anchorY: 0.5 }); shieldRim.x = 25; // Position shield in front of zombie shieldRim.y = 0; var shield = self.attachAsset('shieldVisual', { anchorX: 0.5, anchorY: 0.5 }); shield.x = 25; // Position shield in front of zombie shield.y = 0; var shieldCenter = self.attachAsset('shieldCenter', { anchorX: 0.5, anchorY: 0.5 }); shieldCenter.x = 25; // Position shield center decoration shieldCenter.y = 0; self.health = 150; // Zombie health (only takes damage after shield is destroyed) self.shieldHealth = 150; // Shield health (must be destroyed first) self.speed = 0.25; // Slower than regular zombie due to shield weight self.gridRow = 0; self.isEating = false; self.eatDamage = 12; self.hasAttackedHouse = false; self.hasShield = true; // Track if shield is still active self.update = function () { // Handle freeze effect if (self.isFrozen && LK.ticks > self.frozenUntil) { self.speed = self.originalSpeed; self.isFrozen = false; // Remove visual freeze effect but keep original brown tint if (self.body) { self.body.tint = 0x8B4513; } if (self.head) { self.head.tint = 0x8B4513; } } if (!self.isEating) { self.x -= self.speed; } // Check if shield is destroyed if (self.hasShield && self.shieldHealth <= 0) { self.hasShield = false; shield.destroy(); // Remove shield visual shieldRim.destroy(); // Remove shield rim shieldCenter.destroy(); // Remove shield center decoration self.speed = 0.4; // Increase speed when shield is lost } // Check if reached house if (self.x <= 150 && !self.hasAttackedHouse) { houseHealth -= 12; healthText.setText('House Health: ' + houseHealth); self.hasAttackedHouse = true; self.destroy(); for (var z = zombies.length - 1; z >= 0; z--) { if (zombies[z] === self) { zombies.splice(z, 1); break; } } if (houseHealth <= 0) { LK.showGameOver(); } } // Check collision with plants for (var i = plants.length - 1; i >= 0; i--) { var plant = plants[i]; if (plant.gridRow === self.gridRow && Math.abs(self.x - plant.x) < 50) { self.isEating = true; // WallNut plants block zombies completely if (plant instanceof WallNut) { // Initialize damage timer if not set if (self.lastDamageTime === undefined) { self.lastDamageTime = LK.ticks; } // Deal damage every 30 ticks while touching WallNut if (LK.ticks - self.lastDamageTime >= 30) { plant.health -= 12; self.lastDamageTime = LK.ticks; } // Zombie cannot move past WallNut until it's destroyed if (plant.health > 0) { // Push zombie back slightly to prevent overlap self.x = plant.x + 55; } } else { // Initialize damage timer if not set if (self.lastDamageTime === undefined) { self.lastDamageTime = LK.ticks; } // Deal damage every 60 ticks while touching other plants if (LK.ticks - self.lastDamageTime >= 60) { plant.health -= 12; // Shield zombie destroys plants with moderate damage self.lastDamageTime = LK.ticks; } } if (plant.health <= 0) { plant.destroy(); plants.splice(i, 1); self.isEating = false; self.lastDamageTime = undefined; // Reset damage timer when plant is destroyed } break; } } // Reset damage timer when not eating if (!self.isEating) { self.lastDamageTime = undefined; } }; return self; }); var Sunflower = Container.expand(function () { var self = Container.call(this); // Create petals around center for (var i = 0; i < 8; i++) { var petal = self.attachAsset('sunflowerPetal', { anchorX: 0.5, anchorY: 1 }); var angle = i / 8 * Math.PI * 2; petal.x = Math.cos(angle) * 25; petal.y = Math.sin(angle) * 25; petal.rotation = angle + Math.PI / 2; } var center = self.attachAsset('sunflowerCenter', { anchorX: 0.5, anchorY: 0.5 }); self.health = 100; self.lastSunGeneration = 0; self.sunDelay = 300; // 5 seconds at 60fps self.gridRow = 0; self.gridCol = 0; self.generateSun = function () { sun += 25; sunText.setText('Sun: ' + sun); self.lastSunGeneration = LK.ticks; }; self.update = function () { if (LK.ticks - self.lastSunGeneration > self.sunDelay) { self.generateSun(); } }; return self; }); var TankZombie = Container.expand(function () { var self = Container.call(this); var body = self.attachAsset('zombieBody', { anchorX: 0.5, anchorY: 1 }); body.y = 25; body.tint = 0x333333; // Dark gray tint for tank zombie body.scaleX = 1.5; body.scaleY = 1.5; var head = self.attachAsset('zombieHead', { anchorX: 0.5, anchorY: 1 }); head.y = -15; head.tint = 0x333333; head.scaleX = 1.5; head.scaleY = 1.5; self.health = 450; // Very high health self.speed = 0.2; // Very slow self.gridRow = 0; self.isEating = false; self.eatDamage = 20; self.hasAttackedHouse = false; self.update = function () { // Handle freeze effect if (self.isFrozen && LK.ticks > self.frozenUntil) { self.speed = self.originalSpeed; self.isFrozen = false; // Remove visual freeze effect but keep original dark gray tint if (self.body) { self.body.tint = 0x333333; } if (self.head) { self.head.tint = 0x333333; } } if (!self.isEating) { self.x -= self.speed; } // Check if reached house if (self.x <= 150 && !self.hasAttackedHouse) { houseHealth -= 25; healthText.setText('House Health: ' + houseHealth); self.hasAttackedHouse = true; self.destroy(); for (var z = zombies.length - 1; z >= 0; z--) { if (zombies[z] === self) { zombies.splice(z, 1); break; } } if (houseHealth <= 0) { LK.showGameOver(); } } // Check collision with plants for (var i = plants.length - 1; i >= 0; i--) { var plant = plants[i]; if (plant.gridRow === self.gridRow && Math.abs(self.x - plant.x) < 50) { self.isEating = true; // WallNut plants block zombies completely if (plant instanceof WallNut) { // Initialize damage timer if not set if (self.lastDamageTime === undefined) { self.lastDamageTime = LK.ticks; } // Deal damage every 40 ticks (slower but high damage) while touching WallNut if (LK.ticks - self.lastDamageTime >= 40) { plant.health -= 20; // Tank zombie destroys wallnut with high damage self.lastDamageTime = LK.ticks; } // Zombie cannot move past WallNut until it's destroyed if (plant.health > 0) { // Push zombie back slightly to prevent overlap self.x = plant.x + 55; } } else { // Initialize damage timer if not set if (self.lastDamageTime === undefined) { self.lastDamageTime = LK.ticks; } // Deal damage every 70 ticks while touching other plants if (LK.ticks - self.lastDamageTime >= 70) { plant.health -= 25; // Tank zombie destroys plants with massive damage self.lastDamageTime = LK.ticks; } } if (plant.health <= 0) { plant.destroy(); plants.splice(i, 1); self.isEating = false; self.lastDamageTime = undefined; // Reset damage timer when plant is destroyed } break; } } // Reset damage timer when not eating if (!self.isEating) { self.lastDamageTime = undefined; } }; return self; }); var WallNut = Container.expand(function () { var self = Container.call(this); var body = self.attachAsset('placedWallNutBody', { anchorX: 0.5, anchorY: 0.5 }); body.scaleX = 1.3; body.scaleY = 1.3; self.health = 60; // Increased health, breaks after 6 zombie hits self.gridRow = 0; self.gridCol = 0; self.update = function () { // Check if being touched by any zombie and take continuous damage for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; if (zombie.gridRow === self.gridRow && Math.abs(zombie.x - self.x) < 50) { // Initialize damage timer if not set if (self.lastDamageTime === undefined) { self.lastDamageTime = LK.ticks; } // Take damage every 30 ticks (0.5 seconds) from any zombie type if (LK.ticks - self.lastDamageTime >= 30) { var damageAmount = 10; // Base damage from regular zombies if (zombie instanceof FastZombie) { damageAmount = 15; } else if (zombie instanceof TankZombie) { damageAmount = 20; } else if (zombie instanceof ShieldZombie) { damageAmount = 12; } else if (zombie instanceof JumperZombie) { damageAmount = 12; } self.health -= damageAmount; self.lastDamageTime = LK.ticks; } break; // Only take damage from one zombie at a time } } // Reset damage timer when no zombies are touching var anyZombieTouching = false; for (var j = 0; j < zombies.length; j++) { var zombie = zombies[j]; if (zombie.gridRow === self.gridRow && Math.abs(zombie.x - self.x) < 50) { anyZombieTouching = true; break; } } if (!anyZombieTouching) { self.lastDamageTime = undefined; } // Check if health decreased to play breaking sound if (self.lastHealth === undefined) { self.lastHealth = self.health; } if (self.lastHealth > self.health) { LK.getSound('ceviz').play(); // Play walnut breaking sound when damaged } self.lastHealth = self.health; // Check if wallnut is destroyed if (self.health <= 0) { // Play walnut breaking sound when completely destroyed LK.getSound('ceviz').play(); // Remove from plants array and destroy for (var k = plants.length - 1; k >= 0; k--) { if (plants[k] === self) { plants.splice(k, 1); break; } } self.destroy(); } }; return self; }); var Zombie = Container.expand(function () { var self = Container.call(this); var body = self.attachAsset('zombieBody', { anchorX: 0.5, anchorY: 1 }); body.y = 25; var head = self.attachAsset('zombieHead', { anchorX: 0.5, anchorY: 1 }); head.y = -10; self.health = 150; self.speed = 0.3; self.gridRow = 0; self.isEating = false; self.eatDamage = 10; self.hasAttackedHouse = false; self.update = function () { // Handle freeze effect if (self.isFrozen && LK.ticks > self.frozenUntil) { self.speed = self.originalSpeed; self.isFrozen = false; // Remove visual freeze effect if (self.body) { self.body.tint = 0xFFFFFF; } if (self.head) { self.head.tint = 0xFFFFFF; } } if (!self.isEating) { self.x -= self.speed; } // Check if reached house - zombie damages house once with 10 health if (self.x <= 150 && !self.hasAttackedHouse) { houseHealth -= 10; healthText.setText('House Health: ' + houseHealth); self.hasAttackedHouse = true; // Destroy zombie after attacking house self.destroy(); for (var z = zombies.length - 1; z >= 0; z--) { if (zombies[z] === self) { zombies.splice(z, 1); break; } } // Check game over condition if (houseHealth <= 0) { LK.showGameOver(); } } // Check collision with plants for (var i = plants.length - 1; i >= 0; i--) { var plant = plants[i]; if (plant.gridRow === self.gridRow && Math.abs(self.x - plant.x) < 50) { self.isEating = true; // WallNut plants block zombies completely if (plant instanceof WallNut) { // Initialize damage timer if not set if (self.lastDamageTime === undefined) { self.lastDamageTime = LK.ticks; } // Deal damage every 30 ticks (0.5 seconds) while touching WallNut if (LK.ticks - self.lastDamageTime >= 30) { plant.health -= 10; // Continuous damage to wallnut self.lastDamageTime = LK.ticks; } // Zombie cannot move past WallNut until it's destroyed if (plant.health > 0) { // Push zombie back slightly to prevent overlap self.x = plant.x + 55; } } else { // Initialize damage timer if not set if (self.lastDamageTime === undefined) { self.lastDamageTime = LK.ticks; } // Deal damage every 60 ticks (1 second) while touching other plants if (LK.ticks - self.lastDamageTime >= 60) { plant.health -= 10; // Increased damage to destroy plants faster self.lastDamageTime = LK.ticks; } } if (plant.health <= 0) { plant.destroy(); plants.splice(i, 1); self.isEating = false; self.lastDamageTime = undefined; // Reset damage timer when plant is destroyed } break; } } // Reset damage timer when not eating if (!self.isEating) { self.lastDamageTime = undefined; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x5D4E37 // Darker brown paper background color }); /**** * Game Code ****/ // Level selection screen variables // Import storage for persistent level progression var levelButtons = []; var totalLevels = 10; var unlockedLevels = storage.unlockedLevels || 1; // Load from storage or default to 1 var levelProgress = storage.levelProgress || {}; // Load from storage or default to empty // Global game variables var plants = []; var zombies = []; var bullets = []; var sun = 150; var sunText; var houseHealth; var healthText; // Music control variables var isMusicOn = storage.isMusicOn !== undefined ? storage.isMusicOn : true; // Default music on var musicButton; // Create brown paper background var background = LK.getAsset('paperBackground', { anchorX: 0, anchorY: 0 }); game.addChild(background); // Create title var titleBg = LK.getAsset('titleBackground', { anchorX: 0.5, anchorY: 0.5 }); titleBg.x = 1024; titleBg.y = 300; game.addChild(titleBg); // Create level buttons in a grid layout var buttonsPerRow = 5; var buttonSpacingX = 350; var buttonSpacingY = 250; var startX = 400; var startY = 700; for (var i = 0; i < totalLevels; i++) { var levelButton = new LevelButton(); levelButton.setLevelNumber(i + 1); // Calculate position in grid var row = Math.floor(i / buttonsPerRow); var col = i % buttonsPerRow; levelButton.x = startX + col * buttonSpacingX; levelButton.y = startY + row * buttonSpacingY; // Set unlock status - only unlock levels up to the player's progress levelButton.setUnlocked(i < unlockedLevels); // Set stars from progress (if any) if (levelProgress[i + 1]) { levelButton.setStars(levelProgress[i + 1]); } else { levelButton.setStars(0); } levelButtons.push(levelButton); game.addChild(levelButton); } // Create music toggle button with new design musicButton = LK.getAsset('selectionBox', { anchorX: 0.5, anchorY: 0.5 }); musicButton.x = 1700; musicButton.y = 200; musicButton.scaleX = 0.5; musicButton.scaleY = 0.5; musicButton.tint = isMusicOn ? 0x4CAF50 : 0xF44336; game.addChild(musicButton); // Add music icon symbol var musicIcon = new Text2(isMusicOn ? '♪' : '♪', { size: 50, fill: 0xFFFFFF }); musicIcon.anchor.set(0.5, 0.5); musicIcon.x = 1700; musicIcon.y = 185; musicIcon.alpha = isMusicOn ? 1.0 : 0.3; game.addChild(musicIcon); // Add music status text var musicText = new Text2(isMusicOn ? 'ON' : 'OFF', { size: 24, fill: 0xFFFFFF }); musicText.anchor.set(0.5, 0.5); musicText.x = 1700; musicText.y = 215; game.addChild(musicText); // Music button event handler musicButton.down = function () { LK.getSound('buttonClick').play(); isMusicOn = !isMusicOn; storage.isMusicOn = isMusicOn; // Save to storage musicButton.tint = isMusicOn ? 0x4CAF50 : 0xF44336; musicIcon.alpha = isMusicOn ? 1.0 : 0.3; musicText.setText(isMusicOn ? 'ON' : 'OFF'); if (isMusicOn) { // Resume music if we're in a level if (currentLevel > 0) { LK.playMusic('game'); } } else { LK.stopMusic(); } }; // Function to start a level function startLevel(levelNumber) { LK.getSound('levelSelect').play(); console.log('Starting level: ' + levelNumber); // Set current level for pause functionality currentLevel = levelNumber; // Clear the level selection screen game.removeChildren(); // Initialize the appropriate level if (levelNumber === 1) { initializeLevel1(); } else if (levelNumber === 2) { initializeLevel2(); } else if (levelNumber >= 3 && levelNumber <= 10) { // Initialize dynamic levels for 3-10 initializeDynamicLevel(levelNumber); } } // Initialize Level 1 Plants vs Zombies game function initializeLevel1() { // Start playing game music only if music is enabled if (isMusicOn) { LK.playMusic('game'); } // Game variables houseHealth = 100; sun = 150; // Starting money for level 1 plants = []; // Make plants global zombies = []; // Make zombies global bullets = []; // Make bullets global var sunflowers = []; var lastSunGeneration = 0; var lastZombieSpawn = 0; var levelStartTime = null; // Track when level started - will be set when first plant is placed var levelDuration = 7200; // 2 minutes at 60fps (120 seconds * 60 = 7200 ticks) // Grid setup var gridRows = 10; var gridCols = 12; var gridCellWidth = 180; var gridCellHeight = 120; var gridStartX = 200; var gridStartY = 500; // UI elements sunText = new Text2('Sun: ' + sun, { size: 40, fill: 0x000000 }); sunText.x = 1500; sunText.y = 50; game.addChild(sunText); healthText = new Text2('House Health: ' + houseHealth, { size: 40, fill: 0xFF0000 }); healthText.x = 1500; healthText.y = 100; game.addChild(healthText); // Add timer display var timerText = new Text2('Time: 2:00', { size: 40, fill: 0x0000FF }); timerText.x = 1500; timerText.y = 200; game.addChild(timerText); // Create grid for (var row = 0; row < gridRows; row++) { for (var col = 0; col < gridCols; col++) { var gridCell = LK.getAsset('gridTile', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.75, scaleY: 0.75 }); gridCell.x = gridStartX + col * gridCellWidth; gridCell.y = gridStartY + row * gridCellHeight; gridCell.alpha = 0.8; gridCell.tint = 0x8B7355; game.addChild(gridCell); } } // Plant selection buttons with proper spacing var peashooterButton = LK.getAsset('selectionBox', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); peashooterButton.x = 400; peashooterButton.y = 1800; peashooterButton.tint = 0x006400; game.addChild(peashooterButton); // Add peashooter image to button var peashooterImage = LK.getAsset('peashooterBody', { anchorX: 0.5, anchorY: 0.5 }); peashooterImage.x = 400; peashooterImage.y = 1780; peashooterImage.scaleX = 1.0; peashooterImage.scaleY = 1.0; game.addChild(peashooterImage); var peashooterCost = 50 + Math.max(0, 1 - 5) * 5; // Level 1 cost: 50, same for levels 1-5 var peashooterText = new Text2(peashooterCost + ' Sun', { size: 32, fill: 0xFFFFFF }); peashooterText.anchor.set(0.5, 0.5); peashooterText.x = 400; peashooterText.y = 1830; game.addChild(peashooterText); var sunflowerButton = LK.getAsset('selectionBox', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); sunflowerButton.x = 1000; sunflowerButton.y = 1800; sunflowerButton.tint = 0xB8860B; game.addChild(sunflowerButton); // Add sunflower image to button var sunflowerImage = LK.getAsset('sunflowerCenter', { anchorX: 0.5, anchorY: 0.5 }); sunflowerImage.x = 1000; sunflowerImage.y = 1780; sunflowerImage.scaleX = 0.75; sunflowerImage.scaleY = 0.75; game.addChild(sunflowerImage); var sunflowerCost = 50 + Math.max(0, 1 - 5) * 5; // Level 1 cost: 50, same for levels 1-5 var sunflowerText = new Text2(sunflowerCost + ' Sun', { size: 32, fill: 0xFFFFFF }); sunflowerText.anchor.set(0.5, 0.5); sunflowerText.x = 1000; sunflowerText.y = 1830; game.addChild(sunflowerText); var selectedPlant = null; // Event handlers peashooterButton.down = function () { if (sun >= peashooterCost) { selectedPlant = 'peashooter'; } }; sunflowerButton.down = function () { if (sun >= sunflowerCost) { selectedPlant = 'sunflower'; } }; game.down = function (x, y, obj) { if (selectedPlant && x >= gridStartX && x <= gridStartX + gridCols * gridCellWidth && y >= gridStartY && y <= gridStartY + gridRows * gridCellHeight) { var gridCol = Math.floor((x - gridStartX) / gridCellWidth); var gridRow = Math.floor((y - gridStartY) / gridCellHeight); // Check if position is valid and empty var positionEmpty = true; for (var i = 0; i < plants.length; i++) { if (plants[i].gridRow === gridRow && plants[i].gridCol === gridCol) { positionEmpty = false; break; } } if (positionEmpty && gridCol >= 0 && gridCol < gridCols && gridRow >= 0 && gridRow < gridRows) { var plant = null; if (selectedPlant === 'peashooter' && sun >= peashooterCost) { plant = new Peashooter(); sun -= peashooterCost; } else if (selectedPlant === 'sunflower' && sun >= sunflowerCost) { plant = new Sunflower(); sun -= sunflowerCost; } if (plant) { plant.x = gridStartX + gridCol * gridCellWidth; plant.y = gridStartY + gridRow * gridCellHeight; plant.gridRow = gridRow; plant.gridCol = gridCol; plants.push(plant); game.addChild(plant); sunText.setText('Sun: ' + sun); selectedPlant = null; } } } }; var zombiesStarted = false; // Track if zombies have started spawning // Game update function game.update = function () { // Start zombie spawning and timer only after first plant is placed if (!zombiesStarted && plants.length > 0) { zombiesStarted = true; levelStartTime = LK.ticks; // Start level timer when first plant is placed lastZombieSpawn = LK.ticks; // Initialize spawn timer } // Spawn zombies only after they have been started - progressive spawn rate // Start slow and gradually increase spawn rate over time var gameProgress = Math.min(LK.ticks / 3600, 1); // Progress from 0 to 1 over 60 seconds var currentSpawnRate = 180 - gameProgress * 120; // Start at 180 (3 seconds), end at 60 (1 second) if (zombiesStarted && LK.ticks - lastZombieSpawn > currentSpawnRate) { var zombie = new Zombie(); var row = Math.floor(Math.random() * gridRows); zombie.x = 1900; zombie.y = gridStartY + row * gridCellHeight; zombie.gridRow = row; zombies.push(zombie); game.addChild(zombie); LK.getSound('zombisesi').play(); // Play zombie sound when spawning lastZombieSpawn = LK.ticks; } // Update all game objects for (var i = plants.length - 1; i >= 0; i--) { plants[i].update(); } for (var j = zombies.length - 1; j >= 0; j--) { zombies[j].update(); } for (var k = bullets.length - 1; k >= 0; k--) { bullets[k].update(); } // Generate sun from sunflowers - only if sunflowers exist var sunflowerCount = 0; for (var s = 0; s < plants.length; s++) { if (plants[s] instanceof Sunflower) { sunflowerCount++; } } if (sunflowerCount > 0 && LK.ticks - lastSunGeneration > 600) { // Every 10 seconds, only if sunflowers exist sun += 10; sunText.setText('Sun: ' + sun); lastSunGeneration = LK.ticks; } // Increased spawn when 1.5 minutes (90 seconds = 5400 ticks) remain - starts at 1.5 minutes left var timeElapsed = levelStartTime ? LK.ticks - levelStartTime : 0; var timeRemaining = Math.max(0, levelDuration - timeElapsed); if (timeRemaining <= 5400) { // 1.5 minutes = 90 seconds = 5400 ticks var intensifiedSpawnRate = 30 - (5400 - timeRemaining) / 5400 * 20; // Start at 30 (0.5 seconds), end at 10 (0.17 seconds) if (LK.ticks - lastZombieSpawn > intensifiedSpawnRate) { var zombie = new Zombie(); var row = Math.floor(Math.random() * gridRows); zombie.x = 1900; zombie.y = gridStartY + row * gridCellHeight; zombie.gridRow = row; zombies.push(zombie); game.addChild(zombie); LK.getSound('zombisesi').play(); // Play zombie sound when spawning lastZombieSpawn = LK.ticks; } } // Update timer display var timeElapsed = levelStartTime ? LK.ticks - levelStartTime : 0; var timeRemaining = Math.max(0, levelDuration - timeElapsed); var minutes = Math.floor(timeRemaining / 3600); var seconds = Math.floor(timeRemaining % 3600 / 60); var timeString = minutes + ':' + (seconds < 10 ? '0' : '') + seconds; timerText.setText('Time: ' + timeString); // Check win condition - survive for 2 minutes if (levelStartTime && timeElapsed >= levelDuration) { // Player survived 2 minutes - they win! unlockNextLevel(); // Unlock next level when current level is completed // Calculate stars based on house health: 3 stars = 80+%, 2 stars = 50+%, 1 star = any health remaining var stars = 1; if (houseHealth >= 80) { stars = 3; } else if (houseHealth >= 50) { stars = 2; } setLevelStars(1, stars); LK.showYouWin(); } }; } // Initialize Level 2 Plants vs Zombies game with more zombies and fast zombies function initializeLevel2() { // Start playing game music only if music is enabled if (isMusicOn) { LK.playMusic('game'); } // Game variables houseHealth = 100; sun = 200; // Starting money for level 2 (50 more than level 1) plants = []; // Make plants global zombies = []; // Make zombies global bullets = []; // Make bullets global var levelStartTime = null; // Track when level started - will be set when first plant is placed var levelDuration = 7200; // 2 minutes at 60fps (120 seconds * 60 = 7200 ticks) // Night theme background var nightBg = LK.getAsset('nightBackground', { anchorX: 0, anchorY: 0 }); game.addChild(nightBg); var sunflowers = []; var lastSunGeneration = 0; var lastZombieSpawn = 0; var lastFastZombieSpawn = 0; // Grid setup var gridRows = 10; var gridCols = 12; var gridCellWidth = 180; var gridCellHeight = 120; var gridStartX = 200; var gridStartY = 500; // UI elements sunText = new Text2('Sun: ' + sun, { size: 40, fill: 0x000000 }); sunText.x = 1500; sunText.y = 50; game.addChild(sunText); healthText = new Text2('House Health: ' + houseHealth, { size: 40, fill: 0xFF0000 }); healthText.x = 1500; healthText.y = 100; game.addChild(healthText); // Level indicator var levelText = new Text2('Level 2', { size: 50, fill: 0xFFFFFF }); levelText.x = 1500; levelText.y = 150; game.addChild(levelText); // Add timer display var timerText = new Text2('Time: 2:00', { size: 40, fill: 0x0000FF }); timerText.x = 1500; timerText.y = 200; game.addChild(timerText); // Create grid for (var row = 0; row < gridRows; row++) { for (var col = 0; col < gridCols; col++) { var gridCell = LK.getAsset('gridTile', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.75, scaleY: 0.75 }); gridCell.x = gridStartX + col * gridCellWidth; gridCell.y = gridStartY + row * gridCellHeight; gridCell.alpha = 0.9; gridCell.tint = 0x1A0D33; game.addChild(gridCell); } } // Plant selection buttons with proper spacing var peashooterButton = LK.getAsset('selectionBox', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); peashooterButton.x = 350; peashooterButton.y = 1800; peashooterButton.tint = 0x006400; game.addChild(peashooterButton); // Add peashooter image to button var peashooterImage = LK.getAsset('peashooterBody', { anchorX: 0.5, anchorY: 0.5 }); peashooterImage.x = 350; peashooterImage.y = 1780; peashooterImage.scaleX = 1.0; peashooterImage.scaleY = 1.0; game.addChild(peashooterImage); var peashooterCost = 50 + Math.max(0, 2 - 5) * 5; // Level 2 cost: 50, same for levels 1-5 var peashooterText = new Text2(peashooterCost + ' Sun', { size: 32, fill: 0xFFFFFF }); peashooterText.anchor.set(0.5, 0.5); peashooterText.x = 350; peashooterText.y = 1830; game.addChild(peashooterText); var sunflowerButton = LK.getAsset('selectionBox', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); sunflowerButton.x = 800; sunflowerButton.y = 1800; sunflowerButton.tint = 0xB8860B; game.addChild(sunflowerButton); // Add sunflower image to button var sunflowerImage = LK.getAsset('sunflowerCenter', { anchorX: 0.5, anchorY: 0.5 }); sunflowerImage.x = 800; sunflowerImage.y = 1780; sunflowerImage.scaleX = 0.75; sunflowerImage.scaleY = 0.75; game.addChild(sunflowerImage); var sunflowerCost = 50 + Math.max(0, 2 - 5) * 5; // Level 2 cost: 50, same for levels 1-5 var sunflowerText = new Text2(sunflowerCost + ' Sun', { size: 32, fill: 0xFFFFFF }); sunflowerText.anchor.set(0.5, 0.5); sunflowerText.x = 800; sunflowerText.y = 1830; game.addChild(sunflowerText); var selectedPlant = null; // Add CherryBomb button for level 2 var cherrybombButton = LK.getAsset('selectionBox', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); cherrybombButton.x = 1250; cherrybombButton.y = 1800; cherrybombButton.tint = 0xFF0000; game.addChild(cherrybombButton); var cherrybombImage = LK.getAsset('cherryImage', { anchorX: 0.5, anchorY: 0.5 }); cherrybombImage.x = 1250; cherrybombImage.y = 1780; cherrybombImage.scaleX = 0.75; cherrybombImage.scaleY = 0.75; game.addChild(cherrybombImage); var cherrybombCost = 100 + Math.max(0, 2 - 5) * 10; // Level 2 cost: 100, same for levels 1-5 var cherrybombText = new Text2(cherrybombCost + ' Sun', { size: 32, fill: 0xFFFFFF }); cherrybombText.anchor.set(0.5, 0.5); cherrybombText.x = 1250; cherrybombText.y = 1830; game.addChild(cherrybombText); cherrybombButton.down = function () { if (sun >= cherrybombCost) { selectedPlant = 'cherrybomb'; } }; // Event handlers peashooterButton.down = function () { if (sun >= peashooterCost) { selectedPlant = 'peashooter'; } }; sunflowerButton.down = function () { if (sun >= sunflowerCost) { selectedPlant = 'sunflower'; } }; game.down = function (x, y, obj) { if (selectedPlant && x >= gridStartX && x <= gridStartX + gridCols * gridCellWidth && y >= gridStartY && y <= gridStartY + gridRows * gridCellHeight) { var gridCol = Math.floor((x - gridStartX) / gridCellWidth); var gridRow = Math.floor((y - gridStartY) / gridCellHeight); // Check if position is valid and empty var positionEmpty = true; for (var i = 0; i < plants.length; i++) { if (plants[i].gridRow === gridRow && plants[i].gridCol === gridCol) { positionEmpty = false; break; } } if (positionEmpty && gridCol >= 0 && gridCol < gridCols && gridRow >= 0 && gridRow < gridRows) { var plant = null; if (selectedPlant === 'peashooter' && sun >= peashooterCost) { plant = new Peashooter(); sun -= peashooterCost; } else if (selectedPlant === 'sunflower' && sun >= sunflowerCost) { plant = new Sunflower(); sun -= sunflowerCost; } else if (selectedPlant === 'cherrybomb' && sun >= cherrybombCost) { plant = new CherryBomb(); sun -= cherrybombCost; } if (plant) { plant.x = gridStartX + gridCol * gridCellWidth; plant.y = gridStartY + gridRow * gridCellHeight; plant.gridRow = gridRow; plant.gridCol = gridCol; plants.push(plant); game.addChild(plant); sunText.setText('Sun: ' + sun); selectedPlant = null; } } } }; var zombiesStarted = false; // Track if zombies have started spawning // Game update function for Level 2 game.update = function () { // Start zombie spawning and timer only after first plant is placed if (!zombiesStarted && plants.length > 0) { zombiesStarted = true; levelStartTime = LK.ticks; // Start level timer when first plant is placed lastZombieSpawn = LK.ticks; // Initialize spawn timer lastFastZombieSpawn = LK.ticks; // Initialize fast zombie spawn timer } // Spawn regular zombies with progressive spawn rate - only after zombies started var gameProgress = Math.min(LK.ticks / 5400, 1); // Progress from 0 to 1 over 90 seconds var currentSpawnRate = 120 - gameProgress * 80; // Start at 120 (2 seconds), end at 40 (0.67 seconds) if (zombiesStarted && LK.ticks - lastZombieSpawn > currentSpawnRate) { var zombie = new Zombie(); var row = Math.floor(Math.random() * gridRows); zombie.x = 1900; zombie.y = gridStartY + row * gridCellHeight; zombie.gridRow = row; zombies.push(zombie); game.addChild(zombie); LK.getSound('zombisesi').play(); // Play zombie sound when spawning lastZombieSpawn = LK.ticks; } // Spawn fast zombies with progressive spawn rate - only after zombies started var fastSpawnRate = 180 - gameProgress * 120; // Start at 180 (3 seconds), end at 60 (1 second) if (zombiesStarted && LK.ticks - lastFastZombieSpawn > fastSpawnRate) { var fastZombie = new FastZombie(); var row = Math.floor(Math.random() * gridRows); fastZombie.x = 1900; fastZombie.y = gridStartY + row * gridCellHeight; fastZombie.gridRow = row; zombies.push(fastZombie); game.addChild(fastZombie); LK.getSound('zombisesi').play(); // Play zombie sound when spawning lastFastZombieSpawn = LK.ticks; } // Spawn tank zombies with progressive spawn rate - only after zombies started var tankSpawnRate = 300 - gameProgress * 150; // Start at 300 (5 seconds), end at 150 (2.5 seconds) if (zombiesStarted && LK.ticks % tankSpawnRate === 0 && LK.ticks > 0) { var tankZombie = new TankZombie(); var row = Math.floor(Math.random() * gridRows); tankZombie.x = 1900; tankZombie.y = gridStartY + row * gridCellHeight; tankZombie.gridRow = row; zombies.push(tankZombie); game.addChild(tankZombie); LK.getSound('zombisesi').play(); // Play zombie sound when spawning } // Update all game objects for (var i = plants.length - 1; i >= 0; i--) { plants[i].update(); } for (var j = zombies.length - 1; j >= 0; j--) { zombies[j].update(); } for (var k = bullets.length - 1; k >= 0; k--) { bullets[k].update(); } // Generate sun from sunflowers - only if sunflowers exist var sunflowerCount = 0; for (var s = 0; s < plants.length; s++) { if (plants[s] instanceof Sunflower) { sunflowerCount++; } } if (sunflowerCount > 0 && LK.ticks - lastSunGeneration > 600) { // Every 10 seconds, only if sunflowers exist sun += 10; sunText.setText('Sun: ' + sun); lastSunGeneration = LK.ticks; } // Increased spawn when 1.5 minutes (90 seconds = 5400 ticks) remain - starts at 1.5 minutes left var timeElapsed = levelStartTime ? LK.ticks - levelStartTime : 0; var timeRemaining = Math.max(0, levelDuration - timeElapsed); if (timeRemaining <= 5400) { // 1.5 minutes = 90 seconds = 5400 ticks var intensifiedSpawnRate = 30 - (5400 - timeRemaining) / 5400 * 20; // Start at 30 (0.5 seconds), end at 10 (0.17 seconds) if (LK.ticks - lastZombieSpawn > intensifiedSpawnRate) { var zombie = new Zombie(); var row = Math.floor(Math.random() * gridRows); zombie.x = 1900; zombie.y = gridStartY + row * gridCellHeight; zombie.gridRow = row; zombies.push(zombie); game.addChild(zombie); LK.getSound('zombisesi').play(); // Play zombie sound when spawning lastZombieSpawn = LK.ticks; } // Also spawn fast zombies more frequently during intensified wave var intensifiedFastSpawnRate = 60 - (5400 - timeRemaining) / 5400 * 45; // Start at 60 (1 second), end at 15 (0.25 seconds) if (LK.ticks - lastFastZombieSpawn > intensifiedFastSpawnRate) { var fastZombie = new FastZombie(); var row = Math.floor(Math.random() * gridRows); fastZombie.x = 1900; fastZombie.y = gridStartY + row * gridCellHeight; fastZombie.gridRow = row; zombies.push(fastZombie); game.addChild(fastZombie); LK.getSound('zombisesi').play(); // Play zombie sound when spawning lastFastZombieSpawn = LK.ticks; } } // Update timer display var timeElapsed = levelStartTime ? LK.ticks - levelStartTime : 0; var timeRemaining = Math.max(0, levelDuration - timeElapsed); var minutes = Math.floor(timeRemaining / 3600); var seconds = Math.floor(timeRemaining % 3600 / 60); var timeString = minutes + ':' + (seconds < 10 ? '0' : '') + seconds; timerText.setText('Time: ' + timeString); // Check win condition - survive for 2 minutes if (levelStartTime && timeElapsed >= levelDuration) { // Player survived 2 minutes - they win! unlockNextLevel(); // Unlock next level when current level is completed // Calculate stars based on house health: 3 stars = 80+%, 2 stars = 50+%, 1 star = any health remaining var stars = 1; if (houseHealth >= 80) { stars = 3; } else if (houseHealth >= 50) { stars = 2; } setLevelStars(2, stars); LK.showYouWin(); } }; } // Initialize Dynamic Level for levels 3 and beyond function initializeDynamicLevel(levelNumber) { // Start playing game music only if music is enabled if (isMusicOn) { LK.playMusic('game'); } // Game variables houseHealth = 100; sun = 150 + (levelNumber - 1) * 50; // Progressive starting money: level 1=150, level 2=200, level 3=250, etc. plants = []; // Make plants global zombies = []; // Make zombies global bullets = []; // Make bullets global // Dynamic theme based on level var themeBackgrounds = ['desertBackground', 'iceBackground', 'jungleBackground']; var themeIndex = (levelNumber - 3) % themeBackgrounds.length; var themeBg = LK.getAsset(themeBackgrounds[themeIndex], { anchorX: 0, anchorY: 0 }); game.addChild(themeBg); var sunflowers = []; var lastSunGeneration = 0; var lastZombieSpawn = 0; var lastFastZombieSpawn = 0; // Dynamic difficulty scaling based on level - with increased spawn rates var difficultyMultiplier = levelNumber - 2; // Starts at 1 for level 3 var zombieSpawnRate = Math.max(30, 90 - difficultyMultiplier * 15); // Much faster spawning, increased each level var fastZombieSpawnRate = Math.max(60, 180 - difficultyMultiplier * 20); // Much more fast zombies // Dynamic level duration based on level number var levelDuration; if (levelNumber === 3) { levelDuration = 7200; // 2 minutes at 60fps (120 seconds * 60 = 7200 ticks) } else if (levelNumber >= 4 && levelNumber <= 6) { levelDuration = 9000; // 2.5 minutes at 60fps (150 seconds * 60 = 9000 ticks) } else if (levelNumber >= 7 && levelNumber <= 10) { levelDuration = 12600; // 3.5 minutes at 60fps (210 seconds * 60 = 12600 ticks) } var finalWaveStart = levelDuration - 1800; // Final wave starts 30 seconds before end var levelStartTime = null; // Track when level started - will be set when first plant is placed // Grid setup var gridRows = 10; var gridCols = 12; var gridCellWidth = 180; var gridCellHeight = 120; var gridStartX = 200; var gridStartY = 500; // UI elements sunText = new Text2('Sun: ' + sun, { size: 40, fill: 0x000000 }); sunText.x = 1500; sunText.y = 50; game.addChild(sunText); healthText = new Text2('House Health: ' + houseHealth, { size: 40, fill: 0xFF0000 }); healthText.x = 1500; healthText.y = 100; game.addChild(healthText); // Level indicator var levelText = new Text2('Level ' + levelNumber, { size: 50, fill: 0xFFFFFF }); levelText.x = 1500; levelText.y = 150; game.addChild(levelText); // Add timer display var initialTimeString; if (levelNumber === 3) { initialTimeString = 'Time: 2:00'; } else if (levelNumber >= 4 && levelNumber <= 6) { initialTimeString = 'Time: 2:30'; } else if (levelNumber >= 7 && levelNumber <= 10) { initialTimeString = 'Time: 3:30'; } var timerText = new Text2(initialTimeString, { size: 40, fill: 0x0000FF }); timerText.x = 1500; timerText.y = 200; game.addChild(timerText); // Create grid for (var row = 0; row < gridRows; row++) { for (var col = 0; col < gridCols; col++) { var gridCell = LK.getAsset('gridTile', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.75, scaleY: 0.75 }); gridCell.x = gridStartX + col * gridCellWidth; gridCell.y = gridStartY + row * gridCellHeight; gridCell.alpha = 0.9; // Apply theme-specific tinting with unique grid appearances if (themeBackgrounds[themeIndex] === 'desertBackground') { gridCell.tint = 0xD2691E; // Sandy orange for desert } else if (themeBackgrounds[themeIndex] === 'iceBackground') { gridCell.tint = 0x87CEEB; // Light blue for ice } else if (themeBackgrounds[themeIndex] === 'jungleBackground') { gridCell.tint = 0x228B22; // Forest green for jungle } game.addChild(gridCell); } } // Plant selection buttons with proper spacing var peashooterButton = LK.getAsset('selectionBox', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); peashooterButton.x = 300; peashooterButton.y = 1800; peashooterButton.tint = 0x006400; game.addChild(peashooterButton); // Add peashooter image to button var peashooterImage = LK.getAsset('peashooterBody', { anchorX: 0.5, anchorY: 0.5 }); peashooterImage.x = 300; peashooterImage.y = 1780; peashooterImage.scaleX = 1.0; peashooterImage.scaleY = 1.0; game.addChild(peashooterImage); var peashooterCost = 50 + Math.max(0, levelNumber - 5) * 5; // Cost same for levels 1-5, then increases by 5 per level var peashooterText = new Text2(peashooterCost + ' Sun', { size: 32, fill: 0xFFFFFF }); peashooterText.anchor.set(0.5, 0.5); peashooterText.x = 300; peashooterText.y = 1830; game.addChild(peashooterText); var sunflowerButton = LK.getAsset('selectionBox', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); sunflowerButton.x = 700; sunflowerButton.y = 1800; sunflowerButton.tint = 0xB8860B; game.addChild(sunflowerButton); // Add sunflower image to button var sunflowerImage = LK.getAsset('sunflowerCenter', { anchorX: 0.5, anchorY: 0.5 }); sunflowerImage.x = 700; sunflowerImage.y = 1780; sunflowerImage.scaleX = 0.75; sunflowerImage.scaleY = 0.75; game.addChild(sunflowerImage); var sunflowerCost = 50 + Math.max(0, levelNumber - 5) * 5; // Cost same for levels 1-5, then increases by 5 per level var sunflowerText = new Text2(sunflowerCost + ' Sun', { size: 32, fill: 0xFFFFFF }); sunflowerText.anchor.set(0.5, 0.5); sunflowerText.x = 700; sunflowerText.y = 1830; game.addChild(sunflowerText); var selectedPlant = null; // Add more plant options based on level if (levelNumber >= 3) { // Add DoublePeashooter button var doublePeaButton = LK.getAsset('selectionBox', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); doublePeaButton.x = 1100; doublePeaButton.y = 1800; doublePeaButton.tint = 0x00AA00; game.addChild(doublePeaButton); var doublePeaImage = LK.getAsset('placedDoublePeaBody', { anchorX: 0.5, anchorY: 0.5 }); doublePeaImage.x = 1100; doublePeaImage.y = 1780; game.addChild(doublePeaImage); var doublePeaCost = 100 + Math.max(0, levelNumber - 5) * 10; // Cost same for levels 1-5, then increases by 10 per level var doublePeaText = new Text2(doublePeaCost + ' Sun', { size: 28, fill: 0xFFFFFF }); doublePeaText.anchor.set(0.5, 0.5); doublePeaText.x = 1100; doublePeaText.y = 1830; game.addChild(doublePeaText); doublePeaButton.down = function () { if (sun >= doublePeaCost) { selectedPlant = 'doublepea'; } }; } if (levelNumber >= 4) { // Add CherryBomb button var cherrybombButton = LK.getAsset('selectionBox', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); cherrybombButton.x = 1500; cherrybombButton.y = 1800; cherrybombButton.tint = 0xFF0000; game.addChild(cherrybombButton); var cherrybombImage = LK.getAsset('cherryImage', { anchorX: 0.5, anchorY: 0.5 }); cherrybombImage.x = 1500; cherrybombImage.y = 1780; cherrybombImage.scaleX = 0.6; cherrybombImage.scaleY = 0.6; game.addChild(cherrybombImage); var cherrybombCost = 100 + Math.max(0, levelNumber - 5) * 10; // Cost same for levels 1-5, then increases by 10 per level var cherrybombText = new Text2(cherrybombCost + ' Sun', { size: 28, fill: 0xFFFFFF }); cherrybombText.anchor.set(0.5, 0.5); cherrybombText.x = 1500; cherrybombText.y = 1830; game.addChild(cherrybombText); cherrybombButton.down = function () { if (sun >= cherrybombCost) { selectedPlant = 'cherrybomb'; } }; } if (levelNumber >= 6) { // Add WallNut button below CherryBomb button var wallnutButton = LK.getAsset('selectionBox', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); wallnutButton.x = 1500; wallnutButton.y = 2000; wallnutButton.tint = 0x8B4513; game.addChild(wallnutButton); var wallnutImage = LK.getAsset('placedWallNutBody', { anchorX: 0.5, anchorY: 0.5 }); wallnutImage.x = 1500; wallnutImage.y = 1980; wallnutImage.scaleX = 0.9; wallnutImage.scaleY = 0.9; game.addChild(wallnutImage); var wallnutCost = 50 + Math.max(0, levelNumber - 5) * 5; // Cost same for levels 1-5, then increases by 5 per level var wallnutText = new Text2(wallnutCost + ' Sun', { size: 32, fill: 0xFFFFFF }); wallnutText.anchor.set(0.5, 0.5); wallnutText.x = 1500; wallnutText.y = 2030; game.addChild(wallnutText); wallnutButton.down = function () { if (sun >= wallnutCost) { selectedPlant = 'wallnut'; } }; } if (levelNumber >= 8) { // Add IcePeashooter button below Peashooter var icepeaButton = LK.getAsset('selectionBox', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); icepeaButton.x = 300; icepeaButton.y = 2000; icepeaButton.tint = 0x87CEEB; game.addChild(icepeaButton); var icepeaImage = LK.getAsset('icePeashooterBody', { anchorX: 0.5, anchorY: 0.5 }); icepeaImage.x = 300; icepeaImage.y = 1980; icepeaImage.scaleX = 1.0; icepeaImage.scaleY = 1.0; game.addChild(icepeaImage); var icepeaCost = 75 + Math.max(0, levelNumber - 5) * 8; // Cost same for levels 1-5, then increases by 8 per level var icepeaText = new Text2(icepeaCost + ' Sun', { size: 32, fill: 0xFFFFFF }); icepeaText.anchor.set(0.5, 0.5); icepeaText.x = 300; icepeaText.y = 2030; game.addChild(icepeaText); icepeaButton.down = function () { if (sun >= icepeaCost) { selectedPlant = 'icepea'; } }; } // Event handlers peashooterButton.down = function () { if (sun >= peashooterCost) { selectedPlant = 'peashooter'; } }; sunflowerButton.down = function () { if (sun >= sunflowerCost) { selectedPlant = 'sunflower'; } }; game.down = function (x, y, obj) { if (selectedPlant && x >= gridStartX && x <= gridStartX + gridCols * gridCellWidth && y >= gridStartY && y <= gridStartY + gridRows * gridCellHeight) { var gridCol = Math.floor((x - gridStartX) / gridCellWidth); var gridRow = Math.floor((y - gridStartY) / gridCellHeight); // Check if position is valid and empty var positionEmpty = true; for (var i = 0; i < plants.length; i++) { if (plants[i].gridRow === gridRow && plants[i].gridCol === gridCol) { positionEmpty = false; break; } } if (positionEmpty && gridCol >= 0 && gridCol < gridCols && gridRow >= 0 && gridRow < gridRows) { var plant = null; if (selectedPlant === 'peashooter' && sun >= peashooterCost) { plant = new Peashooter(); sun -= peashooterCost; } else if (selectedPlant === 'sunflower' && sun >= sunflowerCost) { plant = new Sunflower(); sun -= sunflowerCost; } else if (selectedPlant === 'doublepea' && sun >= doublePeaCost) { plant = new DoublePeashooter(); sun -= doublePeaCost; } else if (selectedPlant === 'cherrybomb' && sun >= cherrybombCost) { plant = new CherryBomb(); sun -= cherrybombCost; } else if (selectedPlant === 'wallnut' && sun >= wallnutCost) { plant = new WallNut(); sun -= wallnutCost; } else if (selectedPlant === 'icepea' && sun >= icepeaCost) { plant = new IcePeashooter(); sun -= icepeaCost; } if (plant) { plant.x = gridStartX + gridCol * gridCellWidth; plant.y = gridStartY + gridRow * gridCellHeight; plant.gridRow = gridRow; plant.gridCol = gridCol; plants.push(plant); game.addChild(plant); sunText.setText('Sun: ' + sun); selectedPlant = null; } } } }; var zombiesStarted = false; // Track if zombies have started spawning // Game update function for Dynamic Levels game.update = function () { // Start zombie spawning and timer only after first plant is placed if (!zombiesStarted && plants.length > 0) { zombiesStarted = true; levelStartTime = LK.ticks; // Start level timer when first plant is placed lastZombieSpawn = LK.ticks; // Initialize spawn timer lastFastZombieSpawn = LK.ticks; // Initialize fast zombie spawn timer } // Spawn regular zombies with progressive and scaled difficulty - only after zombies started var gameProgress = Math.min(LK.ticks / levelDuration, 1); // Progress from 0 to 1 over level duration var progressiveSpawnRate = zombieSpawnRate * 2 - gameProgress * zombieSpawnRate; // Start at 2x spawn rate, end at 1x if (zombiesStarted && LK.ticks - lastZombieSpawn > progressiveSpawnRate) { var zombie = new Zombie(); var row = Math.floor(Math.random() * gridRows); zombie.x = 1900; zombie.y = gridStartY + row * gridCellHeight; zombie.gridRow = row; zombies.push(zombie); game.addChild(zombie); LK.getSound('zombisesi').play(); // Play zombie sound when spawning lastZombieSpawn = LK.ticks; } // Spawn fast zombies with progressive and scaled difficulty - only after zombies started var progressiveFastSpawnRate = fastZombieSpawnRate * 2 - gameProgress * fastZombieSpawnRate; // Start at 2x spawn rate, end at 1x if (zombiesStarted && LK.ticks - lastFastZombieSpawn > progressiveFastSpawnRate) { var fastZombie = new FastZombie(); var row = Math.floor(Math.random() * gridRows); fastZombie.x = 1900; fastZombie.y = gridStartY + row * gridCellHeight; fastZombie.gridRow = row; zombies.push(fastZombie); game.addChild(fastZombie); LK.getSound('zombisesi').play(); // Play zombie sound when spawning lastFastZombieSpawn = LK.ticks; } // Spawn tank zombies on higher levels with progressive spawn rate - only after zombies started if (zombiesStarted && levelNumber >= 4) { var baseTankSpawnRate = 180 - difficultyMultiplier * 30; var progressiveTankSpawnRate = baseTankSpawnRate * 2 - gameProgress * baseTankSpawnRate; // Start at 2x spawn rate, end at 1x if (LK.ticks % progressiveTankSpawnRate === 0 && LK.ticks > 0) { var tankZombie = new TankZombie(); var row = Math.floor(Math.random() * gridRows); tankZombie.x = 1900; tankZombie.y = gridStartY + row * gridCellHeight; tankZombie.gridRow = row; zombies.push(tankZombie); game.addChild(tankZombie); LK.getSound('zombisesi').play(); // Play zombie sound when spawning } } // Spawn shield zombies starting from level 5 with progressive spawn rate - only after zombies started if (zombiesStarted && levelNumber >= 5) { var baseShieldSpawnRate = 210 - difficultyMultiplier * 30; var progressiveShieldSpawnRate = baseShieldSpawnRate * 2 - gameProgress * baseShieldSpawnRate; // Start at 2x spawn rate, end at 1x if (LK.ticks % progressiveShieldSpawnRate === 0 && LK.ticks > 0) { var shieldZombie = new ShieldZombie(); var row = Math.floor(Math.random() * gridRows); shieldZombie.x = 1900; shieldZombie.y = gridStartY + row * gridCellHeight; shieldZombie.gridRow = row; zombies.push(shieldZombie); game.addChild(shieldZombie); LK.getSound('zombisesi').play(); // Play zombie sound when spawning } } // Spawn jumper zombies on even higher levels with progressive spawn rate - only after zombies started if (zombiesStarted && levelNumber >= 6) { var baseJumperSpawnRate = 150 - difficultyMultiplier * 20; var progressiveJumperSpawnRate = baseJumperSpawnRate * 2 - gameProgress * baseJumperSpawnRate; // Start at 2x spawn rate, end at 1x if (LK.ticks % progressiveJumperSpawnRate === 0 && LK.ticks > 0) { var jumperZombie = new JumperZombie(); var row = Math.floor(Math.random() * gridRows); jumperZombie.x = 1900; jumperZombie.y = gridStartY + row * gridCellHeight; jumperZombie.gridRow = row; zombies.push(jumperZombie); game.addChild(jumperZombie); LK.getSound('zombisesi').play(); // Play zombie sound when spawning } } // Spawn bomber zombies on levels 8, 9, and 10 with progressive spawn rate - only after zombies started if (zombiesStarted && levelNumber >= 8) { var baseBomberSpawnRate = Math.max(180, 240 - difficultyMultiplier * 25); var progressiveBomberSpawnRate = baseBomberSpawnRate * 2 - gameProgress * baseBomberSpawnRate; // Start at 2x spawn rate, end at 1x if (LK.ticks - lastZombieSpawn > progressiveBomberSpawnRate && LK.ticks % 300 === 0) { var bomberZombie = new BomberZombie(); var row = Math.floor(Math.random() * gridRows); bomberZombie.x = 1900; bomberZombie.y = gridStartY + row * gridCellHeight; bomberZombie.gridRow = row; zombies.push(bomberZombie); game.addChild(bomberZombie); LK.getSound('zombisesi').play(); // Play zombie sound when spawning } } // Update all game objects for (var i = plants.length - 1; i >= 0; i--) { plants[i].update(); } for (var j = zombies.length - 1; j >= 0; j--) { zombies[j].update(); } for (var k = bullets.length - 1; k >= 0; k--) { bullets[k].update(); } // Generate sun from sunflowers - only if sunflowers exist var sunflowerCount = 0; for (var s = 0; s < plants.length; s++) { if (plants[s] instanceof Sunflower) { sunflowerCount++; } } if (sunflowerCount > 0 && LK.ticks - lastSunGeneration > 600) { // Every 10 seconds, only if sunflowers exist sun += 10; sunText.setText('Sun: ' + sun); lastSunGeneration = LK.ticks; } // Intensified wave - spawn many more zombies rapidly when 1.5 minutes remain var timeRemaining = Math.max(0, levelDuration - timeElapsed); if (levelStartTime && timeRemaining <= 5400) { // 1.5 minutes = 90 seconds = 5400 ticks var intensifiedProgress = (5400 - timeRemaining) / 5400; // Progress from 0 to 1 over intensified period var baseIntensifiedSpawnRate = Math.max(8, zombieSpawnRate / 8); // Much faster in intensified wave var progressiveIntensifiedSpawnRate = baseIntensifiedSpawnRate * 3 - intensifiedProgress * baseIntensifiedSpawnRate * 2; // Start at 3x, end at 1x if (LK.ticks - lastZombieSpawn > progressiveIntensifiedSpawnRate) { var zombie = new Zombie(); var row = Math.floor(Math.random() * gridRows); zombie.x = 1900; zombie.y = gridStartY + row * gridCellHeight; zombie.gridRow = row; zombies.push(zombie); game.addChild(zombie); LK.getSound('zombisesi').play(); // Play zombie sound when spawning lastZombieSpawn = LK.ticks; } // Also spawn fast zombies more frequently during intensified wave var baseIntensifiedFastRate = Math.max(15, fastZombieSpawnRate / 8); var progressiveIntensifiedFastRate = baseIntensifiedFastRate * 3 - intensifiedProgress * baseIntensifiedFastRate * 2; // Start at 3x, end at 1x if (LK.ticks - lastFastZombieSpawn > progressiveIntensifiedFastRate) { var fastZombie = new FastZombie(); var row = Math.floor(Math.random() * gridRows); fastZombie.x = 1900; fastZombie.y = gridStartY + row * gridCellHeight; fastZombie.gridRow = row; zombies.push(fastZombie); game.addChild(fastZombie); LK.getSound('zombisesi').play(); // Play zombie sound when spawning lastFastZombieSpawn = LK.ticks; } } // Update timer display var timeElapsed = levelStartTime ? LK.ticks - levelStartTime : 0; var timeRemaining = Math.max(0, levelDuration - timeElapsed); var minutes = Math.floor(timeRemaining / 3600); var seconds = Math.floor(timeRemaining % 3600 / 60); var timeString = minutes + ':' + (seconds < 10 ? '0' : '') + seconds; timerText.setText('Time: ' + timeString); // Check win condition - survive for 2 minutes if (levelStartTime && timeElapsed >= levelDuration) { // Player survived 2 minutes - they win! unlockNextLevel(); // Unlock next level when current level is completed // Calculate stars based on house health: 3 stars = 80+%, 2 stars = 50+%, 1 star = any health remaining var stars = 1; if (houseHealth >= 80) { stars = 3; } else if (houseHealth >= 50) { stars = 2; } setLevelStars(levelNumber, stars); LK.showYouWin(); } }; } // Function to unlock next level function unlockNextLevel() { // Only unlock the next level if current level is completed and next level exists if (currentLevel === unlockedLevels && unlockedLevels < totalLevels) { unlockedLevels++; storage.unlockedLevels = unlockedLevels; // Save to storage if (levelButtons[unlockedLevels - 1]) { levelButtons[unlockedLevels - 1].setUnlocked(true); } } } // Function to set stars for a level function setLevelStars(levelNumber, stars) { levelProgress[levelNumber] = stars; storage.levelProgress = levelProgress; // Save to storage if (levelButtons[levelNumber - 1]) { levelButtons[levelNumber - 1].setStars(stars); } } // Pause menu variables var isPaused = false; var pauseMenu = null; var currentLevel = 0; // Function to show pause menu function showPauseMenu() { if (pauseMenu) return; // Already showing // Create pause menu background pauseMenu = new Container(); var pauseBg = LK.getAsset('paperBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.4 }); pauseBg.x = 0; pauseBg.y = 0; pauseBg.tint = 0x000000; pauseBg.alpha = 0.8; pauseMenu.addChild(pauseBg); // Pause title var pauseTitle = new Text2('PAUSED', { size: 60, fill: 0xFFFFFF }); pauseTitle.anchor.set(0.5, 0.5); pauseTitle.x = 0; pauseTitle.y = -100; pauseMenu.addChild(pauseTitle); // Restart button var restartButton = LK.getAsset('levelButton', { anchorX: 0.5, anchorY: 0.5 }); restartButton.x = 0; restartButton.y = 0; restartButton.tint = 0x4CAF50; pauseMenu.addChild(restartButton); var restartText = new Text2('RESTART', { size: 40, fill: 0x000000 }); restartText.anchor.set(0.5, 0.5); restartText.x = 0; restartText.y = 0; pauseMenu.addChild(restartText); // Resume button var resumeButton = LK.getAsset('levelButton', { anchorX: 0.5, anchorY: 0.5 }); resumeButton.x = 0; resumeButton.y = 120; resumeButton.tint = 0x2196F3; pauseMenu.addChild(resumeButton); var resumeText = new Text2('RESUME', { size: 40, fill: 0x000000 }); resumeText.anchor.set(0.5, 0.5); resumeText.x = 0; resumeText.y = 120; pauseMenu.addChild(resumeText); // Position pause menu at center of screen pauseMenu.x = 1024; pauseMenu.y = 1366; // Add event handlers restartButton.down = function () { LK.getSound('buttonClick').play(); hidePauseMenu(); // Restart current level if (currentLevel === 1) { game.removeChildren(); initializeLevel1(); } else if (currentLevel === 2) { game.removeChildren(); initializeLevel2(); } else if (currentLevel >= 3 && currentLevel <= 10) { game.removeChildren(); initializeDynamicLevel(currentLevel); } }; resumeButton.down = function () { LK.getSound('buttonClick').play(); hidePauseMenu(); }; game.addChild(pauseMenu); } // Function to hide pause menu function hidePauseMenu() { if (pauseMenu) { game.removeChild(pauseMenu); pauseMenu = null; } isPaused = false; } // Override game pause handler LK.on('pause', function () { if (currentLevel > 0) { // Only show pause menu during gameplay isPaused = true; showPauseMenu(); } }); // Simple game update for level selection screen game.update = function () { // No specific updates needed for level selection // All interactions are handled by level button click events };
/****
* Plugins
****/
var storage = LK.import("@upit/storage.v1");
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BomberZombie = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('bomberZombieBody', {
anchorX: 0.5,
anchorY: 1
});
body.y = 25;
var head = self.attachAsset('bomberZombieHead', {
anchorX: 0.5,
anchorY: 1
});
head.y = -10;
// Add explosive belt around waist
var belt = self.attachAsset('explosiveBelt', {
anchorX: 0.5,
anchorY: 0.5
});
belt.x = 0;
belt.y = 15;
// Add multiple bomb indicators on the belt
for (var i = 0; i < 3; i++) {
var bombIndicator = self.attachAsset('bombIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
bombIndicator.x = (i - 1) * 25; // Spread across belt
bombIndicator.y = 15;
// Add blinking animation to bomb indicators
var delay = i * 200; // Stagger the blinking
LK.setTimeout(function () {
var blinkTimer = LK.setInterval(function () {
bombIndicator.alpha = bombIndicator.alpha > 0.5 ? 0.3 : 1.0;
}, 800);
}, delay);
}
// Add fuse wire coming from belt
var fuse = self.attachAsset('fuseWire', {
anchorX: 0.5,
anchorY: 1
});
fuse.x = 30;
fuse.y = 5;
fuse.rotation = Math.PI / 6; // Slight angle
// Add sparking effect to fuse tip
var fuseTip = self.attachAsset('bombIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
fuseTip.x = 35;
fuseTip.y = -5;
fuseTip.scaleX = 0.5;
fuseTip.scaleY = 0.5;
// Animate fuse tip sparking
LK.setInterval(function () {
fuseTip.alpha = Math.random() > 0.5 ? 1.0 : 0.2;
fuseTip.tint = Math.random() > 0.5 ? 0xFFFF00 : 0xFF4500;
}, 300);
self.health = 80; // Lower health than regular zombie - easy to kill but dangerous
self.speed = 0.4; // Slightly faster than regular zombie
self.gridRow = 0;
self.isEating = false;
self.eatDamage = 8;
self.hasAttackedHouse = false;
self.hasExploded = false;
self.update = function () {
// Handle freeze effect
if (self.isFrozen && LK.ticks > self.frozenUntil) {
self.speed = self.originalSpeed;
self.isFrozen = false;
// Remove visual freeze effect but keep original orange tint
if (self.body) {
self.body.tint = 0xFF4500;
}
if (self.head) {
self.head.tint = 0xFF4500;
}
}
if (!self.isEating && !self.hasExploded) {
self.x -= self.speed;
}
// Explode when health drops to 0 or below
if (self.health <= 0 && !self.hasExploded) {
self.hasExploded = true;
// Explosive death - damages plants in 2x2 area around bomber
for (var i = plants.length - 1; i >= 0; i--) {
var plant = plants[i];
var distance = Math.sqrt(Math.pow(self.x - plant.x, 2) + Math.pow(self.y - plant.y, 2));
if (distance < 150) {
// Explosion radius
plant.health -= 40; // Significant damage to nearby plants
if (plant.health <= 0) {
plant.destroy();
plants.splice(i, 1);
}
}
}
// Flash explosion effect
LK.effects.flashScreen(0xFF4500, 500);
// Remove self
for (var z = zombies.length - 1; z >= 0; z--) {
if (zombies[z] === self) {
zombies.splice(z, 1);
break;
}
}
self.destroy();
return;
}
// Check if reached house
if (self.x <= 150 && !self.hasAttackedHouse && !self.hasExploded) {
houseHealth -= 20; // Extra damage when reaching house
healthText.setText('House Health: ' + houseHealth);
self.hasAttackedHouse = true;
// Explode at house for extra damage
self.hasExploded = true;
LK.effects.flashScreen(0xFF4500, 500);
self.destroy();
for (var z = zombies.length - 1; z >= 0; z--) {
if (zombies[z] === self) {
zombies.splice(z, 1);
break;
}
}
if (houseHealth <= 0) {
LK.showGameOver();
}
}
// Check collision with plants
if (!self.hasExploded) {
for (var i = plants.length - 1; i >= 0; i--) {
var plant = plants[i];
if (plant.gridRow === self.gridRow && Math.abs(self.x - plant.x) < 50) {
self.isEating = true;
// WallNut plants block zombies completely
if (plant instanceof WallNut) {
// Initialize damage timer if not set
if (self.lastDamageTime === undefined) {
self.lastDamageTime = LK.ticks;
}
// Deal damage every 35 ticks while touching WallNut
if (LK.ticks - self.lastDamageTime >= 35) {
plant.health -= 8;
self.lastDamageTime = LK.ticks;
}
// Zombie cannot move past WallNut until it's destroyed
if (plant.health > 0) {
// Push zombie back slightly to prevent overlap
self.x = plant.x + 55;
}
} else {
// Initialize damage timer if not set
if (self.lastDamageTime === undefined) {
self.lastDamageTime = LK.ticks;
}
// Deal damage every 50 ticks while touching other plants
if (LK.ticks - self.lastDamageTime >= 50) {
plant.health -= 20; // Bomber zombie destroys plants with explosive damage
self.lastDamageTime = LK.ticks;
}
}
if (plant.health <= 0) {
plant.destroy();
plants.splice(i, 1);
self.isEating = false;
self.lastDamageTime = undefined; // Reset damage timer when plant is destroyed
}
break;
}
}
// Reset damage timer when not eating
if (!self.isEating) {
self.lastDamageTime = undefined;
}
}
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('bulletCore', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5;
self.damage = 50;
self.row = 0;
self.update = function () {
self.x += self.speed;
// Check collision with zombies
for (var i = zombies.length - 1; i >= 0; i--) {
var zombie = zombies[i];
if (zombie.gridRow === self.row && Math.abs(self.x - zombie.x) < 30) {
// Handle ShieldZombie shield mechanics
if (zombie instanceof ShieldZombie && zombie.hasShield) {
zombie.shieldHealth -= self.damage;
// Shield takes damage instead of zombie
} else {
zombie.health -= self.damage;
}
if (zombie.health <= 0) {
zombie.destroy();
zombies.splice(i, 1);
}
self.destroy();
for (var j = bullets.length - 1; j >= 0; j--) {
if (bullets[j] === self) {
bullets.splice(j, 1);
break;
}
}
return;
}
}
// Remove if off screen
if (self.x > 2048) {
self.destroy();
for (var k = bullets.length - 1; k >= 0; k--) {
if (bullets[k] === self) {
bullets.splice(k, 1);
break;
}
}
}
};
return self;
});
var CherryBomb = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('cherryImage', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 50;
self.gridRow = 0;
self.gridCol = 0;
self.fuseTime = 180; // 3 seconds at 60fps
self.planted = false;
self.update = function () {
if (!self.planted) {
self.planted = true;
self.plantedTick = LK.ticks;
}
// Check if any zombie is touching the cherry bomb
var shouldExplode = false;
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var distance = Math.sqrt(Math.pow(self.x - zombie.x, 2) + Math.pow(self.y - zombie.y, 2));
if (distance < 50) {
// Touch distance - much smaller than explosion radius
shouldExplode = true;
break;
}
}
// Explode after fuse time OR when touched by zombie
if (shouldExplode || LK.ticks - self.plantedTick > self.fuseTime) {
LK.getSound('cherybomb').play(); // Play cherrybomb sound when exploding
// Deal damage to all zombies in 3x3 area around bomb
for (var i = zombies.length - 1; i >= 0; i--) {
var zombie = zombies[i];
var distance = Math.sqrt(Math.pow(self.x - zombie.x, 2) + Math.pow(self.y - zombie.y, 2));
if (distance < 200) {
// Check zombie type for damage calculation
if (zombie instanceof TankZombie) {
// Deal 75% percentage damage to tank zombies
var damageAmount = Math.floor(zombie.health * 0.75);
zombie.health -= damageAmount;
} else {
// Instantly kill all other zombie types (regular, fast, shield, jumper)
zombie.health = 0;
}
// Destroy zombie if health drops to or below 0
if (zombie.health <= 0) {
zombie.destroy();
zombies.splice(i, 1);
}
}
}
// Remove self
for (var j = plants.length - 1; j >= 0; j--) {
if (plants[j] === self) {
plants.splice(j, 1);
break;
}
}
self.destroy();
}
};
return self;
});
var DoublePeashooter = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('placedDoublePeaBody', {
anchorX: 0.5,
anchorY: 0.5
});
var cannon1 = self.attachAsset('peashooterCannon', {
anchorX: 0,
anchorY: 0.3
});
cannon1.x = 25;
cannon1.y = -10;
var cannon2 = self.attachAsset('peashooterCannon', {
anchorX: 0,
anchorY: 0.7
});
cannon2.x = 25;
cannon2.y = 10;
self.health = 100;
self.lastShot = 0;
self.shootDelay = 100; // Faster than regular peashooter
self.gridRow = 0;
self.gridCol = 0;
self.shoot = function () {
// Shoot two bullets
var bullet1 = new Bullet();
bullet1.x = self.x + 50;
bullet1.y = self.y - 10;
bullet1.row = self.gridRow;
bullets.push(bullet1);
game.addChild(bullet1);
var bullet2 = new Bullet();
bullet2.x = self.x + 50;
bullet2.y = self.y + 10;
bullet2.row = self.gridRow;
bullets.push(bullet2);
game.addChild(bullet2);
LK.getSound('peasshooter').play(); // Play peashooter sound when shooting
};
self.update = function () {
if (LK.ticks - self.lastShot > self.shootDelay) {
var zombieInRow = false;
for (var i = 0; i < zombies.length; i++) {
if (zombies[i].gridRow === self.gridRow && zombies[i].x > self.x) {
zombieInRow = true;
break;
}
}
if (zombieInRow) {
self.shoot();
self.lastShot = LK.ticks;
}
}
};
return self;
});
var FastZombie = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('zombieBody', {
anchorX: 0.5,
anchorY: 1
});
body.y = 25;
body.tint = 0xFF6666; // Reddish tint to distinguish from regular zombie
var head = self.attachAsset('zombieHead', {
anchorX: 0.5,
anchorY: 1
});
head.y = -10;
head.tint = 0xFF6666; // Reddish tint to distinguish from regular zombie
self.health = 100; // Lower health than regular zombie
self.speed = 0.8; // Faster than regular zombie
self.gridRow = 0;
self.isEating = false;
self.eatDamage = 15; // More damage when eating
self.hasAttackedHouse = false;
self.update = function () {
// Handle freeze effect
if (self.isFrozen && LK.ticks > self.frozenUntil) {
self.speed = self.originalSpeed;
self.isFrozen = false;
// Remove visual freeze effect but keep original red tint
if (self.body) {
self.body.tint = 0xFF6666;
}
if (self.head) {
self.head.tint = 0xFF6666;
}
}
if (!self.isEating) {
self.x -= self.speed;
}
// Check if reached house - zombie damages house once with 15 health
if (self.x <= 150 && !self.hasAttackedHouse) {
houseHealth -= 15;
healthText.setText('House Health: ' + houseHealth);
self.hasAttackedHouse = true;
// Destroy zombie after attacking house
self.destroy();
for (var z = zombies.length - 1; z >= 0; z--) {
if (zombies[z] === self) {
zombies.splice(z, 1);
break;
}
}
// Check game over condition
if (houseHealth <= 0) {
LK.showGameOver();
}
}
// Check collision with plants
for (var i = plants.length - 1; i >= 0; i--) {
var plant = plants[i];
if (plant.gridRow === self.gridRow && Math.abs(self.x - plant.x) < 50) {
self.isEating = true;
// WallNut plants block zombies completely
if (plant instanceof WallNut) {
// Initialize damage timer if not set
if (self.lastDamageTime === undefined) {
self.lastDamageTime = LK.ticks;
}
// Deal damage every 25 ticks (faster than regular zombie) while touching WallNut
if (LK.ticks - self.lastDamageTime >= 25) {
plant.health -= 15; // Fast zombie damages wallnut more
self.lastDamageTime = LK.ticks;
}
// Zombie cannot move past WallNut until it's destroyed
if (plant.health > 0) {
// Push zombie back slightly to prevent overlap
self.x = plant.x + 55;
}
} else {
// Initialize damage timer if not set
if (self.lastDamageTime === undefined) {
self.lastDamageTime = LK.ticks;
}
// Deal damage every 45 ticks while touching other plants
if (LK.ticks - self.lastDamageTime >= 45) {
plant.health -= 15; // Fast zombie destroys plants quickly
self.lastDamageTime = LK.ticks;
}
}
if (plant.health <= 0) {
plant.destroy();
plants.splice(i, 1);
self.isEating = false;
self.lastDamageTime = undefined; // Reset damage timer when plant is destroyed
}
break;
}
}
// Reset damage timer when not eating
if (!self.isEating) {
self.lastDamageTime = undefined;
}
};
return self;
});
var IceBullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('iceBulletCore', {
anchorX: 0.5,
anchorY: 0.5
});
// Add ice crystal effects around the main bullet
for (var i = 0; i < 4; i++) {
var crystal = self.attachAsset('iceCrystal', {
anchorX: 0.5,
anchorY: 0.5
});
var angle = i / 4 * Math.PI * 2;
crystal.x = Math.cos(angle) * 8;
crystal.y = Math.sin(angle) * 8;
crystal.rotation = angle;
crystal.scaleX = 0.3;
crystal.scaleY = 0.4;
crystal.alpha = 0.8;
// Add sparkle animation to crystals
tween(crystal, {
alpha: 0.3
}, {
duration: 300,
easing: tween.easeInOut
});
tween(crystal, {
alpha: 0.8
}, {
duration: 300,
easing: tween.easeInOut
});
}
self.speed = 6; // Slightly faster than regular bullet
self.damage = 40;
self.row = 0;
self.update = function () {
self.x += self.speed;
// Check collision with zombies
for (var i = zombies.length - 1; i >= 0; i--) {
var zombie = zombies[i];
if (zombie.gridRow === self.row && Math.abs(self.x - zombie.x) < 30) {
// Handle ShieldZombie shield mechanics
if (zombie instanceof ShieldZombie && zombie.hasShield) {
zombie.shieldHealth -= self.damage;
// Shield takes damage instead of zombie
} else {
zombie.health -= self.damage;
}
// Apply freeze effect - slow down zombie for 3 seconds
if (!zombie.isFrozen) {
zombie.originalSpeed = zombie.speed;
zombie.speed = zombie.speed * 0.3; // Slow to 30% speed
zombie.isFrozen = true;
zombie.frozenUntil = LK.ticks + 180; // 3 seconds at 60fps
// Visual freeze effect
if (zombie.body) {
zombie.body.tint = 0x87CEEB;
}
if (zombie.head) {
zombie.head.tint = 0x87CEEB;
}
}
if (zombie.health <= 0) {
zombie.destroy();
zombies.splice(i, 1);
}
self.destroy();
for (var j = bullets.length - 1; j >= 0; j--) {
if (bullets[j] === self) {
bullets.splice(j, 1);
break;
}
}
return;
}
}
// Remove if off screen
if (self.x > 2048) {
self.destroy();
for (var k = bullets.length - 1; k >= 0; k--) {
if (bullets[k] === self) {
bullets.splice(k, 1);
break;
}
}
}
};
return self;
});
var IcePeashooter = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('icePeashooterBody', {
anchorX: 0.5,
anchorY: 0.5
});
// Add ice crystals around the body for visual effect
for (var i = 0; i < 6; i++) {
var crystal = self.attachAsset('iceCrystal', {
anchorX: 0.5,
anchorY: 0.5
});
var angle = i / 6 * Math.PI * 2;
crystal.x = Math.cos(angle) * 30;
crystal.y = Math.sin(angle) * 30;
crystal.rotation = angle;
crystal.scaleX = 0.6;
crystal.scaleY = 0.8;
}
var cannon = self.attachAsset('peashooterCannon', {
anchorX: 0,
anchorY: 0.5
});
cannon.x = 25;
cannon.y = 0;
cannon.tint = 0x87CEEB; // Light blue tint for ice effect
self.health = 100;
self.lastShot = 0;
self.shootDelay = 120; // Faster than regular peashooter
self.gridRow = 0;
self.gridCol = 0;
self.shoot = function () {
var bullet = new IceBullet();
bullet.x = self.x + 50;
bullet.y = self.y;
bullet.row = self.gridRow;
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('peasshooter').play(); // Play peashooter sound when shooting
};
self.update = function () {
if (LK.ticks - self.lastShot > self.shootDelay) {
// Check if there's a zombie in this row
var zombieInRow = false;
for (var i = 0; i < zombies.length; i++) {
if (zombies[i].gridRow === self.gridRow && zombies[i].x > self.x) {
zombieInRow = true;
break;
}
}
if (zombieInRow) {
self.shoot();
self.lastShot = LK.ticks;
}
}
};
return self;
});
var JumperZombie = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('zombieBody', {
anchorX: 0.5,
anchorY: 1
});
body.y = 25;
body.tint = 0x00FF00; // Green tint for jumper zombie
var head = self.attachAsset('zombieHead', {
anchorX: 0.5,
anchorY: 1
});
head.y = -10;
head.tint = 0x00FF00;
self.health = 120;
self.speed = 0.5;
self.gridRow = 0;
self.isEating = false;
self.eatDamage = 12;
self.hasAttackedHouse = false;
self.canJump = true;
self.update = function () {
// Handle freeze effect
if (self.isFrozen && LK.ticks > self.frozenUntil) {
self.speed = self.originalSpeed;
self.isFrozen = false;
// Remove visual freeze effect but keep original green tint
if (self.body) {
self.body.tint = 0x00FF00;
}
if (self.head) {
self.head.tint = 0x00FF00;
}
}
if (!self.isEating) {
self.x -= self.speed;
}
// Jump over first plant encountered
if (self.canJump) {
for (var i = 0; i < plants.length; i++) {
var plant = plants[i];
if (plant.gridRow === self.gridRow && Math.abs(self.x - plant.x) < 80 && self.x > plant.x) {
self.x = plant.x - 80; // Jump past the plant
self.canJump = false;
break;
}
}
}
// Check if reached house
if (self.x <= 150 && !self.hasAttackedHouse) {
houseHealth -= 12;
healthText.setText('House Health: ' + houseHealth);
self.hasAttackedHouse = true;
self.destroy();
for (var z = zombies.length - 1; z >= 0; z--) {
if (zombies[z] === self) {
zombies.splice(z, 1);
break;
}
}
if (houseHealth <= 0) {
LK.showGameOver();
}
}
// Check collision with plants (only if can't jump)
if (!self.canJump) {
for (var i = plants.length - 1; i >= 0; i--) {
var plant = plants[i];
if (plant.gridRow === self.gridRow && Math.abs(self.x - plant.x) < 50) {
self.isEating = true;
// WallNut plants block zombies completely
if (plant instanceof WallNut) {
// Initialize damage timer if not set
if (self.lastDamageTime === undefined) {
self.lastDamageTime = LK.ticks;
}
// Deal damage every 30 ticks while touching WallNut
if (LK.ticks - self.lastDamageTime >= 30) {
plant.health -= 12; // Jumper zombie damages wallnut
self.lastDamageTime = LK.ticks;
}
// Zombie cannot move past WallNut until it's destroyed
if (plant.health > 0) {
// Push zombie back slightly to prevent overlap
self.x = plant.x + 55;
}
} else {
// Initialize damage timer if not set
if (self.lastDamageTime === undefined) {
self.lastDamageTime = LK.ticks;
}
// Deal damage every 60 ticks while touching other plants
if (LK.ticks - self.lastDamageTime >= 60) {
plant.health -= 15; // Jumper zombie destroys plants with high damage
self.lastDamageTime = LK.ticks;
}
}
if (plant.health <= 0) {
plant.destroy();
plants.splice(i, 1);
self.isEating = false;
self.lastDamageTime = undefined; // Reset damage timer when plant is destroyed
}
break;
}
}
// Reset damage timer when not eating
if (!self.isEating && !self.canJump) {
self.lastDamageTime = undefined;
}
}
};
return self;
});
// Event handlers
var LevelButton = Container.expand(function () {
var self = Container.call(this);
var buttonBg = self.attachAsset('levelButton', {
anchorX: 0.5,
anchorY: 0.5
});
self.levelNumber = 1;
self.setLevelNumber = function (number) {
self.levelNumber = number;
self.levelText.setText(number.toString());
};
self.isUnlocked = true;
self.stars = 0;
self.maxStars = 3;
// Create level number text
self.levelText = new Text2(self.levelNumber.toString(), {
size: 50,
fill: 0xFFFFFF
});
self.levelText.anchor.set(0.5, 0.5);
self.levelText.y = -30;
self.addChild(self.levelText);
// Create stars display
self.starContainers = [];
for (var i = 0; i < self.maxStars; i++) {
var star = LK.getAsset('starShape', {
anchorX: 0.5,
anchorY: 0.5
});
star.x = (i - 1) * 50;
star.y = 30;
star.tint = 0x666666; // Dark by default
self.addChild(star);
self.starContainers.push(star);
}
self.setStars = function (starCount) {
self.stars = starCount;
for (var i = 0; i < self.maxStars; i++) {
if (i < starCount) {
self.starContainers[i].tint = 0xFFD700; // Golden
} else {
self.starContainers[i].tint = 0x666666; // Dark
}
}
};
self.setUnlocked = function (unlocked) {
self.isUnlocked = unlocked;
if (unlocked) {
buttonBg.tint = 0xFFFFFF;
self.levelText.tint = 0xFFFFFF;
} else {
buttonBg.tint = 0x666666;
self.levelText.tint = 0x666666;
}
};
self.down = function (x, y, obj) {
if (self.isUnlocked) {
LK.getSound('buttonClick').play();
// Start the selected level
startLevel(self.levelNumber);
} else {
// Play a different sound or show feedback for locked levels
console.log('Level ' + self.levelNumber + ' is locked. Complete previous levels first.');
}
};
return self;
});
// Plant classes
var Peashooter = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('peashooterBody', {
anchorX: 0.5,
anchorY: 0.5
});
var cannon = self.attachAsset('peashooterCannon', {
anchorX: 0,
anchorY: 0.5
});
cannon.x = 25;
cannon.y = 0;
self.health = 100;
self.lastShot = 0;
self.shootDelay = 150; // 2.5 seconds at 60fps
self.gridRow = 0;
self.gridCol = 0;
self.shoot = function () {
var bullet = new Bullet();
bullet.x = self.x + 50;
bullet.y = self.y;
bullet.row = self.gridRow;
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('peasshooter').play(); // Play peashooter sound when shooting
};
self.update = function () {
if (LK.ticks - self.lastShot > self.shootDelay) {
// Check if there's a zombie in this row
var zombieInRow = false;
for (var i = 0; i < zombies.length; i++) {
if (zombies[i].gridRow === self.gridRow && zombies[i].x > self.x) {
zombieInRow = true;
break;
}
}
if (zombieInRow) {
self.shoot();
self.lastShot = LK.ticks;
}
}
};
return self;
});
var ShieldZombie = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('zombieBody', {
anchorX: 0.5,
anchorY: 1
});
body.y = 25;
body.tint = 0x8B4513; // Brown tint for shield zombie
var head = self.attachAsset('zombieHead', {
anchorX: 0.5,
anchorY: 1
});
head.y = -10;
head.tint = 0x8B4513;
// Create shield visual with rim, main body, and center decoration
var shieldRim = self.attachAsset('shieldRim', {
anchorX: 0.5,
anchorY: 0.5
});
shieldRim.x = 25; // Position shield in front of zombie
shieldRim.y = 0;
var shield = self.attachAsset('shieldVisual', {
anchorX: 0.5,
anchorY: 0.5
});
shield.x = 25; // Position shield in front of zombie
shield.y = 0;
var shieldCenter = self.attachAsset('shieldCenter', {
anchorX: 0.5,
anchorY: 0.5
});
shieldCenter.x = 25; // Position shield center decoration
shieldCenter.y = 0;
self.health = 150; // Zombie health (only takes damage after shield is destroyed)
self.shieldHealth = 150; // Shield health (must be destroyed first)
self.speed = 0.25; // Slower than regular zombie due to shield weight
self.gridRow = 0;
self.isEating = false;
self.eatDamage = 12;
self.hasAttackedHouse = false;
self.hasShield = true; // Track if shield is still active
self.update = function () {
// Handle freeze effect
if (self.isFrozen && LK.ticks > self.frozenUntil) {
self.speed = self.originalSpeed;
self.isFrozen = false;
// Remove visual freeze effect but keep original brown tint
if (self.body) {
self.body.tint = 0x8B4513;
}
if (self.head) {
self.head.tint = 0x8B4513;
}
}
if (!self.isEating) {
self.x -= self.speed;
}
// Check if shield is destroyed
if (self.hasShield && self.shieldHealth <= 0) {
self.hasShield = false;
shield.destroy(); // Remove shield visual
shieldRim.destroy(); // Remove shield rim
shieldCenter.destroy(); // Remove shield center decoration
self.speed = 0.4; // Increase speed when shield is lost
}
// Check if reached house
if (self.x <= 150 && !self.hasAttackedHouse) {
houseHealth -= 12;
healthText.setText('House Health: ' + houseHealth);
self.hasAttackedHouse = true;
self.destroy();
for (var z = zombies.length - 1; z >= 0; z--) {
if (zombies[z] === self) {
zombies.splice(z, 1);
break;
}
}
if (houseHealth <= 0) {
LK.showGameOver();
}
}
// Check collision with plants
for (var i = plants.length - 1; i >= 0; i--) {
var plant = plants[i];
if (plant.gridRow === self.gridRow && Math.abs(self.x - plant.x) < 50) {
self.isEating = true;
// WallNut plants block zombies completely
if (plant instanceof WallNut) {
// Initialize damage timer if not set
if (self.lastDamageTime === undefined) {
self.lastDamageTime = LK.ticks;
}
// Deal damage every 30 ticks while touching WallNut
if (LK.ticks - self.lastDamageTime >= 30) {
plant.health -= 12;
self.lastDamageTime = LK.ticks;
}
// Zombie cannot move past WallNut until it's destroyed
if (plant.health > 0) {
// Push zombie back slightly to prevent overlap
self.x = plant.x + 55;
}
} else {
// Initialize damage timer if not set
if (self.lastDamageTime === undefined) {
self.lastDamageTime = LK.ticks;
}
// Deal damage every 60 ticks while touching other plants
if (LK.ticks - self.lastDamageTime >= 60) {
plant.health -= 12; // Shield zombie destroys plants with moderate damage
self.lastDamageTime = LK.ticks;
}
}
if (plant.health <= 0) {
plant.destroy();
plants.splice(i, 1);
self.isEating = false;
self.lastDamageTime = undefined; // Reset damage timer when plant is destroyed
}
break;
}
}
// Reset damage timer when not eating
if (!self.isEating) {
self.lastDamageTime = undefined;
}
};
return self;
});
var Sunflower = Container.expand(function () {
var self = Container.call(this);
// Create petals around center
for (var i = 0; i < 8; i++) {
var petal = self.attachAsset('sunflowerPetal', {
anchorX: 0.5,
anchorY: 1
});
var angle = i / 8 * Math.PI * 2;
petal.x = Math.cos(angle) * 25;
petal.y = Math.sin(angle) * 25;
petal.rotation = angle + Math.PI / 2;
}
var center = self.attachAsset('sunflowerCenter', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.lastSunGeneration = 0;
self.sunDelay = 300; // 5 seconds at 60fps
self.gridRow = 0;
self.gridCol = 0;
self.generateSun = function () {
sun += 25;
sunText.setText('Sun: ' + sun);
self.lastSunGeneration = LK.ticks;
};
self.update = function () {
if (LK.ticks - self.lastSunGeneration > self.sunDelay) {
self.generateSun();
}
};
return self;
});
var TankZombie = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('zombieBody', {
anchorX: 0.5,
anchorY: 1
});
body.y = 25;
body.tint = 0x333333; // Dark gray tint for tank zombie
body.scaleX = 1.5;
body.scaleY = 1.5;
var head = self.attachAsset('zombieHead', {
anchorX: 0.5,
anchorY: 1
});
head.y = -15;
head.tint = 0x333333;
head.scaleX = 1.5;
head.scaleY = 1.5;
self.health = 450; // Very high health
self.speed = 0.2; // Very slow
self.gridRow = 0;
self.isEating = false;
self.eatDamage = 20;
self.hasAttackedHouse = false;
self.update = function () {
// Handle freeze effect
if (self.isFrozen && LK.ticks > self.frozenUntil) {
self.speed = self.originalSpeed;
self.isFrozen = false;
// Remove visual freeze effect but keep original dark gray tint
if (self.body) {
self.body.tint = 0x333333;
}
if (self.head) {
self.head.tint = 0x333333;
}
}
if (!self.isEating) {
self.x -= self.speed;
}
// Check if reached house
if (self.x <= 150 && !self.hasAttackedHouse) {
houseHealth -= 25;
healthText.setText('House Health: ' + houseHealth);
self.hasAttackedHouse = true;
self.destroy();
for (var z = zombies.length - 1; z >= 0; z--) {
if (zombies[z] === self) {
zombies.splice(z, 1);
break;
}
}
if (houseHealth <= 0) {
LK.showGameOver();
}
}
// Check collision with plants
for (var i = plants.length - 1; i >= 0; i--) {
var plant = plants[i];
if (plant.gridRow === self.gridRow && Math.abs(self.x - plant.x) < 50) {
self.isEating = true;
// WallNut plants block zombies completely
if (plant instanceof WallNut) {
// Initialize damage timer if not set
if (self.lastDamageTime === undefined) {
self.lastDamageTime = LK.ticks;
}
// Deal damage every 40 ticks (slower but high damage) while touching WallNut
if (LK.ticks - self.lastDamageTime >= 40) {
plant.health -= 20; // Tank zombie destroys wallnut with high damage
self.lastDamageTime = LK.ticks;
}
// Zombie cannot move past WallNut until it's destroyed
if (plant.health > 0) {
// Push zombie back slightly to prevent overlap
self.x = plant.x + 55;
}
} else {
// Initialize damage timer if not set
if (self.lastDamageTime === undefined) {
self.lastDamageTime = LK.ticks;
}
// Deal damage every 70 ticks while touching other plants
if (LK.ticks - self.lastDamageTime >= 70) {
plant.health -= 25; // Tank zombie destroys plants with massive damage
self.lastDamageTime = LK.ticks;
}
}
if (plant.health <= 0) {
plant.destroy();
plants.splice(i, 1);
self.isEating = false;
self.lastDamageTime = undefined; // Reset damage timer when plant is destroyed
}
break;
}
}
// Reset damage timer when not eating
if (!self.isEating) {
self.lastDamageTime = undefined;
}
};
return self;
});
var WallNut = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('placedWallNutBody', {
anchorX: 0.5,
anchorY: 0.5
});
body.scaleX = 1.3;
body.scaleY = 1.3;
self.health = 60; // Increased health, breaks after 6 zombie hits
self.gridRow = 0;
self.gridCol = 0;
self.update = function () {
// Check if being touched by any zombie and take continuous damage
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
if (zombie.gridRow === self.gridRow && Math.abs(zombie.x - self.x) < 50) {
// Initialize damage timer if not set
if (self.lastDamageTime === undefined) {
self.lastDamageTime = LK.ticks;
}
// Take damage every 30 ticks (0.5 seconds) from any zombie type
if (LK.ticks - self.lastDamageTime >= 30) {
var damageAmount = 10; // Base damage from regular zombies
if (zombie instanceof FastZombie) {
damageAmount = 15;
} else if (zombie instanceof TankZombie) {
damageAmount = 20;
} else if (zombie instanceof ShieldZombie) {
damageAmount = 12;
} else if (zombie instanceof JumperZombie) {
damageAmount = 12;
}
self.health -= damageAmount;
self.lastDamageTime = LK.ticks;
}
break; // Only take damage from one zombie at a time
}
}
// Reset damage timer when no zombies are touching
var anyZombieTouching = false;
for (var j = 0; j < zombies.length; j++) {
var zombie = zombies[j];
if (zombie.gridRow === self.gridRow && Math.abs(zombie.x - self.x) < 50) {
anyZombieTouching = true;
break;
}
}
if (!anyZombieTouching) {
self.lastDamageTime = undefined;
}
// Check if health decreased to play breaking sound
if (self.lastHealth === undefined) {
self.lastHealth = self.health;
}
if (self.lastHealth > self.health) {
LK.getSound('ceviz').play(); // Play walnut breaking sound when damaged
}
self.lastHealth = self.health;
// Check if wallnut is destroyed
if (self.health <= 0) {
// Play walnut breaking sound when completely destroyed
LK.getSound('ceviz').play();
// Remove from plants array and destroy
for (var k = plants.length - 1; k >= 0; k--) {
if (plants[k] === self) {
plants.splice(k, 1);
break;
}
}
self.destroy();
}
};
return self;
});
var Zombie = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('zombieBody', {
anchorX: 0.5,
anchorY: 1
});
body.y = 25;
var head = self.attachAsset('zombieHead', {
anchorX: 0.5,
anchorY: 1
});
head.y = -10;
self.health = 150;
self.speed = 0.3;
self.gridRow = 0;
self.isEating = false;
self.eatDamage = 10;
self.hasAttackedHouse = false;
self.update = function () {
// Handle freeze effect
if (self.isFrozen && LK.ticks > self.frozenUntil) {
self.speed = self.originalSpeed;
self.isFrozen = false;
// Remove visual freeze effect
if (self.body) {
self.body.tint = 0xFFFFFF;
}
if (self.head) {
self.head.tint = 0xFFFFFF;
}
}
if (!self.isEating) {
self.x -= self.speed;
}
// Check if reached house - zombie damages house once with 10 health
if (self.x <= 150 && !self.hasAttackedHouse) {
houseHealth -= 10;
healthText.setText('House Health: ' + houseHealth);
self.hasAttackedHouse = true;
// Destroy zombie after attacking house
self.destroy();
for (var z = zombies.length - 1; z >= 0; z--) {
if (zombies[z] === self) {
zombies.splice(z, 1);
break;
}
}
// Check game over condition
if (houseHealth <= 0) {
LK.showGameOver();
}
}
// Check collision with plants
for (var i = plants.length - 1; i >= 0; i--) {
var plant = plants[i];
if (plant.gridRow === self.gridRow && Math.abs(self.x - plant.x) < 50) {
self.isEating = true;
// WallNut plants block zombies completely
if (plant instanceof WallNut) {
// Initialize damage timer if not set
if (self.lastDamageTime === undefined) {
self.lastDamageTime = LK.ticks;
}
// Deal damage every 30 ticks (0.5 seconds) while touching WallNut
if (LK.ticks - self.lastDamageTime >= 30) {
plant.health -= 10; // Continuous damage to wallnut
self.lastDamageTime = LK.ticks;
}
// Zombie cannot move past WallNut until it's destroyed
if (plant.health > 0) {
// Push zombie back slightly to prevent overlap
self.x = plant.x + 55;
}
} else {
// Initialize damage timer if not set
if (self.lastDamageTime === undefined) {
self.lastDamageTime = LK.ticks;
}
// Deal damage every 60 ticks (1 second) while touching other plants
if (LK.ticks - self.lastDamageTime >= 60) {
plant.health -= 10; // Increased damage to destroy plants faster
self.lastDamageTime = LK.ticks;
}
}
if (plant.health <= 0) {
plant.destroy();
plants.splice(i, 1);
self.isEating = false;
self.lastDamageTime = undefined; // Reset damage timer when plant is destroyed
}
break;
}
}
// Reset damage timer when not eating
if (!self.isEating) {
self.lastDamageTime = undefined;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x5D4E37 // Darker brown paper background color
});
/****
* Game Code
****/
// Level selection screen variables
// Import storage for persistent level progression
var levelButtons = [];
var totalLevels = 10;
var unlockedLevels = storage.unlockedLevels || 1; // Load from storage or default to 1
var levelProgress = storage.levelProgress || {}; // Load from storage or default to empty
// Global game variables
var plants = [];
var zombies = [];
var bullets = [];
var sun = 150;
var sunText;
var houseHealth;
var healthText;
// Music control variables
var isMusicOn = storage.isMusicOn !== undefined ? storage.isMusicOn : true; // Default music on
var musicButton;
// Create brown paper background
var background = LK.getAsset('paperBackground', {
anchorX: 0,
anchorY: 0
});
game.addChild(background);
// Create title
var titleBg = LK.getAsset('titleBackground', {
anchorX: 0.5,
anchorY: 0.5
});
titleBg.x = 1024;
titleBg.y = 300;
game.addChild(titleBg);
// Create level buttons in a grid layout
var buttonsPerRow = 5;
var buttonSpacingX = 350;
var buttonSpacingY = 250;
var startX = 400;
var startY = 700;
for (var i = 0; i < totalLevels; i++) {
var levelButton = new LevelButton();
levelButton.setLevelNumber(i + 1);
// Calculate position in grid
var row = Math.floor(i / buttonsPerRow);
var col = i % buttonsPerRow;
levelButton.x = startX + col * buttonSpacingX;
levelButton.y = startY + row * buttonSpacingY;
// Set unlock status - only unlock levels up to the player's progress
levelButton.setUnlocked(i < unlockedLevels);
// Set stars from progress (if any)
if (levelProgress[i + 1]) {
levelButton.setStars(levelProgress[i + 1]);
} else {
levelButton.setStars(0);
}
levelButtons.push(levelButton);
game.addChild(levelButton);
}
// Create music toggle button with new design
musicButton = LK.getAsset('selectionBox', {
anchorX: 0.5,
anchorY: 0.5
});
musicButton.x = 1700;
musicButton.y = 200;
musicButton.scaleX = 0.5;
musicButton.scaleY = 0.5;
musicButton.tint = isMusicOn ? 0x4CAF50 : 0xF44336;
game.addChild(musicButton);
// Add music icon symbol
var musicIcon = new Text2(isMusicOn ? '♪' : '♪', {
size: 50,
fill: 0xFFFFFF
});
musicIcon.anchor.set(0.5, 0.5);
musicIcon.x = 1700;
musicIcon.y = 185;
musicIcon.alpha = isMusicOn ? 1.0 : 0.3;
game.addChild(musicIcon);
// Add music status text
var musicText = new Text2(isMusicOn ? 'ON' : 'OFF', {
size: 24,
fill: 0xFFFFFF
});
musicText.anchor.set(0.5, 0.5);
musicText.x = 1700;
musicText.y = 215;
game.addChild(musicText);
// Music button event handler
musicButton.down = function () {
LK.getSound('buttonClick').play();
isMusicOn = !isMusicOn;
storage.isMusicOn = isMusicOn; // Save to storage
musicButton.tint = isMusicOn ? 0x4CAF50 : 0xF44336;
musicIcon.alpha = isMusicOn ? 1.0 : 0.3;
musicText.setText(isMusicOn ? 'ON' : 'OFF');
if (isMusicOn) {
// Resume music if we're in a level
if (currentLevel > 0) {
LK.playMusic('game');
}
} else {
LK.stopMusic();
}
};
// Function to start a level
function startLevel(levelNumber) {
LK.getSound('levelSelect').play();
console.log('Starting level: ' + levelNumber);
// Set current level for pause functionality
currentLevel = levelNumber;
// Clear the level selection screen
game.removeChildren();
// Initialize the appropriate level
if (levelNumber === 1) {
initializeLevel1();
} else if (levelNumber === 2) {
initializeLevel2();
} else if (levelNumber >= 3 && levelNumber <= 10) {
// Initialize dynamic levels for 3-10
initializeDynamicLevel(levelNumber);
}
}
// Initialize Level 1 Plants vs Zombies game
function initializeLevel1() {
// Start playing game music only if music is enabled
if (isMusicOn) {
LK.playMusic('game');
}
// Game variables
houseHealth = 100;
sun = 150; // Starting money for level 1
plants = []; // Make plants global
zombies = []; // Make zombies global
bullets = []; // Make bullets global
var sunflowers = [];
var lastSunGeneration = 0;
var lastZombieSpawn = 0;
var levelStartTime = null; // Track when level started - will be set when first plant is placed
var levelDuration = 7200; // 2 minutes at 60fps (120 seconds * 60 = 7200 ticks)
// Grid setup
var gridRows = 10;
var gridCols = 12;
var gridCellWidth = 180;
var gridCellHeight = 120;
var gridStartX = 200;
var gridStartY = 500;
// UI elements
sunText = new Text2('Sun: ' + sun, {
size: 40,
fill: 0x000000
});
sunText.x = 1500;
sunText.y = 50;
game.addChild(sunText);
healthText = new Text2('House Health: ' + houseHealth, {
size: 40,
fill: 0xFF0000
});
healthText.x = 1500;
healthText.y = 100;
game.addChild(healthText);
// Add timer display
var timerText = new Text2('Time: 2:00', {
size: 40,
fill: 0x0000FF
});
timerText.x = 1500;
timerText.y = 200;
game.addChild(timerText);
// Create grid
for (var row = 0; row < gridRows; row++) {
for (var col = 0; col < gridCols; col++) {
var gridCell = LK.getAsset('gridTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.75,
scaleY: 0.75
});
gridCell.x = gridStartX + col * gridCellWidth;
gridCell.y = gridStartY + row * gridCellHeight;
gridCell.alpha = 0.8;
gridCell.tint = 0x8B7355;
game.addChild(gridCell);
}
}
// Plant selection buttons with proper spacing
var peashooterButton = LK.getAsset('selectionBox', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
peashooterButton.x = 400;
peashooterButton.y = 1800;
peashooterButton.tint = 0x006400;
game.addChild(peashooterButton);
// Add peashooter image to button
var peashooterImage = LK.getAsset('peashooterBody', {
anchorX: 0.5,
anchorY: 0.5
});
peashooterImage.x = 400;
peashooterImage.y = 1780;
peashooterImage.scaleX = 1.0;
peashooterImage.scaleY = 1.0;
game.addChild(peashooterImage);
var peashooterCost = 50 + Math.max(0, 1 - 5) * 5; // Level 1 cost: 50, same for levels 1-5
var peashooterText = new Text2(peashooterCost + ' Sun', {
size: 32,
fill: 0xFFFFFF
});
peashooterText.anchor.set(0.5, 0.5);
peashooterText.x = 400;
peashooterText.y = 1830;
game.addChild(peashooterText);
var sunflowerButton = LK.getAsset('selectionBox', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
sunflowerButton.x = 1000;
sunflowerButton.y = 1800;
sunflowerButton.tint = 0xB8860B;
game.addChild(sunflowerButton);
// Add sunflower image to button
var sunflowerImage = LK.getAsset('sunflowerCenter', {
anchorX: 0.5,
anchorY: 0.5
});
sunflowerImage.x = 1000;
sunflowerImage.y = 1780;
sunflowerImage.scaleX = 0.75;
sunflowerImage.scaleY = 0.75;
game.addChild(sunflowerImage);
var sunflowerCost = 50 + Math.max(0, 1 - 5) * 5; // Level 1 cost: 50, same for levels 1-5
var sunflowerText = new Text2(sunflowerCost + ' Sun', {
size: 32,
fill: 0xFFFFFF
});
sunflowerText.anchor.set(0.5, 0.5);
sunflowerText.x = 1000;
sunflowerText.y = 1830;
game.addChild(sunflowerText);
var selectedPlant = null;
// Event handlers
peashooterButton.down = function () {
if (sun >= peashooterCost) {
selectedPlant = 'peashooter';
}
};
sunflowerButton.down = function () {
if (sun >= sunflowerCost) {
selectedPlant = 'sunflower';
}
};
game.down = function (x, y, obj) {
if (selectedPlant && x >= gridStartX && x <= gridStartX + gridCols * gridCellWidth && y >= gridStartY && y <= gridStartY + gridRows * gridCellHeight) {
var gridCol = Math.floor((x - gridStartX) / gridCellWidth);
var gridRow = Math.floor((y - gridStartY) / gridCellHeight);
// Check if position is valid and empty
var positionEmpty = true;
for (var i = 0; i < plants.length; i++) {
if (plants[i].gridRow === gridRow && plants[i].gridCol === gridCol) {
positionEmpty = false;
break;
}
}
if (positionEmpty && gridCol >= 0 && gridCol < gridCols && gridRow >= 0 && gridRow < gridRows) {
var plant = null;
if (selectedPlant === 'peashooter' && sun >= peashooterCost) {
plant = new Peashooter();
sun -= peashooterCost;
} else if (selectedPlant === 'sunflower' && sun >= sunflowerCost) {
plant = new Sunflower();
sun -= sunflowerCost;
}
if (plant) {
plant.x = gridStartX + gridCol * gridCellWidth;
plant.y = gridStartY + gridRow * gridCellHeight;
plant.gridRow = gridRow;
plant.gridCol = gridCol;
plants.push(plant);
game.addChild(plant);
sunText.setText('Sun: ' + sun);
selectedPlant = null;
}
}
}
};
var zombiesStarted = false; // Track if zombies have started spawning
// Game update function
game.update = function () {
// Start zombie spawning and timer only after first plant is placed
if (!zombiesStarted && plants.length > 0) {
zombiesStarted = true;
levelStartTime = LK.ticks; // Start level timer when first plant is placed
lastZombieSpawn = LK.ticks; // Initialize spawn timer
}
// Spawn zombies only after they have been started - progressive spawn rate
// Start slow and gradually increase spawn rate over time
var gameProgress = Math.min(LK.ticks / 3600, 1); // Progress from 0 to 1 over 60 seconds
var currentSpawnRate = 180 - gameProgress * 120; // Start at 180 (3 seconds), end at 60 (1 second)
if (zombiesStarted && LK.ticks - lastZombieSpawn > currentSpawnRate) {
var zombie = new Zombie();
var row = Math.floor(Math.random() * gridRows);
zombie.x = 1900;
zombie.y = gridStartY + row * gridCellHeight;
zombie.gridRow = row;
zombies.push(zombie);
game.addChild(zombie);
LK.getSound('zombisesi').play(); // Play zombie sound when spawning
lastZombieSpawn = LK.ticks;
}
// Update all game objects
for (var i = plants.length - 1; i >= 0; i--) {
plants[i].update();
}
for (var j = zombies.length - 1; j >= 0; j--) {
zombies[j].update();
}
for (var k = bullets.length - 1; k >= 0; k--) {
bullets[k].update();
}
// Generate sun from sunflowers - only if sunflowers exist
var sunflowerCount = 0;
for (var s = 0; s < plants.length; s++) {
if (plants[s] instanceof Sunflower) {
sunflowerCount++;
}
}
if (sunflowerCount > 0 && LK.ticks - lastSunGeneration > 600) {
// Every 10 seconds, only if sunflowers exist
sun += 10;
sunText.setText('Sun: ' + sun);
lastSunGeneration = LK.ticks;
}
// Increased spawn when 1.5 minutes (90 seconds = 5400 ticks) remain - starts at 1.5 minutes left
var timeElapsed = levelStartTime ? LK.ticks - levelStartTime : 0;
var timeRemaining = Math.max(0, levelDuration - timeElapsed);
if (timeRemaining <= 5400) {
// 1.5 minutes = 90 seconds = 5400 ticks
var intensifiedSpawnRate = 30 - (5400 - timeRemaining) / 5400 * 20; // Start at 30 (0.5 seconds), end at 10 (0.17 seconds)
if (LK.ticks - lastZombieSpawn > intensifiedSpawnRate) {
var zombie = new Zombie();
var row = Math.floor(Math.random() * gridRows);
zombie.x = 1900;
zombie.y = gridStartY + row * gridCellHeight;
zombie.gridRow = row;
zombies.push(zombie);
game.addChild(zombie);
LK.getSound('zombisesi').play(); // Play zombie sound when spawning
lastZombieSpawn = LK.ticks;
}
}
// Update timer display
var timeElapsed = levelStartTime ? LK.ticks - levelStartTime : 0;
var timeRemaining = Math.max(0, levelDuration - timeElapsed);
var minutes = Math.floor(timeRemaining / 3600);
var seconds = Math.floor(timeRemaining % 3600 / 60);
var timeString = minutes + ':' + (seconds < 10 ? '0' : '') + seconds;
timerText.setText('Time: ' + timeString);
// Check win condition - survive for 2 minutes
if (levelStartTime && timeElapsed >= levelDuration) {
// Player survived 2 minutes - they win!
unlockNextLevel(); // Unlock next level when current level is completed
// Calculate stars based on house health: 3 stars = 80+%, 2 stars = 50+%, 1 star = any health remaining
var stars = 1;
if (houseHealth >= 80) {
stars = 3;
} else if (houseHealth >= 50) {
stars = 2;
}
setLevelStars(1, stars);
LK.showYouWin();
}
};
}
// Initialize Level 2 Plants vs Zombies game with more zombies and fast zombies
function initializeLevel2() {
// Start playing game music only if music is enabled
if (isMusicOn) {
LK.playMusic('game');
}
// Game variables
houseHealth = 100;
sun = 200; // Starting money for level 2 (50 more than level 1)
plants = []; // Make plants global
zombies = []; // Make zombies global
bullets = []; // Make bullets global
var levelStartTime = null; // Track when level started - will be set when first plant is placed
var levelDuration = 7200; // 2 minutes at 60fps (120 seconds * 60 = 7200 ticks)
// Night theme background
var nightBg = LK.getAsset('nightBackground', {
anchorX: 0,
anchorY: 0
});
game.addChild(nightBg);
var sunflowers = [];
var lastSunGeneration = 0;
var lastZombieSpawn = 0;
var lastFastZombieSpawn = 0;
// Grid setup
var gridRows = 10;
var gridCols = 12;
var gridCellWidth = 180;
var gridCellHeight = 120;
var gridStartX = 200;
var gridStartY = 500;
// UI elements
sunText = new Text2('Sun: ' + sun, {
size: 40,
fill: 0x000000
});
sunText.x = 1500;
sunText.y = 50;
game.addChild(sunText);
healthText = new Text2('House Health: ' + houseHealth, {
size: 40,
fill: 0xFF0000
});
healthText.x = 1500;
healthText.y = 100;
game.addChild(healthText);
// Level indicator
var levelText = new Text2('Level 2', {
size: 50,
fill: 0xFFFFFF
});
levelText.x = 1500;
levelText.y = 150;
game.addChild(levelText);
// Add timer display
var timerText = new Text2('Time: 2:00', {
size: 40,
fill: 0x0000FF
});
timerText.x = 1500;
timerText.y = 200;
game.addChild(timerText);
// Create grid
for (var row = 0; row < gridRows; row++) {
for (var col = 0; col < gridCols; col++) {
var gridCell = LK.getAsset('gridTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.75,
scaleY: 0.75
});
gridCell.x = gridStartX + col * gridCellWidth;
gridCell.y = gridStartY + row * gridCellHeight;
gridCell.alpha = 0.9;
gridCell.tint = 0x1A0D33;
game.addChild(gridCell);
}
}
// Plant selection buttons with proper spacing
var peashooterButton = LK.getAsset('selectionBox', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
peashooterButton.x = 350;
peashooterButton.y = 1800;
peashooterButton.tint = 0x006400;
game.addChild(peashooterButton);
// Add peashooter image to button
var peashooterImage = LK.getAsset('peashooterBody', {
anchorX: 0.5,
anchorY: 0.5
});
peashooterImage.x = 350;
peashooterImage.y = 1780;
peashooterImage.scaleX = 1.0;
peashooterImage.scaleY = 1.0;
game.addChild(peashooterImage);
var peashooterCost = 50 + Math.max(0, 2 - 5) * 5; // Level 2 cost: 50, same for levels 1-5
var peashooterText = new Text2(peashooterCost + ' Sun', {
size: 32,
fill: 0xFFFFFF
});
peashooterText.anchor.set(0.5, 0.5);
peashooterText.x = 350;
peashooterText.y = 1830;
game.addChild(peashooterText);
var sunflowerButton = LK.getAsset('selectionBox', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
sunflowerButton.x = 800;
sunflowerButton.y = 1800;
sunflowerButton.tint = 0xB8860B;
game.addChild(sunflowerButton);
// Add sunflower image to button
var sunflowerImage = LK.getAsset('sunflowerCenter', {
anchorX: 0.5,
anchorY: 0.5
});
sunflowerImage.x = 800;
sunflowerImage.y = 1780;
sunflowerImage.scaleX = 0.75;
sunflowerImage.scaleY = 0.75;
game.addChild(sunflowerImage);
var sunflowerCost = 50 + Math.max(0, 2 - 5) * 5; // Level 2 cost: 50, same for levels 1-5
var sunflowerText = new Text2(sunflowerCost + ' Sun', {
size: 32,
fill: 0xFFFFFF
});
sunflowerText.anchor.set(0.5, 0.5);
sunflowerText.x = 800;
sunflowerText.y = 1830;
game.addChild(sunflowerText);
var selectedPlant = null;
// Add CherryBomb button for level 2
var cherrybombButton = LK.getAsset('selectionBox', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
cherrybombButton.x = 1250;
cherrybombButton.y = 1800;
cherrybombButton.tint = 0xFF0000;
game.addChild(cherrybombButton);
var cherrybombImage = LK.getAsset('cherryImage', {
anchorX: 0.5,
anchorY: 0.5
});
cherrybombImage.x = 1250;
cherrybombImage.y = 1780;
cherrybombImage.scaleX = 0.75;
cherrybombImage.scaleY = 0.75;
game.addChild(cherrybombImage);
var cherrybombCost = 100 + Math.max(0, 2 - 5) * 10; // Level 2 cost: 100, same for levels 1-5
var cherrybombText = new Text2(cherrybombCost + ' Sun', {
size: 32,
fill: 0xFFFFFF
});
cherrybombText.anchor.set(0.5, 0.5);
cherrybombText.x = 1250;
cherrybombText.y = 1830;
game.addChild(cherrybombText);
cherrybombButton.down = function () {
if (sun >= cherrybombCost) {
selectedPlant = 'cherrybomb';
}
};
// Event handlers
peashooterButton.down = function () {
if (sun >= peashooterCost) {
selectedPlant = 'peashooter';
}
};
sunflowerButton.down = function () {
if (sun >= sunflowerCost) {
selectedPlant = 'sunflower';
}
};
game.down = function (x, y, obj) {
if (selectedPlant && x >= gridStartX && x <= gridStartX + gridCols * gridCellWidth && y >= gridStartY && y <= gridStartY + gridRows * gridCellHeight) {
var gridCol = Math.floor((x - gridStartX) / gridCellWidth);
var gridRow = Math.floor((y - gridStartY) / gridCellHeight);
// Check if position is valid and empty
var positionEmpty = true;
for (var i = 0; i < plants.length; i++) {
if (plants[i].gridRow === gridRow && plants[i].gridCol === gridCol) {
positionEmpty = false;
break;
}
}
if (positionEmpty && gridCol >= 0 && gridCol < gridCols && gridRow >= 0 && gridRow < gridRows) {
var plant = null;
if (selectedPlant === 'peashooter' && sun >= peashooterCost) {
plant = new Peashooter();
sun -= peashooterCost;
} else if (selectedPlant === 'sunflower' && sun >= sunflowerCost) {
plant = new Sunflower();
sun -= sunflowerCost;
} else if (selectedPlant === 'cherrybomb' && sun >= cherrybombCost) {
plant = new CherryBomb();
sun -= cherrybombCost;
}
if (plant) {
plant.x = gridStartX + gridCol * gridCellWidth;
plant.y = gridStartY + gridRow * gridCellHeight;
plant.gridRow = gridRow;
plant.gridCol = gridCol;
plants.push(plant);
game.addChild(plant);
sunText.setText('Sun: ' + sun);
selectedPlant = null;
}
}
}
};
var zombiesStarted = false; // Track if zombies have started spawning
// Game update function for Level 2
game.update = function () {
// Start zombie spawning and timer only after first plant is placed
if (!zombiesStarted && plants.length > 0) {
zombiesStarted = true;
levelStartTime = LK.ticks; // Start level timer when first plant is placed
lastZombieSpawn = LK.ticks; // Initialize spawn timer
lastFastZombieSpawn = LK.ticks; // Initialize fast zombie spawn timer
}
// Spawn regular zombies with progressive spawn rate - only after zombies started
var gameProgress = Math.min(LK.ticks / 5400, 1); // Progress from 0 to 1 over 90 seconds
var currentSpawnRate = 120 - gameProgress * 80; // Start at 120 (2 seconds), end at 40 (0.67 seconds)
if (zombiesStarted && LK.ticks - lastZombieSpawn > currentSpawnRate) {
var zombie = new Zombie();
var row = Math.floor(Math.random() * gridRows);
zombie.x = 1900;
zombie.y = gridStartY + row * gridCellHeight;
zombie.gridRow = row;
zombies.push(zombie);
game.addChild(zombie);
LK.getSound('zombisesi').play(); // Play zombie sound when spawning
lastZombieSpawn = LK.ticks;
}
// Spawn fast zombies with progressive spawn rate - only after zombies started
var fastSpawnRate = 180 - gameProgress * 120; // Start at 180 (3 seconds), end at 60 (1 second)
if (zombiesStarted && LK.ticks - lastFastZombieSpawn > fastSpawnRate) {
var fastZombie = new FastZombie();
var row = Math.floor(Math.random() * gridRows);
fastZombie.x = 1900;
fastZombie.y = gridStartY + row * gridCellHeight;
fastZombie.gridRow = row;
zombies.push(fastZombie);
game.addChild(fastZombie);
LK.getSound('zombisesi').play(); // Play zombie sound when spawning
lastFastZombieSpawn = LK.ticks;
}
// Spawn tank zombies with progressive spawn rate - only after zombies started
var tankSpawnRate = 300 - gameProgress * 150; // Start at 300 (5 seconds), end at 150 (2.5 seconds)
if (zombiesStarted && LK.ticks % tankSpawnRate === 0 && LK.ticks > 0) {
var tankZombie = new TankZombie();
var row = Math.floor(Math.random() * gridRows);
tankZombie.x = 1900;
tankZombie.y = gridStartY + row * gridCellHeight;
tankZombie.gridRow = row;
zombies.push(tankZombie);
game.addChild(tankZombie);
LK.getSound('zombisesi').play(); // Play zombie sound when spawning
}
// Update all game objects
for (var i = plants.length - 1; i >= 0; i--) {
plants[i].update();
}
for (var j = zombies.length - 1; j >= 0; j--) {
zombies[j].update();
}
for (var k = bullets.length - 1; k >= 0; k--) {
bullets[k].update();
}
// Generate sun from sunflowers - only if sunflowers exist
var sunflowerCount = 0;
for (var s = 0; s < plants.length; s++) {
if (plants[s] instanceof Sunflower) {
sunflowerCount++;
}
}
if (sunflowerCount > 0 && LK.ticks - lastSunGeneration > 600) {
// Every 10 seconds, only if sunflowers exist
sun += 10;
sunText.setText('Sun: ' + sun);
lastSunGeneration = LK.ticks;
}
// Increased spawn when 1.5 minutes (90 seconds = 5400 ticks) remain - starts at 1.5 minutes left
var timeElapsed = levelStartTime ? LK.ticks - levelStartTime : 0;
var timeRemaining = Math.max(0, levelDuration - timeElapsed);
if (timeRemaining <= 5400) {
// 1.5 minutes = 90 seconds = 5400 ticks
var intensifiedSpawnRate = 30 - (5400 - timeRemaining) / 5400 * 20; // Start at 30 (0.5 seconds), end at 10 (0.17 seconds)
if (LK.ticks - lastZombieSpawn > intensifiedSpawnRate) {
var zombie = new Zombie();
var row = Math.floor(Math.random() * gridRows);
zombie.x = 1900;
zombie.y = gridStartY + row * gridCellHeight;
zombie.gridRow = row;
zombies.push(zombie);
game.addChild(zombie);
LK.getSound('zombisesi').play(); // Play zombie sound when spawning
lastZombieSpawn = LK.ticks;
}
// Also spawn fast zombies more frequently during intensified wave
var intensifiedFastSpawnRate = 60 - (5400 - timeRemaining) / 5400 * 45; // Start at 60 (1 second), end at 15 (0.25 seconds)
if (LK.ticks - lastFastZombieSpawn > intensifiedFastSpawnRate) {
var fastZombie = new FastZombie();
var row = Math.floor(Math.random() * gridRows);
fastZombie.x = 1900;
fastZombie.y = gridStartY + row * gridCellHeight;
fastZombie.gridRow = row;
zombies.push(fastZombie);
game.addChild(fastZombie);
LK.getSound('zombisesi').play(); // Play zombie sound when spawning
lastFastZombieSpawn = LK.ticks;
}
}
// Update timer display
var timeElapsed = levelStartTime ? LK.ticks - levelStartTime : 0;
var timeRemaining = Math.max(0, levelDuration - timeElapsed);
var minutes = Math.floor(timeRemaining / 3600);
var seconds = Math.floor(timeRemaining % 3600 / 60);
var timeString = minutes + ':' + (seconds < 10 ? '0' : '') + seconds;
timerText.setText('Time: ' + timeString);
// Check win condition - survive for 2 minutes
if (levelStartTime && timeElapsed >= levelDuration) {
// Player survived 2 minutes - they win!
unlockNextLevel(); // Unlock next level when current level is completed
// Calculate stars based on house health: 3 stars = 80+%, 2 stars = 50+%, 1 star = any health remaining
var stars = 1;
if (houseHealth >= 80) {
stars = 3;
} else if (houseHealth >= 50) {
stars = 2;
}
setLevelStars(2, stars);
LK.showYouWin();
}
};
}
// Initialize Dynamic Level for levels 3 and beyond
function initializeDynamicLevel(levelNumber) {
// Start playing game music only if music is enabled
if (isMusicOn) {
LK.playMusic('game');
}
// Game variables
houseHealth = 100;
sun = 150 + (levelNumber - 1) * 50; // Progressive starting money: level 1=150, level 2=200, level 3=250, etc.
plants = []; // Make plants global
zombies = []; // Make zombies global
bullets = []; // Make bullets global
// Dynamic theme based on level
var themeBackgrounds = ['desertBackground', 'iceBackground', 'jungleBackground'];
var themeIndex = (levelNumber - 3) % themeBackgrounds.length;
var themeBg = LK.getAsset(themeBackgrounds[themeIndex], {
anchorX: 0,
anchorY: 0
});
game.addChild(themeBg);
var sunflowers = [];
var lastSunGeneration = 0;
var lastZombieSpawn = 0;
var lastFastZombieSpawn = 0;
// Dynamic difficulty scaling based on level - with increased spawn rates
var difficultyMultiplier = levelNumber - 2; // Starts at 1 for level 3
var zombieSpawnRate = Math.max(30, 90 - difficultyMultiplier * 15); // Much faster spawning, increased each level
var fastZombieSpawnRate = Math.max(60, 180 - difficultyMultiplier * 20); // Much more fast zombies
// Dynamic level duration based on level number
var levelDuration;
if (levelNumber === 3) {
levelDuration = 7200; // 2 minutes at 60fps (120 seconds * 60 = 7200 ticks)
} else if (levelNumber >= 4 && levelNumber <= 6) {
levelDuration = 9000; // 2.5 minutes at 60fps (150 seconds * 60 = 9000 ticks)
} else if (levelNumber >= 7 && levelNumber <= 10) {
levelDuration = 12600; // 3.5 minutes at 60fps (210 seconds * 60 = 12600 ticks)
}
var finalWaveStart = levelDuration - 1800; // Final wave starts 30 seconds before end
var levelStartTime = null; // Track when level started - will be set when first plant is placed
// Grid setup
var gridRows = 10;
var gridCols = 12;
var gridCellWidth = 180;
var gridCellHeight = 120;
var gridStartX = 200;
var gridStartY = 500;
// UI elements
sunText = new Text2('Sun: ' + sun, {
size: 40,
fill: 0x000000
});
sunText.x = 1500;
sunText.y = 50;
game.addChild(sunText);
healthText = new Text2('House Health: ' + houseHealth, {
size: 40,
fill: 0xFF0000
});
healthText.x = 1500;
healthText.y = 100;
game.addChild(healthText);
// Level indicator
var levelText = new Text2('Level ' + levelNumber, {
size: 50,
fill: 0xFFFFFF
});
levelText.x = 1500;
levelText.y = 150;
game.addChild(levelText);
// Add timer display
var initialTimeString;
if (levelNumber === 3) {
initialTimeString = 'Time: 2:00';
} else if (levelNumber >= 4 && levelNumber <= 6) {
initialTimeString = 'Time: 2:30';
} else if (levelNumber >= 7 && levelNumber <= 10) {
initialTimeString = 'Time: 3:30';
}
var timerText = new Text2(initialTimeString, {
size: 40,
fill: 0x0000FF
});
timerText.x = 1500;
timerText.y = 200;
game.addChild(timerText);
// Create grid
for (var row = 0; row < gridRows; row++) {
for (var col = 0; col < gridCols; col++) {
var gridCell = LK.getAsset('gridTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.75,
scaleY: 0.75
});
gridCell.x = gridStartX + col * gridCellWidth;
gridCell.y = gridStartY + row * gridCellHeight;
gridCell.alpha = 0.9;
// Apply theme-specific tinting with unique grid appearances
if (themeBackgrounds[themeIndex] === 'desertBackground') {
gridCell.tint = 0xD2691E; // Sandy orange for desert
} else if (themeBackgrounds[themeIndex] === 'iceBackground') {
gridCell.tint = 0x87CEEB; // Light blue for ice
} else if (themeBackgrounds[themeIndex] === 'jungleBackground') {
gridCell.tint = 0x228B22; // Forest green for jungle
}
game.addChild(gridCell);
}
}
// Plant selection buttons with proper spacing
var peashooterButton = LK.getAsset('selectionBox', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
peashooterButton.x = 300;
peashooterButton.y = 1800;
peashooterButton.tint = 0x006400;
game.addChild(peashooterButton);
// Add peashooter image to button
var peashooterImage = LK.getAsset('peashooterBody', {
anchorX: 0.5,
anchorY: 0.5
});
peashooterImage.x = 300;
peashooterImage.y = 1780;
peashooterImage.scaleX = 1.0;
peashooterImage.scaleY = 1.0;
game.addChild(peashooterImage);
var peashooterCost = 50 + Math.max(0, levelNumber - 5) * 5; // Cost same for levels 1-5, then increases by 5 per level
var peashooterText = new Text2(peashooterCost + ' Sun', {
size: 32,
fill: 0xFFFFFF
});
peashooterText.anchor.set(0.5, 0.5);
peashooterText.x = 300;
peashooterText.y = 1830;
game.addChild(peashooterText);
var sunflowerButton = LK.getAsset('selectionBox', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
sunflowerButton.x = 700;
sunflowerButton.y = 1800;
sunflowerButton.tint = 0xB8860B;
game.addChild(sunflowerButton);
// Add sunflower image to button
var sunflowerImage = LK.getAsset('sunflowerCenter', {
anchorX: 0.5,
anchorY: 0.5
});
sunflowerImage.x = 700;
sunflowerImage.y = 1780;
sunflowerImage.scaleX = 0.75;
sunflowerImage.scaleY = 0.75;
game.addChild(sunflowerImage);
var sunflowerCost = 50 + Math.max(0, levelNumber - 5) * 5; // Cost same for levels 1-5, then increases by 5 per level
var sunflowerText = new Text2(sunflowerCost + ' Sun', {
size: 32,
fill: 0xFFFFFF
});
sunflowerText.anchor.set(0.5, 0.5);
sunflowerText.x = 700;
sunflowerText.y = 1830;
game.addChild(sunflowerText);
var selectedPlant = null;
// Add more plant options based on level
if (levelNumber >= 3) {
// Add DoublePeashooter button
var doublePeaButton = LK.getAsset('selectionBox', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
doublePeaButton.x = 1100;
doublePeaButton.y = 1800;
doublePeaButton.tint = 0x00AA00;
game.addChild(doublePeaButton);
var doublePeaImage = LK.getAsset('placedDoublePeaBody', {
anchorX: 0.5,
anchorY: 0.5
});
doublePeaImage.x = 1100;
doublePeaImage.y = 1780;
game.addChild(doublePeaImage);
var doublePeaCost = 100 + Math.max(0, levelNumber - 5) * 10; // Cost same for levels 1-5, then increases by 10 per level
var doublePeaText = new Text2(doublePeaCost + ' Sun', {
size: 28,
fill: 0xFFFFFF
});
doublePeaText.anchor.set(0.5, 0.5);
doublePeaText.x = 1100;
doublePeaText.y = 1830;
game.addChild(doublePeaText);
doublePeaButton.down = function () {
if (sun >= doublePeaCost) {
selectedPlant = 'doublepea';
}
};
}
if (levelNumber >= 4) {
// Add CherryBomb button
var cherrybombButton = LK.getAsset('selectionBox', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
cherrybombButton.x = 1500;
cherrybombButton.y = 1800;
cherrybombButton.tint = 0xFF0000;
game.addChild(cherrybombButton);
var cherrybombImage = LK.getAsset('cherryImage', {
anchorX: 0.5,
anchorY: 0.5
});
cherrybombImage.x = 1500;
cherrybombImage.y = 1780;
cherrybombImage.scaleX = 0.6;
cherrybombImage.scaleY = 0.6;
game.addChild(cherrybombImage);
var cherrybombCost = 100 + Math.max(0, levelNumber - 5) * 10; // Cost same for levels 1-5, then increases by 10 per level
var cherrybombText = new Text2(cherrybombCost + ' Sun', {
size: 28,
fill: 0xFFFFFF
});
cherrybombText.anchor.set(0.5, 0.5);
cherrybombText.x = 1500;
cherrybombText.y = 1830;
game.addChild(cherrybombText);
cherrybombButton.down = function () {
if (sun >= cherrybombCost) {
selectedPlant = 'cherrybomb';
}
};
}
if (levelNumber >= 6) {
// Add WallNut button below CherryBomb button
var wallnutButton = LK.getAsset('selectionBox', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
wallnutButton.x = 1500;
wallnutButton.y = 2000;
wallnutButton.tint = 0x8B4513;
game.addChild(wallnutButton);
var wallnutImage = LK.getAsset('placedWallNutBody', {
anchorX: 0.5,
anchorY: 0.5
});
wallnutImage.x = 1500;
wallnutImage.y = 1980;
wallnutImage.scaleX = 0.9;
wallnutImage.scaleY = 0.9;
game.addChild(wallnutImage);
var wallnutCost = 50 + Math.max(0, levelNumber - 5) * 5; // Cost same for levels 1-5, then increases by 5 per level
var wallnutText = new Text2(wallnutCost + ' Sun', {
size: 32,
fill: 0xFFFFFF
});
wallnutText.anchor.set(0.5, 0.5);
wallnutText.x = 1500;
wallnutText.y = 2030;
game.addChild(wallnutText);
wallnutButton.down = function () {
if (sun >= wallnutCost) {
selectedPlant = 'wallnut';
}
};
}
if (levelNumber >= 8) {
// Add IcePeashooter button below Peashooter
var icepeaButton = LK.getAsset('selectionBox', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
icepeaButton.x = 300;
icepeaButton.y = 2000;
icepeaButton.tint = 0x87CEEB;
game.addChild(icepeaButton);
var icepeaImage = LK.getAsset('icePeashooterBody', {
anchorX: 0.5,
anchorY: 0.5
});
icepeaImage.x = 300;
icepeaImage.y = 1980;
icepeaImage.scaleX = 1.0;
icepeaImage.scaleY = 1.0;
game.addChild(icepeaImage);
var icepeaCost = 75 + Math.max(0, levelNumber - 5) * 8; // Cost same for levels 1-5, then increases by 8 per level
var icepeaText = new Text2(icepeaCost + ' Sun', {
size: 32,
fill: 0xFFFFFF
});
icepeaText.anchor.set(0.5, 0.5);
icepeaText.x = 300;
icepeaText.y = 2030;
game.addChild(icepeaText);
icepeaButton.down = function () {
if (sun >= icepeaCost) {
selectedPlant = 'icepea';
}
};
}
// Event handlers
peashooterButton.down = function () {
if (sun >= peashooterCost) {
selectedPlant = 'peashooter';
}
};
sunflowerButton.down = function () {
if (sun >= sunflowerCost) {
selectedPlant = 'sunflower';
}
};
game.down = function (x, y, obj) {
if (selectedPlant && x >= gridStartX && x <= gridStartX + gridCols * gridCellWidth && y >= gridStartY && y <= gridStartY + gridRows * gridCellHeight) {
var gridCol = Math.floor((x - gridStartX) / gridCellWidth);
var gridRow = Math.floor((y - gridStartY) / gridCellHeight);
// Check if position is valid and empty
var positionEmpty = true;
for (var i = 0; i < plants.length; i++) {
if (plants[i].gridRow === gridRow && plants[i].gridCol === gridCol) {
positionEmpty = false;
break;
}
}
if (positionEmpty && gridCol >= 0 && gridCol < gridCols && gridRow >= 0 && gridRow < gridRows) {
var plant = null;
if (selectedPlant === 'peashooter' && sun >= peashooterCost) {
plant = new Peashooter();
sun -= peashooterCost;
} else if (selectedPlant === 'sunflower' && sun >= sunflowerCost) {
plant = new Sunflower();
sun -= sunflowerCost;
} else if (selectedPlant === 'doublepea' && sun >= doublePeaCost) {
plant = new DoublePeashooter();
sun -= doublePeaCost;
} else if (selectedPlant === 'cherrybomb' && sun >= cherrybombCost) {
plant = new CherryBomb();
sun -= cherrybombCost;
} else if (selectedPlant === 'wallnut' && sun >= wallnutCost) {
plant = new WallNut();
sun -= wallnutCost;
} else if (selectedPlant === 'icepea' && sun >= icepeaCost) {
plant = new IcePeashooter();
sun -= icepeaCost;
}
if (plant) {
plant.x = gridStartX + gridCol * gridCellWidth;
plant.y = gridStartY + gridRow * gridCellHeight;
plant.gridRow = gridRow;
plant.gridCol = gridCol;
plants.push(plant);
game.addChild(plant);
sunText.setText('Sun: ' + sun);
selectedPlant = null;
}
}
}
};
var zombiesStarted = false; // Track if zombies have started spawning
// Game update function for Dynamic Levels
game.update = function () {
// Start zombie spawning and timer only after first plant is placed
if (!zombiesStarted && plants.length > 0) {
zombiesStarted = true;
levelStartTime = LK.ticks; // Start level timer when first plant is placed
lastZombieSpawn = LK.ticks; // Initialize spawn timer
lastFastZombieSpawn = LK.ticks; // Initialize fast zombie spawn timer
}
// Spawn regular zombies with progressive and scaled difficulty - only after zombies started
var gameProgress = Math.min(LK.ticks / levelDuration, 1); // Progress from 0 to 1 over level duration
var progressiveSpawnRate = zombieSpawnRate * 2 - gameProgress * zombieSpawnRate; // Start at 2x spawn rate, end at 1x
if (zombiesStarted && LK.ticks - lastZombieSpawn > progressiveSpawnRate) {
var zombie = new Zombie();
var row = Math.floor(Math.random() * gridRows);
zombie.x = 1900;
zombie.y = gridStartY + row * gridCellHeight;
zombie.gridRow = row;
zombies.push(zombie);
game.addChild(zombie);
LK.getSound('zombisesi').play(); // Play zombie sound when spawning
lastZombieSpawn = LK.ticks;
}
// Spawn fast zombies with progressive and scaled difficulty - only after zombies started
var progressiveFastSpawnRate = fastZombieSpawnRate * 2 - gameProgress * fastZombieSpawnRate; // Start at 2x spawn rate, end at 1x
if (zombiesStarted && LK.ticks - lastFastZombieSpawn > progressiveFastSpawnRate) {
var fastZombie = new FastZombie();
var row = Math.floor(Math.random() * gridRows);
fastZombie.x = 1900;
fastZombie.y = gridStartY + row * gridCellHeight;
fastZombie.gridRow = row;
zombies.push(fastZombie);
game.addChild(fastZombie);
LK.getSound('zombisesi').play(); // Play zombie sound when spawning
lastFastZombieSpawn = LK.ticks;
}
// Spawn tank zombies on higher levels with progressive spawn rate - only after zombies started
if (zombiesStarted && levelNumber >= 4) {
var baseTankSpawnRate = 180 - difficultyMultiplier * 30;
var progressiveTankSpawnRate = baseTankSpawnRate * 2 - gameProgress * baseTankSpawnRate; // Start at 2x spawn rate, end at 1x
if (LK.ticks % progressiveTankSpawnRate === 0 && LK.ticks > 0) {
var tankZombie = new TankZombie();
var row = Math.floor(Math.random() * gridRows);
tankZombie.x = 1900;
tankZombie.y = gridStartY + row * gridCellHeight;
tankZombie.gridRow = row;
zombies.push(tankZombie);
game.addChild(tankZombie);
LK.getSound('zombisesi').play(); // Play zombie sound when spawning
}
}
// Spawn shield zombies starting from level 5 with progressive spawn rate - only after zombies started
if (zombiesStarted && levelNumber >= 5) {
var baseShieldSpawnRate = 210 - difficultyMultiplier * 30;
var progressiveShieldSpawnRate = baseShieldSpawnRate * 2 - gameProgress * baseShieldSpawnRate; // Start at 2x spawn rate, end at 1x
if (LK.ticks % progressiveShieldSpawnRate === 0 && LK.ticks > 0) {
var shieldZombie = new ShieldZombie();
var row = Math.floor(Math.random() * gridRows);
shieldZombie.x = 1900;
shieldZombie.y = gridStartY + row * gridCellHeight;
shieldZombie.gridRow = row;
zombies.push(shieldZombie);
game.addChild(shieldZombie);
LK.getSound('zombisesi').play(); // Play zombie sound when spawning
}
}
// Spawn jumper zombies on even higher levels with progressive spawn rate - only after zombies started
if (zombiesStarted && levelNumber >= 6) {
var baseJumperSpawnRate = 150 - difficultyMultiplier * 20;
var progressiveJumperSpawnRate = baseJumperSpawnRate * 2 - gameProgress * baseJumperSpawnRate; // Start at 2x spawn rate, end at 1x
if (LK.ticks % progressiveJumperSpawnRate === 0 && LK.ticks > 0) {
var jumperZombie = new JumperZombie();
var row = Math.floor(Math.random() * gridRows);
jumperZombie.x = 1900;
jumperZombie.y = gridStartY + row * gridCellHeight;
jumperZombie.gridRow = row;
zombies.push(jumperZombie);
game.addChild(jumperZombie);
LK.getSound('zombisesi').play(); // Play zombie sound when spawning
}
}
// Spawn bomber zombies on levels 8, 9, and 10 with progressive spawn rate - only after zombies started
if (zombiesStarted && levelNumber >= 8) {
var baseBomberSpawnRate = Math.max(180, 240 - difficultyMultiplier * 25);
var progressiveBomberSpawnRate = baseBomberSpawnRate * 2 - gameProgress * baseBomberSpawnRate; // Start at 2x spawn rate, end at 1x
if (LK.ticks - lastZombieSpawn > progressiveBomberSpawnRate && LK.ticks % 300 === 0) {
var bomberZombie = new BomberZombie();
var row = Math.floor(Math.random() * gridRows);
bomberZombie.x = 1900;
bomberZombie.y = gridStartY + row * gridCellHeight;
bomberZombie.gridRow = row;
zombies.push(bomberZombie);
game.addChild(bomberZombie);
LK.getSound('zombisesi').play(); // Play zombie sound when spawning
}
}
// Update all game objects
for (var i = plants.length - 1; i >= 0; i--) {
plants[i].update();
}
for (var j = zombies.length - 1; j >= 0; j--) {
zombies[j].update();
}
for (var k = bullets.length - 1; k >= 0; k--) {
bullets[k].update();
}
// Generate sun from sunflowers - only if sunflowers exist
var sunflowerCount = 0;
for (var s = 0; s < plants.length; s++) {
if (plants[s] instanceof Sunflower) {
sunflowerCount++;
}
}
if (sunflowerCount > 0 && LK.ticks - lastSunGeneration > 600) {
// Every 10 seconds, only if sunflowers exist
sun += 10;
sunText.setText('Sun: ' + sun);
lastSunGeneration = LK.ticks;
}
// Intensified wave - spawn many more zombies rapidly when 1.5 minutes remain
var timeRemaining = Math.max(0, levelDuration - timeElapsed);
if (levelStartTime && timeRemaining <= 5400) {
// 1.5 minutes = 90 seconds = 5400 ticks
var intensifiedProgress = (5400 - timeRemaining) / 5400; // Progress from 0 to 1 over intensified period
var baseIntensifiedSpawnRate = Math.max(8, zombieSpawnRate / 8); // Much faster in intensified wave
var progressiveIntensifiedSpawnRate = baseIntensifiedSpawnRate * 3 - intensifiedProgress * baseIntensifiedSpawnRate * 2; // Start at 3x, end at 1x
if (LK.ticks - lastZombieSpawn > progressiveIntensifiedSpawnRate) {
var zombie = new Zombie();
var row = Math.floor(Math.random() * gridRows);
zombie.x = 1900;
zombie.y = gridStartY + row * gridCellHeight;
zombie.gridRow = row;
zombies.push(zombie);
game.addChild(zombie);
LK.getSound('zombisesi').play(); // Play zombie sound when spawning
lastZombieSpawn = LK.ticks;
}
// Also spawn fast zombies more frequently during intensified wave
var baseIntensifiedFastRate = Math.max(15, fastZombieSpawnRate / 8);
var progressiveIntensifiedFastRate = baseIntensifiedFastRate * 3 - intensifiedProgress * baseIntensifiedFastRate * 2; // Start at 3x, end at 1x
if (LK.ticks - lastFastZombieSpawn > progressiveIntensifiedFastRate) {
var fastZombie = new FastZombie();
var row = Math.floor(Math.random() * gridRows);
fastZombie.x = 1900;
fastZombie.y = gridStartY + row * gridCellHeight;
fastZombie.gridRow = row;
zombies.push(fastZombie);
game.addChild(fastZombie);
LK.getSound('zombisesi').play(); // Play zombie sound when spawning
lastFastZombieSpawn = LK.ticks;
}
}
// Update timer display
var timeElapsed = levelStartTime ? LK.ticks - levelStartTime : 0;
var timeRemaining = Math.max(0, levelDuration - timeElapsed);
var minutes = Math.floor(timeRemaining / 3600);
var seconds = Math.floor(timeRemaining % 3600 / 60);
var timeString = minutes + ':' + (seconds < 10 ? '0' : '') + seconds;
timerText.setText('Time: ' + timeString);
// Check win condition - survive for 2 minutes
if (levelStartTime && timeElapsed >= levelDuration) {
// Player survived 2 minutes - they win!
unlockNextLevel(); // Unlock next level when current level is completed
// Calculate stars based on house health: 3 stars = 80+%, 2 stars = 50+%, 1 star = any health remaining
var stars = 1;
if (houseHealth >= 80) {
stars = 3;
} else if (houseHealth >= 50) {
stars = 2;
}
setLevelStars(levelNumber, stars);
LK.showYouWin();
}
};
}
// Function to unlock next level
function unlockNextLevel() {
// Only unlock the next level if current level is completed and next level exists
if (currentLevel === unlockedLevels && unlockedLevels < totalLevels) {
unlockedLevels++;
storage.unlockedLevels = unlockedLevels; // Save to storage
if (levelButtons[unlockedLevels - 1]) {
levelButtons[unlockedLevels - 1].setUnlocked(true);
}
}
}
// Function to set stars for a level
function setLevelStars(levelNumber, stars) {
levelProgress[levelNumber] = stars;
storage.levelProgress = levelProgress; // Save to storage
if (levelButtons[levelNumber - 1]) {
levelButtons[levelNumber - 1].setStars(stars);
}
}
// Pause menu variables
var isPaused = false;
var pauseMenu = null;
var currentLevel = 0;
// Function to show pause menu
function showPauseMenu() {
if (pauseMenu) return; // Already showing
// Create pause menu background
pauseMenu = new Container();
var pauseBg = LK.getAsset('paperBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.4
});
pauseBg.x = 0;
pauseBg.y = 0;
pauseBg.tint = 0x000000;
pauseBg.alpha = 0.8;
pauseMenu.addChild(pauseBg);
// Pause title
var pauseTitle = new Text2('PAUSED', {
size: 60,
fill: 0xFFFFFF
});
pauseTitle.anchor.set(0.5, 0.5);
pauseTitle.x = 0;
pauseTitle.y = -100;
pauseMenu.addChild(pauseTitle);
// Restart button
var restartButton = LK.getAsset('levelButton', {
anchorX: 0.5,
anchorY: 0.5
});
restartButton.x = 0;
restartButton.y = 0;
restartButton.tint = 0x4CAF50;
pauseMenu.addChild(restartButton);
var restartText = new Text2('RESTART', {
size: 40,
fill: 0x000000
});
restartText.anchor.set(0.5, 0.5);
restartText.x = 0;
restartText.y = 0;
pauseMenu.addChild(restartText);
// Resume button
var resumeButton = LK.getAsset('levelButton', {
anchorX: 0.5,
anchorY: 0.5
});
resumeButton.x = 0;
resumeButton.y = 120;
resumeButton.tint = 0x2196F3;
pauseMenu.addChild(resumeButton);
var resumeText = new Text2('RESUME', {
size: 40,
fill: 0x000000
});
resumeText.anchor.set(0.5, 0.5);
resumeText.x = 0;
resumeText.y = 120;
pauseMenu.addChild(resumeText);
// Position pause menu at center of screen
pauseMenu.x = 1024;
pauseMenu.y = 1366;
// Add event handlers
restartButton.down = function () {
LK.getSound('buttonClick').play();
hidePauseMenu();
// Restart current level
if (currentLevel === 1) {
game.removeChildren();
initializeLevel1();
} else if (currentLevel === 2) {
game.removeChildren();
initializeLevel2();
} else if (currentLevel >= 3 && currentLevel <= 10) {
game.removeChildren();
initializeDynamicLevel(currentLevel);
}
};
resumeButton.down = function () {
LK.getSound('buttonClick').play();
hidePauseMenu();
};
game.addChild(pauseMenu);
}
// Function to hide pause menu
function hidePauseMenu() {
if (pauseMenu) {
game.removeChild(pauseMenu);
pauseMenu = null;
}
isPaused = false;
}
// Override game pause handler
LK.on('pause', function () {
if (currentLevel > 0) {
// Only show pause menu during gameplay
isPaused = true;
showPauseMenu();
}
});
// Simple game update for level selection screen
game.update = function () {
// No specific updates needed for level selection
// All interactions are handled by level button click events
};
Kahverengi sayfa yaprağı. In-Game asset. 2d. High contrast. No shadows
Kağıdın içinde plants vs zombies yazsin
Plant vs zombies ay çiçeği. In-Game asset. 2d. High contrast. No shadows
boş buton. In-Game asset. 2d. High contrast. No shadows
plans vs zombies double shoters. In-Game asset. 2d. High contrast. No shadows
çöl. In-Game asset. 2d. High contrast. No shadows
buz arkaplan. In-Game asset. 2d. High contrast. No shadows
gece gökyüzü. In-Game asset. 2d. High contrast. No shadows
orman. In-Game asset. 2d. High contrast. No shadows
toprak arka plan. In-Game asset. 2d. High contrast. No shadows
plants vs zombies ceviz. In-Game asset. 2d. High contrast. No shadows
ıcepeasshooter plants vs zombies. In-Game asset. 2d. High contrast. No shadows
kankanlı zombi plants vs zombies. In-Game asset. 2d. High contrast. No shadows
plants vs zombies olü ceviz. In-Game asset. 2d. High contrast. No shadows
bombacı zombi plants vs zombies. In-Game asset. 2d. High contrast. No shadows