/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var AllStarZombie = Container.expand(function () { var self = Container.call(this); var zombieGraphics = self.attachAsset('zombie', { anchorX: 0.5, anchorY: 0.5 }); // Tint to differentiate from normal zombie zombieGraphics.tint = 0x888888; self.health = 200; self.maxHealth = 200; self.speed = -1.2; // Faster than normal zombie self.damage = 40; self.attackTimer = 0; self.attackDelay = 45; // Faster attack self.row = 0; self.attacking = false; self.update = function () { if (!self.attacking) { self.x += self.speed; } else { self.attackTimer++; if (self.attackTimer >= self.attackDelay) { self.attack(); self.attackTimer = 0; } } }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xff0000, 200); LK.getSound('zombie_hit').play(); if (self.health <= 0) { // Remove from enemies array for (var i = 0; i < enemies.length; i++) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } // Award resources for killing all star zombie resources += 200; updateResourcesDisplay(); self.destroy(); } }; self.attack = function () { // Find plant to attack for (var i = 0; i < plants.length; i++) { var plant = plants[i]; if (plant.row === self.row && Math.abs(plant.x - self.x) <= 50) { // Skip spikeweed - zombies ignore it completely if (plant instanceof Spikeweed) { continue; } plant.health -= self.damage; LK.effects.flashObject(plant, 0xff0000, 200); if (plant.health <= 0) { plants.splice(i, 1); plant.destroy(); grid[plant.row][plant.col].occupied = false; self.attacking = false; } break; } } }; return self; }); var CherryBomb = Container.expand(function () { var self = Container.call(this); var bombGraphics = self.attachAsset('cherryBomb', { anchorX: 0.5, anchorY: 0.5 }); self.cost = 150; self.health = 50; self.maxHealth = 50; self.fuseTimer = 0; self.fuseDelay = 180; // 3 seconds self.update = function () { self.fuseTimer++; if (self.fuseTimer >= self.fuseDelay) { self.explode(); } }; self.explode = function () { var explosion = game.addChild(LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y })); // Damage all enemies in 3x3 grid area var explosionRow = self.row; var explosionCol = self.col; for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; var enemyRow = enemy.row; var enemyCol = Math.floor((enemy.x - GRID_START_X) / CELL_SIZE); // Check if enemy is within 3x3 grid area (1 cell in each direction) if (enemyRow >= explosionRow - 1 && enemyRow <= explosionRow + 1 && enemyCol >= explosionCol - 1 && enemyCol <= explosionCol + 1) { enemy.takeDamage(100); } } LK.getSound('explode').play(); // Remove explosion after animation tween(explosion, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 500, onFinish: function onFinish() { explosion.destroy(); } }); // Remove self for (var j = 0; j < plants.length; j++) { if (plants[j] === self) { plants.splice(j, 1); // Clear grid occupied status grid[self.row][self.col].occupied = false; break; } } self.destroy(); }; return self; }); var Chomper = Container.expand(function () { var self = Container.call(this); var chomperGraphics = self.attachAsset('chomper', { anchorX: 0.5, anchorY: 0.5 }); self.cost = 150; self.health = 200; self.maxHealth = 200; self.row = 0; self.col = 0; self.attackTimer = 0; self.attackDelay = 1200; // 20 seconds at 60fps self.canAttack = true; self.update = function () { if (self.attackTimer > 0) { self.attackTimer--; if (self.attackTimer === 0) { self.canAttack = true; // Change back to normal color when ready (no tint) tween(chomperGraphics, { tint: 0xFFFFFF }, { duration: 200 }); } } if (self.canAttack) { // Check for enemy one square ahead var targetCol = self.col + 1; if (targetCol < GRID_COLS) { for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var enemyCol = Math.floor((enemy.x - GRID_START_X) / CELL_SIZE); if (enemy.row === self.row && enemyCol === targetCol && Math.abs(enemy.x - (GRID_START_X + targetCol * CELL_SIZE + CELL_SIZE / 2)) <= 80) { self.chomp(enemy); break; } } } } }; self.chomp = function (enemy) { // Instantly kill the enemy enemy.takeDamage(10000); // Start cooldown self.canAttack = false; self.attackTimer = self.attackDelay; // Visual feedback - change to black during cooldown tween(chomperGraphics, { tint: 0x000000 }, { duration: 200 }); LK.getSound('zombie_hit').play(); }; return self; }); var Gargantuar = Container.expand(function () { var self = Container.call(this); var gargantuarGraphics = self.attachAsset('gargantuar', { anchorX: 0.5, anchorY: 0.5 }); // Tint to differentiate from other zombies gargantuarGraphics.tint = 0x8B4513; self.health = 500; // Very high health self.maxHealth = 500; self.speed = -0.3; // Slow movement self.damage = 1000; // One-hit kill for plants self.attackTimer = 0; self.attackDelay = 90; // Slower attack self.row = 0; self.attacking = false; self.update = function () { if (!self.attacking) { self.x += self.speed; } else { self.attackTimer++; if (self.attackTimer >= self.attackDelay) { self.attack(); self.attackTimer = 0; } } }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xff0000, 200); LK.getSound('zombie_hit').play(); if (self.health <= 0) { // Remove from enemies array for (var i = 0; i < enemies.length; i++) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } // Award resources for killing gargantuar resources += 50; updateResourcesDisplay(); self.destroy(); } }; self.attack = function () { // Find plant to attack (but ignore spikeweed) for (var i = 0; i < plants.length; i++) { var plant = plants[i]; if (plant.row === self.row && Math.abs(plant.x - self.x) <= 50) { // Skip spikeweed - zombies ignore it completely if (plant instanceof Spikeweed) { continue; } // Gargantuar destroys any plant with one hit plant.health = 0; LK.effects.flashObject(plant, 0xff0000, 200); plants.splice(i, 1); plant.destroy(); grid[plant.row][plant.col].occupied = false; self.attacking = false; break; } } }; return self; }); var JackInTheBoxZombie = Container.expand(function () { var self = Container.call(this); var zombieGraphics = self.attachAsset('jackInTheBox', { anchorX: 0.5, anchorY: 0.5 }); self.health = 80; self.maxHealth = 80; self.speed = -0.8; self.damage = 25; self.attackTimer = 0; self.attackDelay = 60; self.row = 0; self.attacking = false; // Explosion timer (8-15 seconds randomly) self.explosionTimer = 0; self.explosionDelay = Math.floor(Math.random() * (900 - 480) + 480); // 8-15 seconds at 60fps self.exploded = false; self.update = function () { if (!self.exploded) { self.explosionTimer++; if (self.explosionTimer >= self.explosionDelay) { self.explode(); return; } } if (!self.attacking) { self.x += self.speed; } else { self.attackTimer++; if (self.attackTimer >= self.attackDelay) { self.attack(); self.attackTimer = 0; } } }; self.explode = function () { if (self.exploded) { return; } self.exploded = true; var explosion = game.addChild(LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y })); // Destroy plants in 3x3 area around explosion var explosionRow = self.row; var explosionCol = Math.floor((self.x - GRID_START_X) / CELL_SIZE); for (var row = explosionRow - 1; row <= explosionRow + 1; row++) { for (var col = explosionCol - 1; col <= explosionCol + 1; col++) { if (row >= 0 && row < GRID_ROWS && col >= 0 && col < GRID_COLS) { if (grid[row][col].occupied) { for (var i = plants.length - 1; i >= 0; i--) { var plant = plants[i]; if (plant.row === row && plant.col === col) { plants.splice(i, 1); plant.destroy(); grid[row][col].occupied = false; break; } } } } } } LK.getSound('explode').play(); // Remove explosion after animation tween(explosion, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 500, onFinish: function onFinish() { explosion.destroy(); } }); // Remove self from enemies array for (var j = 0; j < enemies.length; j++) { if (enemies[j] === self) { enemies.splice(j, 1); break; } } self.destroy(); }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xff0000, 200); LK.getSound('zombie_hit').play(); if (self.health <= 0) { // Remove from enemies array for (var i = 0; i < enemies.length; i++) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } // Award resources for killing jack in the box zombie resources += 12; updateResourcesDisplay(); self.destroy(); } }; self.attack = function () { // Find plant to attack (but ignore spikeweed) for (var i = 0; i < plants.length; i++) { var plant = plants[i]; if (plant.row === self.row && Math.abs(plant.x - self.x) <= 50) { // Skip spikeweed - zombies ignore it completely if (plant instanceof Spikeweed) { continue; } plant.health -= self.damage; LK.effects.flashObject(plant, 0xff0000, 200); if (plant.health <= 0) { plants.splice(i, 1); plant.destroy(); grid[plant.row][plant.col].occupied = false; self.attacking = false; } break; } } }; return self; }); var Pea = Container.expand(function () { var self = Container.call(this); var peaGraphics = self.attachAsset('pea', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.damage = 20; self.update = function () { self.x += self.speed; }; return self; }); var PeaShooter = Container.expand(function () { var self = Container.call(this); var shooterGraphics = self.attachAsset('peaShooter', { anchorX: 0.5, anchorY: 0.5 }); self.cost = 100; self.health = 100; self.maxHealth = 100; self.shootTimer = 0; self.shootDelay = 90; // 1.5 seconds at 60fps self.row = 0; self.update = function () { self.shootTimer++; if (self.shootTimer >= self.shootDelay) { // Check if there's an enemy in this row var hasEnemyInRow = false; for (var i = 0; i < enemies.length; i++) { if (enemies[i].row === self.row && enemies[i].x > self.x) { hasEnemyInRow = true; break; } } if (hasEnemyInRow) { var pea = new Pea(); pea.x = self.x + 50; pea.y = self.y; pea.row = self.row; projectiles.push(pea); game.addChild(pea); LK.getSound('shoot').play(); self.shootTimer = 0; } } }; return self; }); var Pruner = Container.expand(function () { var self = Container.call(this); var prunerGraphics = self.attachAsset('pruner', { anchorX: 0.5, anchorY: 0.5 }); self.activated = false; self.speed = 0; self.row = 0; self.update = function () { if (self.activated) { self.x += self.speed; // Check collision with enemies while moving for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; if (enemy.row === self.row && Math.abs(enemy.x - self.x) <= 80) { enemy.takeDamage(1000); // Instant kill } } // Remove if off screen if (self.x > 2200) { for (var j = 0; j < pruners.length; j++) { if (pruners[j] === self) { pruners.splice(j, 1); break; } } self.destroy(); } } }; self.activate = function () { if (!self.activated) { self.activated = true; self.speed = 12; tween(self, { tint: 0xff0000 }, { duration: 200 }); } }; return self; }); var Repeater = Container.expand(function () { var self = Container.call(this); var repeaterGraphics = self.attachAsset('repeater', { anchorX: 0.5, anchorY: 0.5 }); self.cost = 200; self.health = 100; self.maxHealth = 100; self.shootTimer = 0; self.shootDelay = 90; // 1.5 seconds at 60fps self.row = 0; self.update = function () { self.shootTimer++; if (self.shootTimer >= self.shootDelay) { // Check if there's an enemy in this row var hasEnemyInRow = false; for (var i = 0; i < enemies.length; i++) { if (enemies[i].row === self.row && enemies[i].x > self.x) { hasEnemyInRow = true; break; } } if (hasEnemyInRow) { // Shoot first pea var pea1 = new Pea(); pea1.x = self.x + 50; pea1.y = self.y; pea1.row = self.row; projectiles.push(pea1); game.addChild(pea1); // Shoot second pea slightly delayed var pea2 = new Pea(); pea2.x = self.x + 30; pea2.y = self.y; pea2.row = self.row; projectiles.push(pea2); game.addChild(pea2); LK.getSound('shoot').play(); self.shootTimer = 0; } } }; return self; }); var Skeleton = Container.expand(function () { var self = Container.call(this); var skeletonGraphics = self.attachAsset('skeleton', { anchorX: 0.5, anchorY: 0.5 }); self.health = 120; self.maxHealth = 120; self.speed = -1.0; self.damage = 20; self.attackTimer = 0; self.attackDelay = 45; // Faster attack self.row = 0; self.attacking = false; self.update = function () { if (!self.attacking) { self.x += self.speed; } else { self.attackTimer++; if (self.attackTimer >= self.attackDelay) { self.attack(); self.attackTimer = 0; } } }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xff0000, 200); LK.getSound('zombie_hit').play(); if (self.health <= 0) { // Remove from enemies array for (var i = 0; i < enemies.length; i++) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } // Award resources for killing skeleton resources += 15; updateResourcesDisplay(); self.destroy(); } }; self.attack = function () { // Find plant to attack for (var i = 0; i < plants.length; i++) { var plant = plants[i]; if (plant.row === self.row && Math.abs(plant.x - self.x) <= 50) { // Skip spikeweed - zombies ignore it completely if (plant instanceof Spikeweed) { continue; } plant.health -= self.damage; LK.effects.flashObject(plant, 0xff0000, 200); if (plant.health <= 0) { plants.splice(i, 1); plant.destroy(); grid[plant.row][plant.col].occupied = false; self.attacking = false; } break; } } }; return self; }); var Spikeweed = Container.expand(function () { var self = Container.call(this); var spikeweedGraphics = self.attachAsset('spikeweed', { anchorX: 0.5, anchorY: 0.5 }); self.cost = 100; self.health = 300; self.maxHealth = 300; self.damage = 20; self.damageTimer = 0; self.damageDelay = 36; // 0.6 seconds at 60fps self.update = function () { self.damageTimer++; // Check for enemies stepping on spikeweed for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.row === self.row && Math.abs(enemy.x - self.x) <= 80 && Math.abs(enemy.y - self.y) <= 80) { if (self.damageTimer >= self.damageDelay) { enemy.takeDamage(self.damage); LK.effects.flashObject(self, 0xff0000, 200); self.damageTimer = 0; } } } }; return self; }); var Sun = Container.expand(function () { var self = Container.call(this); var sunGraphics = self.attachAsset('sun', { anchorX: 0.5, anchorY: 0.5 }); self.value = 25; self.lifeTimer = 0; self.maxLifeTime = 600; // 10 seconds self.collected = false; self.update = function () { self.lifeTimer++; if (self.lifeTimer >= self.maxLifeTime && !self.collected) { // Remove from suns array for (var i = 0; i < suns.length; i++) { if (suns[i] === self) { suns.splice(i, 1); break; } } self.destroy(); } }; self.down = function (x, y, obj) { if (!self.collected) { self.collected = true; resources += self.value; updateResourcesDisplay(); // Animate collection tween(self, { scaleX: 0.1, scaleY: 0.1, alpha: 0 }, { duration: 300, onFinish: function onFinish() { // Remove from suns array for (var i = 0; i < suns.length; i++) { if (suns[i] === self) { suns.splice(i, 1); break; } } self.destroy(); } }); } }; return self; }); var Sunflower = Container.expand(function () { var self = Container.call(this); var sunflowerGraphics = self.attachAsset('sunflower', { anchorX: 0.5, anchorY: 0.5 }); self.cost = 50; self.health = 100; self.maxHealth = 100; self.sunTimer = 0; self.sunDelay = 480; // 8 seconds self.update = function () { self.sunTimer++; if (self.sunTimer >= self.sunDelay) { self.produceSun(); self.sunTimer = 0; } }; self.produceSun = function () { var sun = new Sun(); sun.x = self.x + (Math.random() - 0.5) * 60; sun.y = self.y + (Math.random() - 0.5) * 60; suns.push(sun); game.addChild(sun); }; return self; }); var WallNut = Container.expand(function () { var self = Container.call(this); var nutGraphics = self.attachAsset('wallNut', { anchorX: 0.5, anchorY: 0.5 }); self.cost = 50; self.health = 200; self.maxHealth = 200; return self; }); var Zombie = Container.expand(function () { var self = Container.call(this); var zombieGraphics = self.attachAsset('zombie', { anchorX: 0.5, anchorY: 0.5 }); self.health = 80; self.maxHealth = 80; self.speed = -0.6; self.damage = 25; self.attackTimer = 0; self.attackDelay = 60; // 1 second self.row = 0; self.attacking = false; self.update = function () { if (!self.attacking) { self.x += self.speed; } else { self.attackTimer++; if (self.attackTimer >= self.attackDelay) { self.attack(); self.attackTimer = 0; } } }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xff0000, 200); LK.getSound('zombie_hit').play(); if (self.health <= 0) { // Remove from enemies array for (var i = 0; i < enemies.length; i++) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } // Award resources for killing zombie resources += 10; updateResourcesDisplay(); self.destroy(); } }; self.attack = function () { // Find plant to attack for (var i = 0; i < plants.length; i++) { var plant = plants[i]; if (plant.row === self.row && Math.abs(plant.x - self.x) <= 50) { // Skip spikeweed - zombies ignore it completely if (plant instanceof Spikeweed) { continue; } plant.health -= self.damage; LK.effects.flashObject(plant, 0xff0000, 200); if (plant.health <= 0) { plants.splice(i, 1); plant.destroy(); grid[plant.row][plant.col].occupied = false; self.attacking = false; } break; } } }; return self; }); var Zomboni = Container.expand(function () { var self = Container.call(this); var zomboniGraphics = self.attachAsset('zomboni', { anchorX: 0.5, anchorY: 0.5 }); // Tint to differentiate from other zombies zomboniGraphics.tint = 0x444444; self.health = 300; // Triple normal zombie health self.maxHealth = 300; self.speed = -0.8; // Faster than normal zombie self.damage = 1000; // Crushes plants instantly self.attackTimer = 0; self.attackDelay = 30; // Fast crushing self.row = 0; self.attacking = false; self.update = function () { if (!self.attacking) { self.x += self.speed; } else { self.attackTimer++; if (self.attackTimer >= self.attackDelay) { self.attack(); self.attackTimer = 0; } } }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xff0000, 200); LK.getSound('zombie_hit').play(); if (self.health <= 0) { // Remove from enemies array for (var i = 0; i < enemies.length; i++) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } // Award resources for killing zomboni resources += 30; updateResourcesDisplay(); self.destroy(); } }; self.attack = function () { // Find plant to attack and crush it instantly for (var i = 0; i < plants.length; i++) { var plant = plants[i]; if (plant.row === self.row && Math.abs(plant.x - self.x) <= 50) { // Skip spikeweed - zombies ignore it completely if (plant instanceof Spikeweed) { continue; } // Zomboni crushes any plant instantly plant.health = 0; LK.effects.flashObject(plant, 0xff0000, 200); plants.splice(i, 1); plant.destroy(); grid[plant.row][plant.col].occupied = false; self.attacking = false; break; } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2d5016 }); /**** * Game Code ****/ // Grid setup var GRID_ROWS = 5; var GRID_COLS = 9; var CELL_SIZE = 200; var GRID_START_X = 224; // Center the grid: (2048 - 1800) / 2 var GRID_START_Y = 700; // Game state var resources = 150; var currentWave = 1; var enemiesSpawned = 0; var enemiesPerWave = 5; var waveDelay = 300; // 5 seconds between waves var waveTimer = 0; var gameStartTimer = 0; var firstWaveStarted = false; var alertShown = false; var gameState = 'playing'; // 'playing', 'won', 'lost' var isNight = false; var nightTransitionCompleted = false; var dayTransitionCompleted = false; // Game objects var grid = []; var plants = []; var enemies = []; var projectiles = []; var suns = []; var pruners = []; var selectedPlantType = 'peaShooter'; // Plant recharge system var plantRecharges = { peaShooter: 0, wallNut: 0, sunflower: 0, repeater: 0, cherryBomb: 0, spikeweed: 0, chomper: 0 }; var plantRechargeTimes = { peaShooter: 180, // 3 seconds wallNut: 300, // 5 seconds sunflower: 240, // 4 seconds repeater: 360, // 6 seconds cherryBomb: 600, // 10 seconds spikeweed: 240, // 4 seconds chomper: 420 // 7 seconds }; // UI elements var resourcesText = new Text2('Resources: ' + resources, { size: 60, fill: 0xFFFFFF }); resourcesText.anchor.set(0, 0); resourcesText.x = 120; resourcesText.y = 50; LK.gui.topLeft.addChild(resourcesText); var waveText = new Text2('Wave: ' + currentWave, { size: 60, fill: 0xFFFFFF }); waveText.anchor.set(0.5, 0); waveText.x = 0; waveText.y = 50; LK.gui.top.addChild(waveText); var instructionText = new Text2('Tap grid to place plants', { size: 40, fill: 0xFFFF00 }); instructionText.anchor.set(0.5, 0); instructionText.x = 0; instructionText.y = 120; LK.gui.top.addChild(instructionText); // Shovel button at top var shovelBtn = LK.getAsset('shovel', { anchorX: 0.5, anchorY: 0.5 }); shovelBtn.x = 0; shovelBtn.y = 200; LK.gui.top.addChild(shovelBtn); // Plant selection buttons with icons and prices var peaShooterBtn = LK.getAsset('peaShooter', { anchorX: 0.5, anchorY: 1, scaleX: 0.8, scaleY: 0.8 }); peaShooterBtn.x = 75; peaShooterBtn.y = -160; LK.gui.bottomLeft.addChild(peaShooterBtn); var peaShooterPrice = new Text2('100', { size: 30, fill: 0xFFFFFF }); peaShooterPrice.anchor.set(0.5, 0); peaShooterPrice.x = 75; peaShooterPrice.y = -160; LK.gui.bottomLeft.addChild(peaShooterPrice); var wallNutBtn = LK.getAsset('wallNut', { anchorX: 0.5, anchorY: 1, scaleX: 0.8, scaleY: 0.8 }); wallNutBtn.x = 225; wallNutBtn.y = -160; LK.gui.bottomLeft.addChild(wallNutBtn); var wallNutPrice = new Text2('50', { size: 30, fill: 0xFFFFFF }); wallNutPrice.anchor.set(0.5, 0); wallNutPrice.x = 225; wallNutPrice.y = -160; LK.gui.bottomLeft.addChild(wallNutPrice); var sunflowerBtn = LK.getAsset('sunflower', { anchorX: 0.5, anchorY: 1, scaleX: 0.8, scaleY: 0.8 }); sunflowerBtn.x = 375; sunflowerBtn.y = -160; LK.gui.bottomLeft.addChild(sunflowerBtn); var sunflowerPrice = new Text2('50', { size: 30, fill: 0xFFFFFF }); sunflowerPrice.anchor.set(0.5, 0); sunflowerPrice.x = 375; sunflowerPrice.y = -160; LK.gui.bottomLeft.addChild(sunflowerPrice); var repeaterBtn = LK.getAsset('repeater', { anchorX: 0.5, anchorY: 1, scaleX: 0.8, scaleY: 0.8 }); repeaterBtn.x = 525; repeaterBtn.y = -160; LK.gui.bottomLeft.addChild(repeaterBtn); var repeaterPrice = new Text2('200', { size: 30, fill: 0xFFFFFF }); repeaterPrice.anchor.set(0.5, 0); repeaterPrice.x = 525; repeaterPrice.y = -160; LK.gui.bottomLeft.addChild(repeaterPrice); var cherryBombBtn = LK.getAsset('cherryBomb', { anchorX: 0.5, anchorY: 1, scaleX: 0.8, scaleY: 0.8 }); cherryBombBtn.x = 675; cherryBombBtn.y = -160; LK.gui.bottomLeft.addChild(cherryBombBtn); var cherryBombPrice = new Text2('150', { size: 30, fill: 0xFFFFFF }); cherryBombPrice.anchor.set(0.5, 0); cherryBombPrice.x = 675; cherryBombPrice.y = -160; LK.gui.bottomLeft.addChild(cherryBombPrice); var spikeweedBtn = LK.getAsset('spikeweed', { anchorX: 0.5, anchorY: 1, scaleX: 0.8, scaleY: 0.8 }); spikeweedBtn.x = 825; spikeweedBtn.y = -160; LK.gui.bottomLeft.addChild(spikeweedBtn); var spikeweedPrice = new Text2('100', { size: 30, fill: 0xFFFFFF }); spikeweedPrice.anchor.set(0.5, 0); spikeweedPrice.x = 825; spikeweedPrice.y = -160; LK.gui.bottomLeft.addChild(spikeweedPrice); var chomperBtn = LK.getAsset('chomper', { anchorX: 0.5, anchorY: 1, scaleX: 0.8, scaleY: 0.8 }); chomperBtn.x = 975; chomperBtn.y = -160; LK.gui.bottomLeft.addChild(chomperBtn); var chomperPrice = new Text2('150', { size: 30, fill: 0xFFFFFF }); chomperPrice.anchor.set(0.5, 0); chomperPrice.x = 975; chomperPrice.y = -160; LK.gui.bottomLeft.addChild(chomperPrice); // Create grid for (var row = 0; row < GRID_ROWS; row++) { grid[row] = []; for (var col = 0; col < GRID_COLS; col++) { var cell = game.addChild(LK.getAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, x: GRID_START_X + col * CELL_SIZE + CELL_SIZE / 2, y: GRID_START_Y + row * CELL_SIZE + CELL_SIZE / 2, alpha: 0.3 })); cell.row = row; cell.col = col; cell.occupied = false; grid[row][col] = cell; } } // Create pruners (one per row at the back) for (var row = 0; row < GRID_ROWS; row++) { var pruner = new Pruner(); pruner.x = GRID_START_X - 50; // Further ahead pruner.y = GRID_START_Y + row * CELL_SIZE + CELL_SIZE / 2; pruner.row = row; pruners.push(pruner); game.addChild(pruner); } function updateResourcesDisplay() { resourcesText.setText('Resources: ' + resources); } function updateWaveDisplay() { waveText.setText('Wave: ' + currentWave); } function getPlantCost(plantType) { switch (plantType) { case 'peaShooter': return 100; case 'wallNut': return 50; case 'sunflower': return 50; case 'repeater': return 200; case 'cherryBomb': return 150; case 'spikeweed': return 100; case 'chomper': return 150; default: return 100; } } function canAffordPlant(plantType) { return resources >= getPlantCost(plantType); } function placePlant(row, col, plantType) { if (grid[row][col].occupied || !canAffordPlant(plantType) || plantRecharges[plantType] > 0) { return false; } var plant; var cost = getPlantCost(plantType); switch (plantType) { case 'peaShooter': plant = new PeaShooter(); break; case 'wallNut': plant = new WallNut(); break; case 'sunflower': plant = new Sunflower(); break; case 'repeater': plant = new Repeater(); break; case 'cherryBomb': plant = new CherryBomb(); break; case 'spikeweed': plant = new Spikeweed(); break; case 'chomper': plant = new Chomper(); break; default: return false; } plant.x = GRID_START_X + col * CELL_SIZE + CELL_SIZE / 2; plant.y = GRID_START_Y + row * CELL_SIZE + CELL_SIZE / 2; plant.row = row; plant.col = col; plants.push(plant); game.addChild(plant); grid[row][col].occupied = true; resources -= cost; plantRecharges[plantType] = plantRechargeTimes[plantType]; updateResourcesDisplay(); LK.getSound('plant').play(); return true; } function removePlant(row, col) { if (!grid[row][col].occupied) { return false; } // Find and remove plant from plants array for (var i = 0; i < plants.length; i++) { var plant = plants[i]; if (plant.row === row && plant.col === col) { plants.splice(i, 1); plant.destroy(); grid[row][col].occupied = false; return true; } } return false; } function spawnEnemy(row) { var enemy; var rand = Math.random(); var spawnMultiplier = isNight ? 1.5 : 1; // More enemies at night var waveMultiplier = Math.min(1 + (currentWave - 1) * 0.15, 2.5); // Increase spawn chance more aggressively with waves if (currentWave >= 5 && rand < 0.12 * waveMultiplier) { enemy = new Gargantuar(); } else if (currentWave >= 4 && rand < 0.18 * waveMultiplier) { enemy = new Zomboni(); } else if (currentWave >= 3 && rand < 0.25 * waveMultiplier) { enemy = new JackInTheBoxZombie(); } else if (currentWave >= 2 && rand < 0.35 * waveMultiplier) { enemy = new AllStarZombie(); } else if (currentWave >= 3 && rand < 0.6 * waveMultiplier) { enemy = new Skeleton(); } else { enemy = new Zombie(); } enemy.x = 2000; enemy.y = GRID_START_Y + row * CELL_SIZE + CELL_SIZE / 2; enemy.row = row; enemiesSpawned++; enemies.push(enemy); game.addChild(enemy); // Spawn additional enemies at night and more as waves progress from wave 3 var extraSpawnChance = isNight ? 0.4 : 0.2; if (currentWave >= 3) { extraSpawnChance += (currentWave - 1) * 0.08; // More aggressive increase with waves } if (Math.random() < extraSpawnChance) { var extraEnemy = new Zombie(); extraEnemy.x = 2050; extraEnemy.y = GRID_START_Y + row * CELL_SIZE + CELL_SIZE / 2; extraEnemy.row = row; enemies.push(extraEnemy); game.addChild(extraEnemy); } } function checkWaveComplete() { if (enemiesSpawned >= enemiesPerWave && enemies.length === 0) { currentWave++; updateWaveDisplay(); if (currentWave > 10) { gameState = 'won'; LK.showYouWin(); return; } enemiesSpawned = 0; enemiesPerWave = Math.min(5 + currentWave * 4, 30); waveTimer = 0; } } function checkGameOver() { for (var i = 0; i < enemies.length; i++) { if (enemies[i].x <= GRID_START_X - 50) { gameState = 'lost'; LK.showGameOver(); return; } } } // Button event handlers shovelBtn.down = function (x, y, obj) { selectedPlantType = 'shovel'; instructionText.setText('Selected: Shovel - Click plants to remove'); }; peaShooterBtn.down = function (x, y, obj) { if (plantRecharges.peaShooter === 0) { selectedPlantType = 'peaShooter'; instructionText.setText('Selected: Pea Shooter (100)'); } else { instructionText.setText('Pea Shooter recharging: ' + Math.ceil(plantRecharges.peaShooter / 60) + 's'); } }; wallNutBtn.down = function (x, y, obj) { if (plantRecharges.wallNut === 0) { selectedPlantType = 'wallNut'; instructionText.setText('Selected: Wall Nut (50)'); } else { instructionText.setText('Wall Nut recharging: ' + Math.ceil(plantRecharges.wallNut / 60) + 's'); } }; sunflowerBtn.down = function (x, y, obj) { if (plantRecharges.sunflower === 0) { selectedPlantType = 'sunflower'; instructionText.setText('Selected: Sunflower (50)'); } else { instructionText.setText('Sunflower recharging: ' + Math.ceil(plantRecharges.sunflower / 60) + 's'); } }; repeaterBtn.down = function (x, y, obj) { if (plantRecharges.repeater === 0) { selectedPlantType = 'repeater'; instructionText.setText('Selected: Repeater (200)'); } else { instructionText.setText('Repeater recharging: ' + Math.ceil(plantRecharges.repeater / 60) + 's'); } }; cherryBombBtn.down = function (x, y, obj) { if (plantRecharges.cherryBomb === 0) { selectedPlantType = 'cherryBomb'; instructionText.setText('Selected: Cherry Bomb (150)'); } else { instructionText.setText('Cherry Bomb recharging: ' + Math.ceil(plantRecharges.cherryBomb / 60) + 's'); } }; spikeweedBtn.down = function (x, y, obj) { if (plantRecharges.spikeweed === 0) { selectedPlantType = 'spikeweed'; instructionText.setText('Selected: Spikeweed (100)'); } else { instructionText.setText('Spikeweed recharging: ' + Math.ceil(plantRecharges.spikeweed / 60) + 's'); } }; chomperBtn.down = function (x, y, obj) { if (plantRecharges.chomper === 0) { selectedPlantType = 'chomper'; instructionText.setText('Selected: Chomper (150)'); } else { instructionText.setText('Chomper recharging: ' + Math.ceil(plantRecharges.chomper / 60) + 's'); } }; // Grid click handler game.down = function (x, y, obj) { if (gameState !== 'playing') { return; } // Check if click is within grid bounds if (x >= GRID_START_X && x <= GRID_START_X + GRID_COLS * CELL_SIZE && y >= GRID_START_Y && y <= GRID_START_Y + GRID_ROWS * CELL_SIZE) { var col = Math.floor((x - GRID_START_X) / CELL_SIZE); var row = Math.floor((y - GRID_START_Y) / CELL_SIZE); if (row >= 0 && row < GRID_ROWS && col >= 0 && col < GRID_COLS) { if (selectedPlantType === 'shovel') { removePlant(row, col); } else { placePlant(row, col, selectedPlantType); } } } }; // Main game loop game.update = function () { if (gameState !== 'playing') { return; } // Handle day/night cycle if (currentWave === 6 && !nightTransitionCompleted) { // Transition to night after wave 5 isNight = true; nightTransitionCompleted = true; game.setBackgroundColor(0x001122); // Flash screen to indicate night LK.effects.flashScreen(0x000044, 1000); } else if (currentWave === 11 && !dayTransitionCompleted) { // Transition back to day after wave 10 isNight = false; dayTransitionCompleted = true; game.setBackgroundColor(0x2d5016); // Flash screen to indicate day LK.effects.flashScreen(0x88ff44, 1000); } // Keep background color consistent during night/day periods if (isNight && currentWave >= 6 && currentWave < 11) { game.setBackgroundColor(0x001122); } else if (!isNight && (currentWave < 6 || currentWave >= 11)) { game.setBackgroundColor(0x2d5016); } // Handle game start delay if (!firstWaveStarted) { gameStartTimer++; if (gameStartTimer >= 1500 && !alertShown) { // 25 seconds alertShown = true; LK.effects.flashScreen(0xff0000, 2000); // Create alert text var alertText = new Text2('ZOMBIES ARE COMING!', { size: 80, fill: 0xff0000 }); alertText.anchor.set(0.5, 0.5); alertText.x = 0; alertText.y = 0; LK.gui.center.addChild(alertText); // Animate alert text tween(alertText, { scaleX: 1.5, scaleY: 1.5 }, { duration: 500 }); tween(alertText, { alpha: 0 }, { duration: 2000, onFinish: function onFinish() { alertText.destroy(); firstWaveStarted = true; } }); } } // Spawn enemies if (firstWaveStarted) { waveTimer++; if (enemiesSpawned < enemiesPerWave && waveTimer % 120 === 0) { // Every 2 seconds var randomRow = Math.floor(Math.random() * GRID_ROWS); spawnEnemy(randomRow); } } // Update projectiles for (var i = projectiles.length - 1; i >= 0; i--) { var projectile = projectiles[i]; // Check collision with enemies var hit = false; for (var j = 0; j < enemies.length; j++) { var enemy = enemies[j]; if (projectile.row === enemy.row && projectile.x >= enemy.x - 35 && projectile.x <= enemy.x + 35) { enemy.takeDamage(projectile.damage); hit = true; break; } } // Remove projectile if hit or off screen if (hit || projectile.x > 2100) { projectiles.splice(i, 1); projectile.destroy(); } } // Check enemy-plant collisions for (var k = 0; k < enemies.length; k++) { var enemy = enemies[k]; enemy.attacking = false; for (var l = 0; l < plants.length; l++) { var plant = plants[l]; if (enemy.row === plant.row && enemy.x >= plant.x - 50 && enemy.x <= plant.x + 50) { // Skip spikeweed - zombies ignore it completely and don't stop for it if (plant instanceof Spikeweed) { continue; } enemy.attacking = true; break; } } } // Update plant recharges for (var plantType in plantRecharges) { if (plantRecharges[plantType] > 0) { plantRecharges[plantType]--; } } // Check pruner activation for (var m = 0; m < pruners.length; m++) { var pruner = pruners[m]; if (!pruner.activated) { for (var n = 0; n < enemies.length; n++) { var enemy = enemies[n]; if (enemy.row === pruner.row && Math.abs(enemy.x - pruner.x) <= 80) { pruner.activate(); break; } } } } checkWaveComplete(); checkGameOver(); }; // Play background music LK.playMusic('Grasswalk');
===================================================================
--- original.js
+++ change.js
@@ -45,9 +45,9 @@
break;
}
}
// Award resources for killing all star zombie
- resources += 20;
+ resources += 200;
updateResourcesDisplay();
self.destroy();
}
};
@@ -296,9 +296,11 @@
}
}
};
self.explode = function () {
- if (self.exploded) return;
+ if (self.exploded) {
+ return;
+ }
self.exploded = true;
var explosion = game.addChild(LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
@@ -1296,9 +1298,11 @@
}
};
// Grid click handler
game.down = function (x, y, obj) {
- if (gameState !== 'playing') return;
+ if (gameState !== 'playing') {
+ return;
+ }
// Check if click is within grid bounds
if (x >= GRID_START_X && x <= GRID_START_X + GRID_COLS * CELL_SIZE && y >= GRID_START_Y && y <= GRID_START_Y + GRID_ROWS * CELL_SIZE) {
var col = Math.floor((x - GRID_START_X) / CELL_SIZE);
var row = Math.floor((y - GRID_START_Y) / CELL_SIZE);
@@ -1312,9 +1316,11 @@
}
};
// Main game loop
game.update = function () {
- if (gameState !== 'playing') return;
+ if (gameState !== 'playing') {
+ return;
+ }
// Handle day/night cycle
if (currentWave === 6 && !nightTransitionCompleted) {
// Transition to night after wave 5
isNight = true;
varias nubes de Humo color rojo un poco oscuro saliendo de una explosión. In-Game asset. High contrast
Una casilla de pasto con bordes color verde oscuro que cubra toda la imagen y unas pequeñas flores. In-Game asset. High contrast
Sol puntiagudo luminoso. In-Game asset. 2d. High contrast. No shadows
pequeña pala de acero, con mango de madera y un agarre de color rojo. In-Game asset. 2d. High contrast. No shadows