/****
* 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 = 200;
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 += 50;
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 = 200;
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
@@ -79,9 +79,9 @@
var bombGraphics = self.attachAsset('cherryBomb', {
anchorX: 0.5,
anchorY: 0.5
});
- self.cost = 150;
+ self.cost = 200;
self.health = 50;
self.maxHealth = 50;
self.fuseTimer = 0;
self.fuseDelay = 180; // 3 seconds
@@ -747,9 +747,9 @@
break;
}
}
// Award resources for killing zombie
- resources += 10;
+ resources += 50;
updateResourcesDisplay();
self.destroy();
}
};
@@ -860,9 +860,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 resources = 200;
var currentWave = 1;
var enemiesSpawned = 0;
var enemiesPerWave = 5;
var waveDelay = 300; // 5 seconds between waves
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