User prompt
Creates a plant called puffshroom that costs 0 suns and shoots spores and creates a separate resource for the spore and has a range to detect a zombie of 3 tiles and can only be used from wave 11 onwards and appears on page 2 of the plant selector βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make the repeater plant appear on the first page of the plant selector.
User prompt
Make the left arrow of the plant selector go further to the left so that the repeater plant comes in after the chomper plant in the plant selector on the first page.
User prompt
Make the gargantuar spawn a fastzombie 3 squares further away from the gargantuar
User prompt
Make the arrow button further to the left and on the first page of the plant selector, have Peashooter, Sunflower, Cherrypopper, Wallnut, Potatomine, Snowshooter, Chomper, and Repeater in that order.
User prompt
Make the gargantuar unable to be eaten by the chomper plant
User prompt
Make the chomper plant eat the nearest zombie and make the minerzombie take 5 seconds instead of 3 βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Also make the chomper plant adjust when it is black when it can't eat another zombie and return to its normal color when it can eat another zombie βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make the first zombie spawn in wave 1 after 10 seconds.
User prompt
Sets the chomper to a two-tile range and a 15 second cooldown. βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
fix the bug that causes the potatomine plant to explode without eliminating the zombie that touched it and make the potatomine plant take 15 seconds to charge, not 5 anymore, make it so that when the potatomine explodes with a newspaperzombie and gargantuar it leaves it at half health βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
creates a plant called potatomine that costs 25 suns and will take 5 seconds to be charged to explode when a zombie touches it this plant will explode and while it is on cooldown when placed it will be black and when it is charged it will return to its normal color βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Move the plant selector a little to the left and create a plant called chomper that eats the nearby zombie in a range of one square and has a cooldown to be able to eat another zombie and costs 150 soles and when it is on cooldown it has a black color βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Now make different plant selectors with different plants and make splitpea appear in the second plant selector and for now let there only be 2 plant selectors and when pressing the right arrow button it changes to the second plant selector and when pressing the left arrow it returns to the previous plant selector
User prompt
Add two arrows, one on the left side of the plant selector and the other on the right side of the plant selector, which for now has no function.
User prompt
Make the suns bigger and have the suns that appear without a sunflower have an animation that says they are falling from the top to the bottom of the screen. βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make the shovel at the top right of the screen and make it bigger.
User prompt
Make the letters of the sun costs of the plants black and remove the black outline.
User prompt
Make the letters of the sole costs of the plants even bigger.
User prompt
Make the sun counter have a black outline and make the plant and shovel cost letters larger.
User prompt
make the sun letters have a black outline
User prompt
Modify Splitpea so that it can also detect zombies that are behind
User prompt
Create a plant called splitpea that costs 125 soles and shoots one pea forward and two peas backward.
User prompt
Create a plant called repeater that costs 200 soles and shoots two peas.
User prompt
create an asset for the minerzombie
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BackwardPea = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('pea', {
anchorX: 0.5,
anchorY: 0.5
});
// Tint green to show it's from splitpea
graphics.tint = 0x44ff44;
self.speed = -8; // Negative speed to go backwards
self.damage = 15;
self.gridY = 0;
self.update = function () {
self.x += self.speed;
// Check collision with zombies
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
if (zombie.gridY === self.gridY && self.intersects(zombie)) {
// Check if it's a JesterZombie
if (zombie.isJester) {
// Create reflected pea going forward
var reflectedPea = new ReflectedPea();
reflectedPea.x = zombie.x + 50;
reflectedPea.y = zombie.y;
reflectedPea.gridY = zombie.gridY;
reflectedpeas.push(reflectedPea);
game.addChild(reflectedPea);
// Flash jester zombie to show deflection
LK.effects.flashObject(zombie, 0xffff00, 200);
} else {
zombie.takeDamage(self.damage, 'pea');
}
self.destroy();
for (var j = 0; j < backwardpeas.length; j++) {
if (backwardpeas[j] === self) {
backwardpeas.splice(j, 1);
break;
}
}
return;
}
}
// Remove if off screen (left side)
if (self.x < -100) {
self.destroy();
for (var k = 0; k < backwardpeas.length; k++) {
if (backwardpeas[k] === self) {
backwardpeas.splice(k, 1);
break;
}
}
}
};
return self;
});
var BasicZombie = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('basicZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0.5;
self.health = 100;
self.maxHealth = 100;
self.damage = 40;
self.gridY = 0;
self.attackTimer = 0;
self.attackDelay = 60;
self.update = function () {
// Check if there's a plant to attack
var plantToAttack = null;
for (var i = 0; i < plants.length; i++) {
var plant = plants[i];
if (plant.gridY === self.gridY && Math.abs(plant.x - self.x) < 80) {
plantToAttack = plant;
break;
}
}
if (plantToAttack) {
self.attackTimer++;
if (self.attackTimer >= self.attackDelay) {
if (plantToAttack.takeDamage) {
plantToAttack.takeDamage(self.damage);
}
self.attackTimer = 0;
}
} else {
self.x -= self.speed;
self.attackTimer = 0;
}
// Check if reached house
if (self.x < 150) {
LK.showGameOver();
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 200);
LK.getSound('zombie_hit').play();
if (self.health <= 0) {
self.destroy();
for (var i = 0; i < zombies.length; i++) {
if (zombies[i] === self) {
zombies.splice(i, 1);
break;
}
}
zombiesKilled++;
}
};
return self;
});
var Zomboni = BasicZombie.expand(function () {
var self = BasicZombie.call(this);
// Replace the graphics with zomboni asset
self.removeChild(self.children[0]);
var graphics = self.attachAsset('zomboni', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1.5; // Fast movement
self.health = 150;
self.maxHealth = 150;
self.damage = 100;
self.gridY = 0;
self.attackTimer = 0;
self.attackDelay = 30; // Faster elimination
self.update = function () {
// Move forward continuously
self.x -= self.speed;
// Check for plants to eliminate (in a wider area due to size)
for (var i = plants.length - 1; i >= 0; i--) {
var plant = plants[i];
if (plant.gridY === self.gridY && Math.abs(plant.x - self.x) < 100) {
// Instantly eliminate the plant
LK.effects.flashObject(plant, 0xff0000, 200);
// Remove from plant grid
var gridKey = plant.gridX + ',' + plant.gridY;
delete plantGrid[gridKey];
plant.destroy();
plants.splice(i, 1);
}
}
// Check if reached house
if (self.x < 150) {
LK.showGameOver();
}
};
return self;
});
var TankZombie = BasicZombie.expand(function () {
var self = BasicZombie.call(this);
// Replace the graphics with tank zombie asset
self.removeChild(self.children[0]);
var graphics = self.attachAsset('tankZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0.3;
self.health = 200;
self.maxHealth = 200;
self.damage = 80;
self.gridY = 0;
self.attackTimer = 0;
self.attackDelay = 60;
return self;
});
var NewspaperZombie = BasicZombie.expand(function () {
var self = BasicZombie.call(this);
// Replace the graphics with newspaper zombie asset
self.removeChild(self.children[0]);
var graphics = self.attachAsset('newspaperZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0.5;
self.health = 100;
self.maxHealth = 100;
self.damage = 40;
self.gridY = 0;
self.attackTimer = 0;
self.attackDelay = 60;
self.hasRaged = false;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 200);
LK.getSound('zombie_hit').play();
// Check if health dropped to half or below and hasn't raged yet
if (self.health <= self.maxHealth / 2 && !self.hasRaged) {
self.hasRaged = true;
// Increase speed and damage
self.speed = 1.5;
self.damage = 80;
// Visual effect for rage mode - flash red and change tint
LK.effects.flashObject(self, 0xff0000, 500);
tween(graphics, {
tint: 0xff4444
}, {
duration: 300,
easing: tween.easeOut
});
}
if (self.health <= 0) {
self.destroy();
for (var i = 0; i < zombies.length; i++) {
if (zombies[i] === self) {
zombies.splice(i, 1);
break;
}
}
zombiesKilled++;
}
};
return self;
});
var MinerZombie = BasicZombie.expand(function () {
var self = BasicZombie.call(this);
// Replace the graphics with miner zombie asset (appears as normal zombie)
self.removeChild(self.children[0]);
var graphics = self.attachAsset('minerZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0.5;
self.health = 80;
self.maxHealth = 80;
self.damage = 40;
self.gridY = 0;
self.attackTimer = 0;
self.attackDelay = 60;
self.teleportTimer = 0;
self.teleportDelay = 300; // 5 seconds at 60fps
self.hasTeleported = false;
self.update = function () {
// If not teleported yet, count down to teleport
if (!self.hasTeleported) {
self.teleportTimer++;
if (self.teleportTimer >= self.teleportDelay) {
self.teleport();
} else {
// Move normally before teleporting
self.x -= self.speed;
}
} else {
// After teleporting, behave like normal zombie but attacking from behind
var plantToAttack = null;
for (var i = 0; i < plants.length; i++) {
var plant = plants[i];
if (plant.gridY === self.gridY && Math.abs(plant.x - self.x) < 80) {
plantToAttack = plant;
break;
}
}
if (plantToAttack) {
self.attackTimer++;
if (self.attackTimer >= self.attackDelay) {
if (plantToAttack.takeDamage) {
plantToAttack.takeDamage(self.damage);
}
self.attackTimer = 0;
}
} else {
self.x += self.speed; // Move forward (right) after teleporting
self.attackTimer = 0;
}
}
// Check if reached house (only matters before teleporting)
if (!self.hasTeleported && self.x < 150) {
LK.showGameOver();
}
// Check if moved off screen after teleporting
if (self.hasTeleported && self.x > 2200) {
self.destroy();
for (var i = 0; i < zombies.length; i++) {
if (zombies[i] === self) {
zombies.splice(i, 1);
break;
}
}
}
};
self.teleport = function () {
if (self.hasTeleported) return;
self.hasTeleported = true;
// Flash effect before teleporting
LK.effects.flashObject(self, 0xffff00, 300);
// Teleport to the first column (leftmost position behind plants)
var newX = gridStartX + gridWidth / 2;
var newY = gridStartY + self.gridY * gridHeight + gridHeight / 2;
// Animate teleportation with tween
tween(self, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
// Move to new position
self.x = newX;
self.y = newY;
// Animate appearing at new location
tween(self, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeOut
});
// Flash effect when appearing
LK.effects.flashObject(self, 0x00ff00, 300);
}
});
};
return self;
});
var JesterZombie = BasicZombie.expand(function () {
var self = BasicZombie.call(this);
// Replace the graphics with jester zombie asset
self.removeChild(self.children[0]);
var graphics = self.attachAsset('jesterZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0.8;
self.health = 120;
self.maxHealth = 120;
self.damage = 50;
self.gridY = 0;
self.attackTimer = 0;
self.attackDelay = 60;
self.isJester = true; // Flag to identify this zombie type
// Override takeDamage to be immune to peas
self.takeDamage = function (damage, source) {
// Only take damage from non-pea sources
if (source !== 'pea' && source !== 'frozenpea') {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 200);
LK.getSound('zombie_hit').play();
if (self.health <= 0) {
self.destroy();
for (var i = 0; i < zombies.length; i++) {
if (zombies[i] === self) {
zombies.splice(i, 1);
break;
}
}
zombiesKilled++;
}
}
};
return self;
});
var JackInBoxZombie = BasicZombie.expand(function () {
var self = BasicZombie.call(this);
// Replace the graphics with jack in box zombie asset
self.removeChild(self.children[0]);
var graphics = self.attachAsset('jackInBoxZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0.7;
self.health = 80;
self.maxHealth = 80;
self.damage = 60;
self.hasExploded = false;
self.explode = function () {
if (self.hasExploded) return;
self.hasExploded = true;
// Flash the jack in box zombie white before exploding
LK.effects.flashObject(self, 0xffffff, 200);
// Scale up effect for explosion
tween(graphics, {
scaleX: 3,
scaleY: 3,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Remove from zombies array
for (var i = 0; i < zombies.length; i++) {
if (zombies[i] === self) {
zombies.splice(i, 1);
break;
}
}
// Destroy the jack in box zombie
self.destroy();
}
});
// Eliminate plants in a 250 pixel radius
var explosionRadius = 250;
for (var i = plants.length - 1; i >= 0; i--) {
var plant = plants[i];
var distance = Math.sqrt(Math.pow(plant.x - self.x, 2) + Math.pow(plant.y - self.y, 2));
if (distance <= explosionRadius) {
// Flash plant red before destroying
LK.effects.flashObject(plant, 0xff0000, 200);
// Remove from plant grid
var gridKey = plant.gridX + ',' + plant.gridY;
delete plantGrid[gridKey];
plant.destroy();
plants.splice(i, 1);
}
}
};
self.update = function () {
// Check if there's a plant to explode on
var plantToExplode = null;
for (var i = 0; i < plants.length; i++) {
var plant = plants[i];
if (plant.gridY === self.gridY && Math.abs(plant.x - self.x) < 80) {
plantToExplode = plant;
break;
}
}
if (plantToExplode && !self.hasExploded) {
self.explode();
} else {
self.x -= self.speed;
}
// Check if reached house
if (self.x < 150) {
LK.showGameOver();
}
};
return self;
});
var Gargantuar = BasicZombie.expand(function () {
var self = BasicZombie.call(this);
// Replace the graphics with gargantuar asset
self.removeChild(self.children[0]);
var graphics = self.attachAsset('gargantuar', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0.2; // Very slow
self.health = 400; // Very resistant
self.maxHealth = 400;
self.damage = 200; // High damage to eliminate plants instantly
self.gridY = 0;
self.attackTimer = 0;
self.attackDelay = 30; // Fast elimination
self.hasSpawnedFastZombie = false;
self.isResistantToExplosion = true; // Flag for cherry popper resistance
self.update = function () {
// Check if there's a plant to eliminate
var plantToEliminate = null;
for (var i = 0; i < plants.length; i++) {
var plant = plants[i];
if (plant.gridY === self.gridY && Math.abs(plant.x - self.x) < 120) {
plantToEliminate = plant;
break;
}
}
if (plantToEliminate) {
self.attackTimer++;
if (self.attackTimer >= self.attackDelay) {
// Instantly eliminate the plant
LK.effects.flashObject(plantToEliminate, 0xff0000, 200);
// Remove from plant grid
var gridKey = plantToEliminate.gridX + ',' + plantToEliminate.gridY;
delete plantGrid[gridKey];
plantToEliminate.destroy();
for (var j = 0; j < plants.length; j++) {
if (plants[j] === plantToEliminate) {
plants.splice(j, 1);
break;
}
}
self.attackTimer = 0;
}
} else {
self.x -= self.speed;
self.attackTimer = 0;
}
// Spawn FastZombie when at half health
if (self.health <= self.maxHealth / 2 && !self.hasSpawnedFastZombie) {
self.hasSpawnedFastZombie = true;
var fastZombie = new FastZombie();
// Spawn 3 squares further away (3 * gridWidth = 3 * 320 = 960 pixels)
fastZombie.x = self.x + 960;
fastZombie.y = self.y;
fastZombie.gridY = self.gridY;
zombies.push(fastZombie);
game.addChild(fastZombie);
// Visual effect for spawning
LK.effects.flashObject(self, 0xffff00, 500);
}
// Check if reached house
if (self.x < 150) {
LK.showGameOver();
}
};
return self;
});
var FastZombie = BasicZombie.expand(function () {
var self = BasicZombie.call(this);
// Replace the graphics with fast zombie asset
self.removeChild(self.children[0]);
var graphics = self.attachAsset('fastZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1.2;
self.health = 60;
self.maxHealth = 60;
self.damage = 40;
self.gridY = 0;
self.attackTimer = 0;
self.attackDelay = 60;
return self;
});
var CherryPopper = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('cherrypopper', {
anchorX: 0.5,
anchorY: 0.5
});
self.cost = 150;
self.gridX = 0;
self.gridY = 0;
self.hasExploded = false;
self.explode = function () {
if (self.hasExploded) return;
self.hasExploded = true;
// Flash the cherry popper white before exploding
LK.effects.flashObject(self, 0xffffff, 200);
// Scale up effect for explosion
tween(graphics, {
scaleX: 3,
scaleY: 3,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Remove from plants array
for (var i = 0; i < plants.length; i++) {
if (plants[i] === self) {
plants.splice(i, 1);
break;
}
}
// Remove from plant grid
var gridKey = self.gridX + ',' + self.gridY;
delete plantGrid[gridKey];
// Destroy the cherry popper
self.destroy();
}
});
// Eliminate zombies in a 300 pixel radius
var explosionRadius = 300;
for (var i = zombies.length - 1; i >= 0; i--) {
var zombie = zombies[i];
var distance = Math.sqrt(Math.pow(zombie.x - self.x, 2) + Math.pow(zombie.y - self.y, 2));
if (distance <= explosionRadius) {
// Special handling for Gargantuar
if (zombie.constructor === Gargantuar) {
// Flash zombie red
LK.effects.flashObject(zombie, 0xff0000, 200);
// If at full health or more than half, reduce to half
if (zombie.health > zombie.maxHealth / 2) {
zombie.health = zombie.maxHealth / 2;
// Visual effect for being damaged but not destroyed
tween(zombie, {
tint: 0xff4444
}, {
duration: 500,
easing: tween.easeOut
});
} else {
// Second explosion - eliminate Gargantuar
zombie.destroy();
zombies.splice(i, 1);
zombiesKilled++;
}
} else {
// Normal zombies - eliminate immediately
LK.effects.flashObject(zombie, 0xff0000, 200);
zombie.destroy();
zombies.splice(i, 1);
zombiesKilled++;
}
}
}
};
// Auto-explode when planted (immediately after placement)
self.update = function () {
if (!self.hasExploded) {
self.explode();
}
};
return self;
});
var Chomper = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('chomper', {
anchorX: 0.5,
anchorY: 0.5
});
self.cost = 150;
self.health = 100;
self.maxHealth = 100;
self.gridX = 0;
self.gridY = 0;
self.eatCooldown = 0;
self.maxEatCooldown = 900; // 15 seconds at 60fps
self.isOnCooldown = false;
self.digestionTimer = 0;
self.maxDigestionTime = 180; // 3 seconds to digest
self.isDigesting = false;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
self.destroy();
for (var i = 0; i < plants.length; i++) {
if (plants[i] === self) {
plants.splice(i, 1);
break;
}
}
var gridKey = self.gridX + ',' + self.gridY;
delete plantGrid[gridKey];
} else {
// Visual damage feedback
var damageRatio = self.health / self.maxHealth;
graphics.alpha = 0.3 + damageRatio * 0.7;
}
};
self.update = function () {
// Handle digestion timer
if (self.isDigesting) {
self.digestionTimer++;
if (self.digestionTimer >= self.maxDigestionTime) {
self.isDigesting = false;
self.digestionTimer = 0;
// Reset color after digesting
tween(graphics, {
tint: 0xffffff
}, {
duration: 300,
easing: tween.easeOut
});
}
return; // Don't eat while digesting
}
// Handle cooldown
if (self.isOnCooldown) {
self.eatCooldown--;
if (self.eatCooldown <= 0) {
self.isOnCooldown = false;
// Reset color when cooldown ends
tween(graphics, {
tint: 0xffffff
}, {
duration: 300,
easing: tween.easeOut
});
} else {
// Ensure black color is maintained during cooldown
if (graphics.tint !== 0x000000) {
tween(graphics, {
tint: 0x000000
}, {
duration: 100,
easing: tween.easeOut
});
}
}
return; // Don't eat while on cooldown
}
// Look for the nearest zombie in adjacent squares (two square range)
var nearestZombie = null;
var nearestDistance = Infinity;
var nearestIndex = -1;
for (var i = zombies.length - 1; i >= 0; i--) {
var zombie = zombies[i];
// Check if zombie is in same lane and within two grid square range
var gridDistance = Math.abs(zombie.x - self.x) / gridWidth;
if (zombie.gridY === self.gridY && gridDistance <= 2.2) {
var distance = Math.abs(zombie.x - self.x);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestZombie = zombie;
nearestIndex = i;
}
}
}
// Eat the nearest zombie if found
if (nearestZombie) {
self.eatZombie(nearestZombie, nearestIndex);
}
};
self.eatZombie = function (zombie, zombieIndex) {
// Check if zombie is a Gargantuar - cannot be eaten
if (zombie.constructor === Gargantuar) {
// Flash red to show chomper can't eat this zombie
LK.effects.flashObject(self, 0xff0000, 300);
return; // Exit without eating
}
// Flash white to show eating action
LK.effects.flashObject(self, 0xffffff, 300);
// Remove zombie immediately
zombie.destroy();
zombies.splice(zombieIndex, 1);
zombiesKilled++;
// Start digestion period
self.isDigesting = true;
self.digestionTimer = 0;
// Visual feedback for digesting (green tint)
tween(graphics, {
tint: 0x44ff44
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Start cooldown after digestion
self.isOnCooldown = true;
self.eatCooldown = self.maxEatCooldown;
// Visual feedback for cooldown (black tint)
tween(graphics, {
tint: 0x000000
}, {
duration: 300,
easing: tween.easeOut
});
}
});
};
return self;
});
var FrozenPea = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('frozenpea', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = 15;
self.gridY = 0;
self.slowDuration = 300; // 5 seconds at 60fps
self.slowFactor = 0.3; // Reduce speed to 30%
self.update = function () {
self.x += self.speed;
// Check collision with zombies
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
if (zombie.gridY === self.gridY && self.intersects(zombie)) {
// Check if it's a JesterZombie
if (zombie.isJester) {
// Create reflected frozen pea going back towards plants
var reflectedFrozenPea = new ReflectedFrozenPea();
reflectedFrozenPea.x = zombie.x - 50;
reflectedFrozenPea.y = zombie.y;
reflectedFrozenPea.gridY = zombie.gridY;
reflectedfrozenpeas.push(reflectedFrozenPea);
game.addChild(reflectedFrozenPea);
// Flash jester zombie to show deflection
LK.effects.flashObject(zombie, 0xffff00, 200);
} else {
zombie.takeDamage(self.damage, 'frozenpea');
// Apply slow effect only if not a Zomboni or NewspaperZombie
if (zombie.constructor !== Zomboni && zombie.constructor !== NewspaperZombie) {
if (!zombie.originalSpeed) {
zombie.originalSpeed = zombie.speed;
}
zombie.speed = zombie.originalSpeed * self.slowFactor;
zombie.slowTimer = self.slowDuration;
// Visual effect for slowed zombie
tween(zombie, {
tint: 0x87ceeb
}, {
duration: 300,
easing: tween.easeOut
});
}
}
self.destroy();
for (var j = 0; j < frozenpeas.length; j++) {
if (frozenpeas[j] === self) {
frozenpeas.splice(j, 1);
break;
}
}
return;
}
}
// Remove if off screen
if (self.x > 2200) {
self.destroy();
for (var k = 0; k < frozenpeas.length; k++) {
if (frozenpeas[k] === self) {
frozenpeas.splice(k, 1);
break;
}
}
}
};
return self;
});
var Pea = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('pea', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = 15;
self.gridY = 0;
self.update = function () {
self.x += self.speed;
// Check collision with zombies
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
if (zombie.gridY === self.gridY && self.intersects(zombie)) {
// Check if it's a JesterZombie
if (zombie.isJester) {
// Create reflected pea going back towards plants
var reflectedPea = new ReflectedPea();
reflectedPea.x = zombie.x - 50;
reflectedPea.y = zombie.y;
reflectedPea.gridY = zombie.gridY;
reflectedpeas.push(reflectedPea);
game.addChild(reflectedPea);
// Flash jester zombie to show deflection
LK.effects.flashObject(zombie, 0xffff00, 200);
} else {
zombie.takeDamage(self.damage, 'pea');
}
self.destroy();
for (var j = 0; j < peas.length; j++) {
if (peas[j] === self) {
peas.splice(j, 1);
break;
}
}
return;
}
}
// Remove if off screen
if (self.x > 2200) {
self.destroy();
for (var k = 0; k < peas.length; k++) {
if (peas[k] === self) {
peas.splice(k, 1);
break;
}
}
}
};
return self;
});
var Peashooter = Container.expand(function () {
var self = Container.call(this);
var graphics = 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.gridX = 0;
self.gridY = 0;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
self.destroy();
for (var i = 0; i < plants.length; i++) {
if (plants[i] === self) {
plants.splice(i, 1);
break;
}
}
var gridKey = self.gridX + ',' + self.gridY;
delete plantGrid[gridKey];
} else {
// Visual damage feedback
var damageRatio = self.health / self.maxHealth;
graphics.alpha = 0.3 + damageRatio * 0.7;
}
};
self.update = function () {
self.shootTimer++;
if (self.shootTimer >= self.shootDelay) {
// Check if there's a zombie in this lane
var hasZombieInLane = false;
for (var i = 0; i < zombies.length; i++) {
if (zombies[i].gridY === self.gridY && zombies[i].x > self.x) {
hasZombieInLane = true;
break;
}
}
if (hasZombieInLane) {
self.shoot();
self.shootTimer = 0;
}
}
};
self.shoot = function () {
var pea = new Pea();
pea.x = self.x + 60;
pea.y = self.y;
pea.gridY = self.gridY;
peas.push(pea);
game.addChild(pea);
LK.getSound('shoot').play();
};
return self;
});
var Potatomine = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('potatomine', {
anchorX: 0.5,
anchorY: 0.5
});
self.cost = 25;
self.health = 100;
self.maxHealth = 100;
self.gridX = 0;
self.gridY = 0;
self.chargeTimer = 0;
self.maxChargeTime = 900; // 15 seconds at 60fps
self.isCharged = false;
self.hasExploded = false;
// Start with black color (charging state)
graphics.tint = 0x000000;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
self.destroy();
for (var i = 0; i < plants.length; i++) {
if (plants[i] === self) {
plants.splice(i, 1);
break;
}
}
var gridKey = self.gridX + ',' + self.gridY;
delete plantGrid[gridKey];
} else {
// Visual damage feedback
var damageRatio = self.health / self.maxHealth;
graphics.alpha = 0.3 + damageRatio * 0.7;
}
};
self.update = function () {
// Handle charging timer
if (!self.isCharged && !self.hasExploded) {
self.chargeTimer++;
if (self.chargeTimer >= self.maxChargeTime) {
self.isCharged = true;
// Change to normal color when charged
tween(graphics, {
tint: 0xffffff
}, {
duration: 500,
easing: tween.easeOut
});
}
return; // Don't explode while charging
}
// Check for zombies touching this potatomine
if (self.isCharged && !self.hasExploded) {
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
if (zombie.gridY === self.gridY && self.intersects(zombie)) {
self.explode();
break;
}
}
}
};
self.explode = function () {
if (self.hasExploded) return;
self.hasExploded = true;
// Flash white before exploding
LK.effects.flashObject(self, 0xffffff, 200);
// Scale up effect for explosion
tween(graphics, {
scaleX: 3,
scaleY: 3,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Remove from plants array
for (var i = 0; i < plants.length; i++) {
if (plants[i] === self) {
plants.splice(i, 1);
break;
}
}
// Remove from plant grid
var gridKey = self.gridX + ',' + self.gridY;
delete plantGrid[gridKey];
// Destroy the potatomine
self.destroy();
}
});
// Eliminate zombies in a 200 pixel radius
var explosionRadius = 200;
for (var i = zombies.length - 1; i >= 0; i--) {
var zombie = zombies[i];
var distance = Math.sqrt(Math.pow(zombie.x - self.x, 2) + Math.pow(zombie.y - self.y, 2));
if (distance <= explosionRadius) {
// Special handling for Gargantuar and NewspaperZombie
if (zombie.constructor === Gargantuar || zombie.constructor === NewspaperZombie) {
// Flash zombie red
LK.effects.flashObject(zombie, 0xff0000, 200);
// Reduce to half health
zombie.health = zombie.maxHealth / 2;
// Visual effect for being damaged but not destroyed
tween(zombie, {
tint: 0xff4444
}, {
duration: 500,
easing: tween.easeOut
});
// Check if zombie should be eliminated after damage
if (zombie.health <= 0) {
zombie.destroy();
zombies.splice(i, 1);
zombiesKilled++;
}
} else {
// Normal zombies - eliminate immediately
LK.effects.flashObject(zombie, 0xff0000, 200);
zombie.destroy();
zombies.splice(i, 1);
zombiesKilled++;
}
}
}
};
return self;
});
var Puffshroom = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('puffshroom', {
anchorX: 0.5,
anchorY: 0.5
});
self.cost = 0;
self.health = 100;
self.maxHealth = 100;
self.shootTimer = 0;
self.shootDelay = 120; // 2 seconds at 60fps
self.gridX = 0;
self.gridY = 0;
self.detectionRange = 3; // 3 tiles
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
self.destroy();
for (var i = 0; i < plants.length; i++) {
if (plants[i] === self) {
plants.splice(i, 1);
break;
}
}
var gridKey = self.gridX + ',' + self.gridY;
delete plantGrid[gridKey];
} else {
// Visual damage feedback
var damageRatio = self.health / self.maxHealth;
graphics.alpha = 0.3 + damageRatio * 0.7;
}
};
self.update = function () {
self.shootTimer++;
if (self.shootTimer >= self.shootDelay) {
// Check if there's a zombie within 3 tiles in this lane
var hasZombieInRange = false;
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
if (zombie.gridY === self.gridY && zombie.x > self.x) {
var distance = Math.abs(zombie.x - self.x);
var tileDistance = distance / gridWidth;
if (tileDistance <= self.detectionRange) {
hasZombieInRange = true;
break;
}
}
}
if (hasZombieInRange) {
self.shoot();
self.shootTimer = 0;
}
}
};
self.shoot = function () {
var spore = new Spore();
spore.x = self.x + 40;
spore.y = self.y;
spore.gridY = self.gridY;
spores.push(spore);
game.addChild(spore);
LK.getSound('shoot').play();
};
return self;
});
var ReflectedFrozenPea = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('frozenpea', {
anchorX: 0.5,
anchorY: 0.5
});
// Tint dark red to show it's reflected
graphics.tint = 0xaa2222;
self.speed = -8; // Negative speed to go backwards
self.damage = 20; // Slightly more damage than regular frozen peas
self.gridY = 0;
self.update = function () {
self.x += self.speed;
// Check collision with plants
for (var i = 0; i < plants.length; i++) {
var plant = plants[i];
if (plant.gridY === self.gridY && self.intersects(plant)) {
if (plant.takeDamage) {
plant.takeDamage(self.damage);
}
self.destroy();
for (var j = 0; j < reflectedfrozenpeas.length; j++) {
if (reflectedfrozenpeas[j] === self) {
reflectedfrozenpeas.splice(j, 1);
break;
}
}
return;
}
}
// Remove if off screen (left side)
if (self.x < -100) {
self.destroy();
for (var k = 0; k < reflectedfrozenpeas.length; k++) {
if (reflectedfrozenpeas[k] === self) {
reflectedfrozenpeas.splice(k, 1);
break;
}
}
}
};
return self;
});
var ReflectedPea = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('pea', {
anchorX: 0.5,
anchorY: 0.5
});
// Tint red to show it's reflected
graphics.tint = 0xff4444;
self.speed = -8; // Negative speed to go backwards
self.damage = 20; // Slightly more damage than regular peas
self.gridY = 0;
self.update = function () {
self.x += self.speed;
// Check collision with plants
for (var i = 0; i < plants.length; i++) {
var plant = plants[i];
if (plant.gridY === self.gridY && self.intersects(plant)) {
if (plant.takeDamage) {
plant.takeDamage(self.damage);
}
self.destroy();
for (var j = 0; j < reflectedpeas.length; j++) {
if (reflectedpeas[j] === self) {
reflectedpeas.splice(j, 1);
break;
}
}
return;
}
}
// Remove if off screen (left side)
if (self.x < -100) {
self.destroy();
for (var k = 0; k < reflectedpeas.length; k++) {
if (reflectedpeas[k] === self) {
reflectedpeas.splice(k, 1);
break;
}
}
}
};
return self;
});
var ReflectedSpore = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('spore', {
anchorX: 0.5,
anchorY: 0.5
});
// Tint dark brown to show it's reflected
graphics.tint = 0x654321;
self.speed = -6;
self.damage = 15;
self.gridY = 0;
self.update = function () {
self.x += self.speed;
// Check collision with plants
for (var i = 0; i < plants.length; i++) {
var plant = plants[i];
if (plant.gridY === self.gridY && self.intersects(plant)) {
if (plant.takeDamage) {
plant.takeDamage(self.damage);
}
self.destroy();
for (var j = 0; j < reflectedspores.length; j++) {
if (reflectedspores[j] === self) {
reflectedspores.splice(j, 1);
break;
}
}
return;
}
}
// Remove if off screen (left side)
if (self.x < -100) {
self.destroy();
for (var k = 0; k < reflectedspores.length; k++) {
if (reflectedspores[k] === self) {
reflectedspores.splice(k, 1);
break;
}
}
}
};
return self;
});
var Repeater = Container.expand(function () {
var self = Container.call(this);
var graphics = 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.secondShotTimer = 0;
self.secondShotDelay = 15; // 0.25 seconds delay between shots
self.waitingForSecondShot = false;
self.gridX = 0;
self.gridY = 0;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
self.destroy();
for (var i = 0; i < plants.length; i++) {
if (plants[i] === self) {
plants.splice(i, 1);
break;
}
}
var gridKey = self.gridX + ',' + self.gridY;
delete plantGrid[gridKey];
} else {
// Visual damage feedback
var damageRatio = self.health / self.maxHealth;
graphics.alpha = 0.3 + damageRatio * 0.7;
}
};
self.update = function () {
// Handle second shot timing
if (self.waitingForSecondShot) {
self.secondShotTimer++;
if (self.secondShotTimer >= self.secondShotDelay) {
self.shoot(); // Fire second pea
self.waitingForSecondShot = false;
self.secondShotTimer = 0;
}
} else {
self.shootTimer++;
if (self.shootTimer >= self.shootDelay) {
// Check if there's a zombie in this lane
var hasZombieInLane = false;
for (var i = 0; i < zombies.length; i++) {
if (zombies[i].gridY === self.gridY && zombies[i].x > self.x) {
hasZombieInLane = true;
break;
}
}
if (hasZombieInLane) {
self.shoot(); // Fire first pea
self.waitingForSecondShot = true; // Queue second shot
self.shootTimer = 0;
}
}
}
};
self.shoot = function () {
var pea = new Pea();
pea.x = self.x + 60;
pea.y = self.y;
pea.gridY = self.gridY;
peas.push(pea);
game.addChild(pea);
LK.getSound('shoot').play();
};
return self;
});
var Snowshooter = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('snowshooter', {
anchorX: 0.5,
anchorY: 0.5
});
self.cost = 175;
self.health = 100;
self.maxHealth = 100;
self.shootTimer = 0;
self.shootDelay = 90; // 1.5 seconds at 60fps
self.gridX = 0;
self.gridY = 0;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
self.destroy();
for (var i = 0; i < plants.length; i++) {
if (plants[i] === self) {
plants.splice(i, 1);
break;
}
}
var gridKey = self.gridX + ',' + self.gridY;
delete plantGrid[gridKey];
} else {
// Visual damage feedback
var damageRatio = self.health / self.maxHealth;
graphics.alpha = 0.3 + damageRatio * 0.7;
}
};
self.update = function () {
self.shootTimer++;
if (self.shootTimer >= self.shootDelay) {
// Check if there's a zombie in this lane
var hasZombieInLane = false;
for (var i = 0; i < zombies.length; i++) {
if (zombies[i].gridY === self.gridY && zombies[i].x > self.x) {
hasZombieInLane = true;
break;
}
}
if (hasZombieInLane) {
self.shoot();
self.shootTimer = 0;
}
}
};
self.shoot = function () {
var frozenpea = new FrozenPea();
frozenpea.x = self.x + 60;
frozenpea.y = self.y;
frozenpea.gridY = self.gridY;
frozenpeas.push(frozenpea);
game.addChild(frozenpea);
LK.getSound('shoot').play();
};
return self;
});
var SplitPea = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('splitpea', {
anchorX: 0.5,
anchorY: 0.5
});
self.cost = 125;
self.health = 100;
self.maxHealth = 100;
self.shootTimer = 0;
self.shootDelay = 90; // 1.5 seconds at 60fps
self.gridX = 0;
self.gridY = 0;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
self.destroy();
for (var i = 0; i < plants.length; i++) {
if (plants[i] === self) {
plants.splice(i, 1);
break;
}
}
var gridKey = self.gridX + ',' + self.gridY;
delete plantGrid[gridKey];
} else {
// Visual damage feedback
var damageRatio = self.health / self.maxHealth;
graphics.alpha = 0.3 + damageRatio * 0.7;
}
};
self.update = function () {
self.shootTimer++;
if (self.shootTimer >= self.shootDelay) {
// Check if there's a zombie in this lane (both front and back)
var hasZombieInLane = false;
for (var i = 0; i < zombies.length; i++) {
if (zombies[i].gridY === self.gridY) {
hasZombieInLane = true;
break;
}
}
if (hasZombieInLane) {
self.shoot();
self.shootTimer = 0;
}
}
};
self.shoot = function () {
// Shoot one pea forward
var forwardPea = new Pea();
forwardPea.x = self.x + 60;
forwardPea.y = self.y;
forwardPea.gridY = self.gridY;
peas.push(forwardPea);
game.addChild(forwardPea);
// Shoot two peas backward
var backwardPea1 = new BackwardPea();
backwardPea1.x = self.x - 60;
backwardPea1.y = self.y - 20;
backwardPea1.gridY = self.gridY;
backwardpeas.push(backwardPea1);
game.addChild(backwardPea1);
var backwardPea2 = new BackwardPea();
backwardPea2.x = self.x - 60;
backwardPea2.y = self.y + 20;
backwardPea2.gridY = self.gridY;
backwardpeas.push(backwardPea2);
game.addChild(backwardPea2);
LK.getSound('shoot').play();
};
return self;
});
var Spore = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('spore', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.damage = 10;
self.gridY = 0;
self.update = function () {
self.x += self.speed;
// Check collision with zombies
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
if (zombie.gridY === self.gridY && self.intersects(zombie)) {
// Check if it's a JesterZombie
if (zombie.isJester) {
// Create reflected spore going back towards plants
var reflectedSpore = new ReflectedSpore();
reflectedSpore.x = zombie.x - 50;
reflectedSpore.y = zombie.y;
reflectedSpore.gridY = zombie.gridY;
reflectedspores.push(reflectedSpore);
game.addChild(reflectedSpore);
// Flash jester zombie to show deflection
LK.effects.flashObject(zombie, 0xffff00, 200);
} else {
zombie.takeDamage(self.damage, 'spore');
}
self.destroy();
for (var j = 0; j < spores.length; j++) {
if (spores[j] === self) {
spores.splice(j, 1);
break;
}
}
return;
}
}
// Remove if off screen
if (self.x > 2200) {
self.destroy();
for (var k = 0; k < spores.length; k++) {
if (spores[k] === self) {
spores.splice(k, 1);
break;
}
}
}
};
return self;
});
var Sun = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('sun', {
anchorX: 0.5,
anchorY: 0.5
});
self.value = 25;
self.lifeTimer = 0;
self.maxLife = 600; // 10 seconds
self.update = function () {
self.lifeTimer++;
if (self.lifeTimer > self.maxLife) {
self.destroy();
for (var i = 0; i < suns.length; i++) {
if (suns[i] === self) {
suns.splice(i, 1);
break;
}
}
}
};
self.down = function (x, y, obj) {
sunPoints += self.value;
updateSunDisplay();
self.destroy();
for (var i = 0; i < suns.length; i++) {
if (suns[i] === self) {
suns.splice(i, 1);
break;
}
}
LK.getSound('collect').play();
};
return self;
});
var Sunflower = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('sunflower', {
anchorX: 0.5,
anchorY: 0.5
});
self.cost = 50;
self.health = 100;
self.maxHealth = 100;
self.sunTimer = 0;
self.sunDelay = 600; // 10 seconds at 60fps
self.gridX = 0;
self.gridY = 0;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
self.destroy();
for (var i = 0; i < plants.length; i++) {
if (plants[i] === self) {
plants.splice(i, 1);
break;
}
}
var gridKey = self.gridX + ',' + self.gridY;
delete plantGrid[gridKey];
} else {
// Visual damage feedback
var damageRatio = self.health / self.maxHealth;
graphics.alpha = 0.3 + damageRatio * 0.7;
}
};
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) * 50;
sun.y = self.y + (Math.random() - 0.5) * 50;
suns.push(sun);
game.addChild(sun);
};
return self;
});
var WallNut = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('wallnut', {
anchorX: 0.5,
anchorY: 0.5
});
self.cost = 50;
self.health = 300;
self.maxHealth = 300;
self.gridX = 0;
self.gridY = 0;
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.destroy();
for (var i = 0; i < plants.length; i++) {
if (plants[i] === self) {
plants.splice(i, 1);
break;
}
}
var gridKey = self.gridX + ',' + self.gridY;
delete plantGrid[gridKey];
} else {
// Visual damage feedback
var damageRatio = self.health / self.maxHealth;
graphics.alpha = 0.3 + damageRatio * 0.7;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x8BC34A
});
/****
* Game Code
****/
// Sounds
// Zombies
// Projectiles and collectibles
// Plants
// Game grid and UI elements
// Game variables
var sunPoints = 150;
var currentWave = 1;
var maxWaves = 20;
var waveTimer = 0;
var waveDelay = 1800; // 30 seconds
var zombieSpawnTimer = 0;
var zombieSpawnDelay = 240; // 4 seconds
var zombiesPerWave = 5;
var zombiesSpawned = 0;
var zombiesKilled = 0;
var totalZombiesToKill = 0;
var waveStarted = false;
var zombieSpawnQueue = [];
// Game arrays
var plants = [];
var zombies = [];
var peas = [];
var frozenpeas = [];
var reflectedpeas = [];
var reflectedfrozenpeas = [];
var backwardpeas = [];
var spores = [];
var reflectedspores = [];
var suns = [];
var plantGrid = {}; // Track occupied grid positions
// Grid settings
var gridStartX = 400;
var gridStartY = 400;
var gridWidth = 320;
var gridHeight = 227;
var gridCols = 5;
var gridRows = 5;
// Selected plant type
var selectedPlantType = null;
// Plant selector management
var currentSelectorPage = 1;
var maxSelectorPages = 2;
// Create lawn grid
for (var row = 0; row < gridRows; row++) {
for (var col = 0; col < gridCols; col++) {
var tile = game.addChild(LK.getAsset('grass', {
x: gridStartX + col * gridWidth,
y: gridStartY + row * gridHeight,
anchorX: 0,
anchorY: 0
}));
}
}
// Create paths (zombie lanes)
for (var lane = 0; lane < gridRows; lane++) {
var pathTile = game.addChild(LK.getAsset('path', {
x: gridStartX + gridCols * gridWidth,
y: gridStartY + lane * gridHeight,
anchorX: 0,
anchorY: 0
}));
}
// Create house
var house = game.addChild(LK.getAsset('house', {
x: 50,
y: gridStartY + gridRows * gridHeight / 2 - 200,
anchorX: 0,
anchorY: 0
}));
// UI Setup
var sunDisplay = new Text2(sunPoints.toString(), {
size: 80,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 3
});
sunDisplay.anchor.set(0, 0);
LK.gui.topLeft.addChild(sunDisplay);
sunDisplay.x = 120;
sunDisplay.y = 20;
var waveDisplay = new Text2('Wave: ' + currentWave + '/' + maxWaves, {
size: 60,
fill: 0x000000
});
waveDisplay.anchor.set(0.5, 0);
LK.gui.top.addChild(waveDisplay);
waveDisplay.y = 20;
// Plant selection buttons - repositioned for new order
var peashooterBtn = LK.getAsset('peashooter', {
x: 250,
y: 2500,
scaleX: 0.8,
scaleY: 0.8,
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(peashooterBtn);
var sunflowerBtn = LK.getAsset('sunflower', {
x: 450,
y: 2500,
scaleX: 0.8,
scaleY: 0.8,
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(sunflowerBtn);
var cherrypopperBtn = LK.getAsset('cherrypopper', {
x: 650,
y: 2500,
scaleX: 0.8,
scaleY: 0.8,
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(cherrypopperBtn);
var wallnutBtn = LK.getAsset('wallnut', {
x: 850,
y: 2500,
scaleX: 0.8,
scaleY: 0.8,
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(wallnutBtn);
var potatomineBtn = LK.getAsset('potatomine', {
x: 1050,
y: 2500,
scaleX: 0.8,
scaleY: 0.8,
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(potatomineBtn);
var snowshooterBtn = LK.getAsset('snowshooter', {
x: 1250,
y: 2500,
scaleX: 0.8,
scaleY: 0.8,
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(snowshooterBtn);
var chomperBtn = LK.getAsset('chomper', {
x: 1450,
y: 2500,
scaleX: 0.8,
scaleY: 0.8,
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(chomperBtn);
var repeaterBtn = LK.getAsset('repeater', {
x: 1650,
y: 2500,
scaleX: 0.8,
scaleY: 0.8,
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(repeaterBtn);
var splitpeaBtn = LK.getAsset('splitpea', {
x: 250,
y: 2500,
scaleX: 0.8,
scaleY: 0.8,
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(splitpeaBtn);
var puffshroomBtn = LK.getAsset('puffshroom', {
x: 450,
y: 2500,
scaleX: 0.8,
scaleY: 0.8,
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(puffshroomBtn);
var shovelBtn = LK.getAsset('shovel', {
x: 1900,
y: 150,
scaleX: 1.5,
scaleY: 1.5,
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(shovelBtn);
// Cost display texts - updated positions to match new layout
var peaCostTxt = new Text2('100', {
size: 70,
fill: 0x000000
});
peaCostTxt.anchor.set(0.5, 0);
game.addChild(peaCostTxt);
peaCostTxt.x = 250;
peaCostTxt.y = 2580;
var sunCostTxt = new Text2('50', {
size: 70,
fill: 0x000000
});
sunCostTxt.anchor.set(0.5, 0);
game.addChild(sunCostTxt);
sunCostTxt.x = 450;
sunCostTxt.y = 2580;
var cherryCostTxt = new Text2('150', {
size: 70,
fill: 0x000000
});
cherryCostTxt.anchor.set(0.5, 0);
game.addChild(cherryCostTxt);
cherryCostTxt.x = 650;
cherryCostTxt.y = 2580;
var wallCostTxt = new Text2('50', {
size: 70,
fill: 0x000000
});
wallCostTxt.anchor.set(0.5, 0);
game.addChild(wallCostTxt);
wallCostTxt.x = 850;
wallCostTxt.y = 2580;
var potatomineCosTxt = new Text2('25', {
size: 70,
fill: 0x000000
});
potatomineCosTxt.anchor.set(0.5, 0);
game.addChild(potatomineCosTxt);
potatomineCosTxt.x = 1050;
potatomineCosTxt.y = 2580;
var snowCostTxt = new Text2('175', {
size: 70,
fill: 0x000000
});
snowCostTxt.anchor.set(0.5, 0);
game.addChild(snowCostTxt);
snowCostTxt.x = 1250;
snowCostTxt.y = 2580;
var chomperCostTxt = new Text2('150', {
size: 70,
fill: 0x000000
});
chomperCostTxt.anchor.set(0.5, 0);
game.addChild(chomperCostTxt);
chomperCostTxt.x = 1450;
chomperCostTxt.y = 2580;
var repeaterCostTxt = new Text2('200', {
size: 70,
fill: 0x000000
});
repeaterCostTxt.anchor.set(0.5, 0);
game.addChild(repeaterCostTxt);
repeaterCostTxt.x = 1650;
repeaterCostTxt.y = 2580;
var splitpeaCostTxt = new Text2('125', {
size: 70,
fill: 0x000000
});
splitpeaCostTxt.anchor.set(0.5, 0);
game.addChild(splitpeaCostTxt);
splitpeaCostTxt.x = 250;
splitpeaCostTxt.y = 2580;
var puffshroomCostTxt = new Text2('0', {
size: 70,
fill: 0x000000
});
puffshroomCostTxt.anchor.set(0.5, 0);
game.addChild(puffshroomCostTxt);
puffshroomCostTxt.x = 450;
puffshroomCostTxt.y = 2580;
var shovelCostTxt = new Text2('Free', {
size: 70,
fill: 0x000000
});
shovelCostTxt.anchor.set(0.5, 0);
game.addChild(shovelCostTxt);
shovelCostTxt.x = 1900;
shovelCostTxt.y = 230;
// Add left arrow for plant selector
var leftArrow = new Text2('<', {
size: 120,
fill: 0x000000
});
leftArrow.anchor.set(0.5, 0.5);
game.addChild(leftArrow);
leftArrow.x = 50;
leftArrow.y = 2500;
// Add right arrow for plant selector
var rightArrow = new Text2('>', {
size: 120,
fill: 0x000000
});
rightArrow.anchor.set(0.5, 0.5);
game.addChild(rightArrow);
rightArrow.x = 2000;
rightArrow.y = 2500;
// Calculate total zombies for victory condition
for (var w = 1; w <= maxWaves; w++) {
totalZombiesToKill += zombiesPerWave + Math.floor(w / 2);
}
function updateSunDisplay() {
sunDisplay.setText(sunPoints.toString());
}
function updatePlantSelector() {
// Hide all plants first
peashooterBtn.visible = false;
sunflowerBtn.visible = false;
wallnutBtn.visible = false;
snowshooterBtn.visible = false;
repeaterBtn.visible = false;
cherrypopperBtn.visible = false;
splitpeaBtn.visible = false;
chomperBtn.visible = false;
potatomineBtn.visible = false;
puffshroomBtn.visible = false;
// Hide all cost texts
peaCostTxt.visible = false;
sunCostTxt.visible = false;
wallCostTxt.visible = false;
snowCostTxt.visible = false;
repeaterCostTxt.visible = false;
cherryCostTxt.visible = false;
splitpeaCostTxt.visible = false;
chomperCostTxt.visible = false;
potatomineCosTxt.visible = false;
puffshroomCostTxt.visible = false;
if (currentSelectorPage === 1) {
// First page - show: Peashooter, Sunflower, Cherrypopper, Wallnut, Potatomine, Snowshooter, Chomper, and Repeater
peashooterBtn.visible = true;
sunflowerBtn.visible = true;
cherrypopperBtn.visible = true;
wallnutBtn.visible = true;
potatomineBtn.visible = true;
snowshooterBtn.visible = true;
chomperBtn.visible = true;
repeaterBtn.visible = true;
// Show corresponding cost texts
peaCostTxt.visible = true;
sunCostTxt.visible = true;
cherryCostTxt.visible = true;
wallCostTxt.visible = true;
potatomineCosTxt.visible = true;
snowCostTxt.visible = true;
chomperCostTxt.visible = true;
repeaterCostTxt.visible = true;
} else if (currentSelectorPage === 2) {
// Second page - show splitpea and puffshroom (only from wave 11)
splitpeaBtn.visible = true;
splitpeaCostTxt.visible = true;
if (currentWave >= 11) {
puffshroomBtn.visible = true;
puffshroomCostTxt.visible = true;
}
}
}
function getGridPosition(x, y) {
var col = Math.floor((x - gridStartX) / gridWidth);
var row = Math.floor((y - gridStartY) / gridHeight);
if (col >= 0 && col < gridCols && row >= 0 && row < gridRows) {
return {
col: col,
row: row,
x: gridStartX + col * gridWidth + gridWidth / 2,
y: gridStartY + row * gridHeight + gridHeight / 2
};
}
return null;
}
function spawnZombie() {
var lane = Math.floor(Math.random() * gridRows);
var zombieType = Math.random();
var zombie;
// Higher spawn rates for special zombies from wave 15 onwards
if (currentWave >= 15) {
if (zombieType < 0.25) {
zombie = new Gargantuar();
} else if (zombieType < 0.50) {
zombie = new Zomboni();
} else if (zombieType < 0.65) {
zombie = new JesterZombie();
} else if (zombieType < 0.75) {
zombie = new TankZombie();
} else if (zombieType < 0.80) {
zombie = new JackInBoxZombie();
} else if (zombieType < 0.86) {
zombie = new MinerZombie();
} else if (zombieType < 0.92) {
zombie = new FastZombie();
} else if (zombieType < 0.98) {
zombie = new NewspaperZombie();
} else {
zombie = new BasicZombie();
}
} else {
// Original spawn rates for waves before 15
if (currentWave > 8 && zombieType < 0.05) {
zombie = new Gargantuar();
} else if (currentWave > 6 && zombieType < 0.15) {
zombie = new Zomboni();
} else if (currentWave > 5 && zombieType < 0.25) {
zombie = new TankZombie();
} else if (currentWave > 4 && zombieType < 0.35) {
zombie = new JesterZombie();
} else if (currentWave > 2 && zombieType < 0.45) {
zombie = new JackInBoxZombie();
} else if (currentWave > 3 && zombieType < 0.60) {
zombie = new MinerZombie();
} else if (currentWave > 3 && zombieType < 0.75) {
zombie = new FastZombie();
} else if (currentWave > 1 && zombieType < 0.90) {
zombie = new NewspaperZombie();
} else {
zombie = new BasicZombie();
}
}
zombie.x = gridStartX + gridCols * gridWidth + 100;
zombie.y = gridStartY + lane * gridHeight + gridHeight / 2;
zombie.gridY = lane;
// Add spawn delay animation - zombie starts invisible and fades in
zombie.alpha = 0;
zombie.scaleX = 0.5;
zombie.scaleY = 0.5;
// Animate zombie spawning in
tween(zombie, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.easeOut
});
zombies.push(zombie);
game.addChild(zombie);
zombiesSpawned++;
}
function startNextWave() {
if (currentWave <= maxWaves) {
zombiesPerWave = 5 + Math.floor(currentWave / 2);
zombiesSpawned = 0;
waveStarted = false;
zombieSpawnQueue = [];
waveDisplay.setText('Wave: ' + currentWave + '/' + maxWaves);
// Change background color to dark blue from wave 11
if (currentWave >= 11) {
game.setBackgroundColor(0x191970); // Dark blue
}
// Flash screen red when wave increases
LK.effects.flashScreen(0xff0000, 1000);
// Add delay before wave actually starts spawning zombies
// For wave 1, add 10 second delay. For other waves, 2 second delay
var waveStartDelay = currentWave === 1 ? 10000 : 2000;
LK.setTimeout(function () {
waveStarted = true;
// Pre-queue zombie spawn times with delays
// Use faster spawn delay from wave 5 onwards, and even faster from wave 11
var currentSpawnDelay;
if (currentWave >= 11) {
currentSpawnDelay = 80; // Even faster from wave 11 (1.33 seconds)
} else if (currentWave >= 5) {
currentSpawnDelay = 120; // Faster from wave 5 (2 seconds)
} else {
currentSpawnDelay = zombieSpawnDelay; // Normal speed (4 seconds)
}
for (var i = 0; i < zombiesPerWave; i++) {
zombieSpawnQueue.push({
spawnTime: LK.ticks + i * currentSpawnDelay + Math.random() * 60,
// Add some randomness (reduced for faster waves)
spawned: false
});
}
}, waveStartDelay);
}
}
// Generate sun periodically
var sunGenerationTimer = 0;
var sunGenerationDelay = 480; // 8 seconds
// Event handlers
peashooterBtn.down = function () {
if (sunPoints >= 100) {
selectedPlantType = 'peashooter';
LK.effects.flashObject(peashooterBtn, 0x00ff00, 300);
}
};
sunflowerBtn.down = function () {
if (sunPoints >= 50) {
selectedPlantType = 'sunflower';
LK.effects.flashObject(sunflowerBtn, 0x00ff00, 300);
}
};
wallnutBtn.down = function () {
if (sunPoints >= 50) {
selectedPlantType = 'wallnut';
LK.effects.flashObject(wallnutBtn, 0x00ff00, 300);
}
};
snowshooterBtn.down = function () {
if (sunPoints >= 175) {
selectedPlantType = 'snowshooter';
LK.effects.flashObject(snowshooterBtn, 0x00ff00, 300);
}
};
repeaterBtn.down = function () {
if (sunPoints >= 200) {
selectedPlantType = 'repeater';
LK.effects.flashObject(repeaterBtn, 0x00ff00, 300);
}
};
cherrypopperBtn.down = function () {
if (sunPoints >= 150) {
selectedPlantType = 'cherrypopper';
LK.effects.flashObject(cherrypopperBtn, 0x00ff00, 300);
}
};
splitpeaBtn.down = function () {
if (sunPoints >= 125) {
selectedPlantType = 'splitpea';
LK.effects.flashObject(splitpeaBtn, 0x00ff00, 300);
}
};
chomperBtn.down = function () {
if (sunPoints >= 150) {
selectedPlantType = 'chomper';
LK.effects.flashObject(chomperBtn, 0x00ff00, 300);
}
};
potatomineBtn.down = function () {
if (sunPoints >= 25) {
selectedPlantType = 'potatomine';
LK.effects.flashObject(potatomineBtn, 0x00ff00, 300);
}
};
puffshroomBtn.down = function () {
if (currentWave >= 11) {
selectedPlantType = 'puffshroom';
LK.effects.flashObject(puffshroomBtn, 0x00ff00, 300);
}
};
shovelBtn.down = function () {
selectedPlantType = 'shovel';
LK.effects.flashObject(shovelBtn, 0x00ff00, 300);
};
leftArrow.down = function () {
if (currentSelectorPage > 1) {
currentSelectorPage--;
updatePlantSelector();
LK.effects.flashObject(leftArrow, 0x00ff00, 200);
} else {
// Flash gray if can't go back further
LK.effects.flashObject(leftArrow, 0x888888, 200);
}
};
rightArrow.down = function () {
if (currentSelectorPage < maxSelectorPages) {
currentSelectorPage++;
updatePlantSelector();
LK.effects.flashObject(rightArrow, 0x00ff00, 200);
} else {
// Flash gray if can't go forward further
LK.effects.flashObject(rightArrow, 0x888888, 200);
}
};
game.down = function (x, y, obj) {
var gridPos = getGridPosition(x, y);
if (gridPos && selectedPlantType) {
var gridKey = gridPos.col + ',' + gridPos.row;
// Handle shovel - remove existing plant
if (selectedPlantType === 'shovel') {
if (plantGrid[gridKey]) {
var plantToRemove = plantGrid[gridKey];
// Remove from plants array
for (var i = 0; i < plants.length; i++) {
if (plants[i] === plantToRemove) {
plants.splice(i, 1);
break;
}
}
// Remove from grid
delete plantGrid[gridKey];
// Destroy the plant
plantToRemove.destroy();
selectedPlantType = null;
}
return;
}
// Check if position is already occupied
if (plantGrid[gridKey]) {
return;
}
var plant = null;
var cost = 0;
if (selectedPlantType === 'peashooter' && sunPoints >= 100) {
plant = new Peashooter();
cost = 100;
} else if (selectedPlantType === 'sunflower' && sunPoints >= 50) {
plant = new Sunflower();
cost = 50;
} else if (selectedPlantType === 'wallnut' && sunPoints >= 50) {
plant = new WallNut();
cost = 50;
} else if (selectedPlantType === 'snowshooter' && sunPoints >= 175) {
plant = new Snowshooter();
cost = 175;
} else if (selectedPlantType === 'repeater' && sunPoints >= 200) {
plant = new Repeater();
cost = 200;
} else if (selectedPlantType === 'cherrypopper' && sunPoints >= 150) {
plant = new CherryPopper();
cost = 150;
} else if (selectedPlantType === 'splitpea' && sunPoints >= 125) {
plant = new SplitPea();
cost = 125;
} else if (selectedPlantType === 'chomper' && sunPoints >= 150) {
plant = new Chomper();
cost = 150;
} else if (selectedPlantType === 'potatomine' && sunPoints >= 25) {
plant = new Potatomine();
cost = 25;
} else if (selectedPlantType === 'puffshroom' && currentWave >= 11) {
plant = new Puffshroom();
cost = 0;
}
if (plant) {
plant.x = gridPos.x;
plant.y = gridPos.y;
plant.gridX = gridPos.col;
plant.gridY = gridPos.row;
plants.push(plant);
game.addChild(plant);
plantGrid[gridKey] = plant;
sunPoints -= cost;
updateSunDisplay();
selectedPlantType = null;
LK.getSound('plant').play();
}
}
};
game.update = function () {
// Generate sun periodically
sunGenerationTimer++;
if (sunGenerationTimer >= sunGenerationDelay) {
var sun = new Sun();
sun.x = 200 + Math.random() * 1600;
sun.y = -100; // Start above screen
sun.targetY = 200 + Math.random() * 1000; // Final position
sun.isFalling = true; // Mark as falling sun
// Animate falling from top to bottom
tween(sun, {
y: sun.targetY
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
sun.isFalling = false;
}
});
suns.push(sun);
game.addChild(sun);
sunGenerationTimer = 0;
}
// Wave management
if (currentWave <= maxWaves) {
waveTimer++;
// Spawn zombies for current wave using queue system
if (waveStarted && zombiesSpawned < zombiesPerWave) {
for (var i = 0; i < zombieSpawnQueue.length; i++) {
var queueItem = zombieSpawnQueue[i];
if (!queueItem.spawned && LK.ticks >= queueItem.spawnTime) {
spawnZombie();
queueItem.spawned = true;
break; // Only spawn one zombie per frame
}
}
}
// Start next wave
if (zombiesSpawned >= zombiesPerWave && zombies.length === 0) {
currentWave++;
if (currentWave <= maxWaves) {
startNextWave();
waveTimer = 0;
}
}
}
// Check victory condition
if (currentWave > maxWaves && zombies.length === 0) {
LK.showYouWin();
}
// Handle slow effects on zombies
for (var z = 0; z < zombies.length; z++) {
var zombie = zombies[z];
if (zombie.slowTimer) {
zombie.slowTimer--;
if (zombie.slowTimer <= 0) {
// Restore original speed
if (zombie.originalSpeed) {
zombie.speed = zombie.originalSpeed;
}
// Remove slow tint
tween(zombie, {
tint: 0xffffff
}, {
duration: 300,
easing: tween.easeOut
});
zombie.slowTimer = null;
}
}
}
// Update button availability
peashooterBtn.alpha = sunPoints >= 100 ? 1.0 : 0.5;
sunflowerBtn.alpha = sunPoints >= 50 ? 1.0 : 0.5;
wallnutBtn.alpha = sunPoints >= 50 ? 1.0 : 0.5;
snowshooterBtn.alpha = sunPoints >= 175 ? 1.0 : 0.5;
repeaterBtn.alpha = sunPoints >= 200 ? 1.0 : 0.5;
cherrypopperBtn.alpha = sunPoints >= 150 ? 1.0 : 0.5;
splitpeaBtn.alpha = sunPoints >= 125 ? 1.0 : 0.5;
chomperBtn.alpha = sunPoints >= 150 ? 1.0 : 0.5;
potatomineBtn.alpha = sunPoints >= 25 ? 1.0 : 0.5;
puffshroomBtn.alpha = currentWave >= 11 ? 1.0 : 0.5;
shovelBtn.alpha = 1.0; // Shovel is always available
// Update arrow button availability
updateArrowButtons();
};
// Initialize plant selector
updatePlantSelector();
// Update arrow button availability in main update loop
function updateArrowButtons() {
leftArrow.alpha = currentSelectorPage > 1 ? 1.0 : 0.5;
rightArrow.alpha = currentSelectorPage < maxSelectorPages ? 1.0 : 0.5;
}
// Start first wave
startNextWave(); ===================================================================
--- original.js
+++ change.js
@@ -1048,8 +1048,74 @@
}
};
return self;
});
+var Puffshroom = Container.expand(function () {
+ var self = Container.call(this);
+ var graphics = self.attachAsset('puffshroom', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.cost = 0;
+ self.health = 100;
+ self.maxHealth = 100;
+ self.shootTimer = 0;
+ self.shootDelay = 120; // 2 seconds at 60fps
+ self.gridX = 0;
+ self.gridY = 0;
+ self.detectionRange = 3; // 3 tiles
+ self.takeDamage = function (damage) {
+ self.health -= damage;
+ LK.effects.flashObject(self, 0xff0000, 200);
+ if (self.health <= 0) {
+ self.destroy();
+ for (var i = 0; i < plants.length; i++) {
+ if (plants[i] === self) {
+ plants.splice(i, 1);
+ break;
+ }
+ }
+ var gridKey = self.gridX + ',' + self.gridY;
+ delete plantGrid[gridKey];
+ } else {
+ // Visual damage feedback
+ var damageRatio = self.health / self.maxHealth;
+ graphics.alpha = 0.3 + damageRatio * 0.7;
+ }
+ };
+ self.update = function () {
+ self.shootTimer++;
+ if (self.shootTimer >= self.shootDelay) {
+ // Check if there's a zombie within 3 tiles in this lane
+ var hasZombieInRange = false;
+ for (var i = 0; i < zombies.length; i++) {
+ var zombie = zombies[i];
+ if (zombie.gridY === self.gridY && zombie.x > self.x) {
+ var distance = Math.abs(zombie.x - self.x);
+ var tileDistance = distance / gridWidth;
+ if (tileDistance <= self.detectionRange) {
+ hasZombieInRange = true;
+ break;
+ }
+ }
+ }
+ if (hasZombieInRange) {
+ self.shoot();
+ self.shootTimer = 0;
+ }
+ }
+ };
+ self.shoot = function () {
+ var spore = new Spore();
+ spore.x = self.x + 40;
+ spore.y = self.y;
+ spore.gridY = self.gridY;
+ spores.push(spore);
+ game.addChild(spore);
+ LK.getSound('shoot').play();
+ };
+ return self;
+});
var ReflectedFrozenPea = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('frozenpea', {
anchorX: 0.5,
@@ -1134,8 +1200,51 @@
}
};
return self;
});
+var ReflectedSpore = Container.expand(function () {
+ var self = Container.call(this);
+ var graphics = self.attachAsset('spore', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ // Tint dark brown to show it's reflected
+ graphics.tint = 0x654321;
+ self.speed = -6;
+ self.damage = 15;
+ self.gridY = 0;
+ self.update = function () {
+ self.x += self.speed;
+ // Check collision with plants
+ for (var i = 0; i < plants.length; i++) {
+ var plant = plants[i];
+ if (plant.gridY === self.gridY && self.intersects(plant)) {
+ if (plant.takeDamage) {
+ plant.takeDamage(self.damage);
+ }
+ self.destroy();
+ for (var j = 0; j < reflectedspores.length; j++) {
+ if (reflectedspores[j] === self) {
+ reflectedspores.splice(j, 1);
+ break;
+ }
+ }
+ return;
+ }
+ }
+ // Remove if off screen (left side)
+ if (self.x < -100) {
+ self.destroy();
+ for (var k = 0; k < reflectedspores.length; k++) {
+ if (reflectedspores[k] === self) {
+ reflectedspores.splice(k, 1);
+ break;
+ }
+ }
+ }
+ };
+ return self;
+});
var Repeater = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('repeater', {
anchorX: 0.5,
@@ -1342,8 +1451,60 @@
LK.getSound('shoot').play();
};
return self;
});
+var Spore = Container.expand(function () {
+ var self = Container.call(this);
+ var graphics = self.attachAsset('spore', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.speed = 6;
+ self.damage = 10;
+ self.gridY = 0;
+ self.update = function () {
+ self.x += self.speed;
+ // Check collision with zombies
+ for (var i = 0; i < zombies.length; i++) {
+ var zombie = zombies[i];
+ if (zombie.gridY === self.gridY && self.intersects(zombie)) {
+ // Check if it's a JesterZombie
+ if (zombie.isJester) {
+ // Create reflected spore going back towards plants
+ var reflectedSpore = new ReflectedSpore();
+ reflectedSpore.x = zombie.x - 50;
+ reflectedSpore.y = zombie.y;
+ reflectedSpore.gridY = zombie.gridY;
+ reflectedspores.push(reflectedSpore);
+ game.addChild(reflectedSpore);
+ // Flash jester zombie to show deflection
+ LK.effects.flashObject(zombie, 0xffff00, 200);
+ } else {
+ zombie.takeDamage(self.damage, 'spore');
+ }
+ self.destroy();
+ for (var j = 0; j < spores.length; j++) {
+ if (spores[j] === self) {
+ spores.splice(j, 1);
+ break;
+ }
+ }
+ return;
+ }
+ }
+ // Remove if off screen
+ if (self.x > 2200) {
+ self.destroy();
+ for (var k = 0; k < spores.length; k++) {
+ if (spores[k] === self) {
+ spores.splice(k, 1);
+ break;
+ }
+ }
+ }
+ };
+ return self;
+});
var Sun = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('sun', {
anchorX: 0.5,
@@ -1494,8 +1655,10 @@
var frozenpeas = [];
var reflectedpeas = [];
var reflectedfrozenpeas = [];
var backwardpeas = [];
+var spores = [];
+var reflectedspores = [];
var suns = [];
var plantGrid = {}; // Track occupied grid positions
// Grid settings
var gridStartX = 400;
@@ -1635,8 +1798,17 @@
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(splitpeaBtn);
+var puffshroomBtn = LK.getAsset('puffshroom', {
+ x: 450,
+ y: 2500,
+ scaleX: 0.8,
+ scaleY: 0.8,
+ anchorX: 0.5,
+ anchorY: 0.5
+});
+game.addChild(puffshroomBtn);
var shovelBtn = LK.getAsset('shovel', {
x: 1900,
y: 150,
scaleX: 1.5,
@@ -1717,8 +1889,16 @@
splitpeaCostTxt.anchor.set(0.5, 0);
game.addChild(splitpeaCostTxt);
splitpeaCostTxt.x = 250;
splitpeaCostTxt.y = 2580;
+var puffshroomCostTxt = new Text2('0', {
+ size: 70,
+ fill: 0x000000
+});
+puffshroomCostTxt.anchor.set(0.5, 0);
+game.addChild(puffshroomCostTxt);
+puffshroomCostTxt.x = 450;
+puffshroomCostTxt.y = 2580;
var shovelCostTxt = new Text2('Free', {
size: 70,
fill: 0x000000
});
@@ -1761,8 +1941,9 @@
cherrypopperBtn.visible = false;
splitpeaBtn.visible = false;
chomperBtn.visible = false;
potatomineBtn.visible = false;
+ puffshroomBtn.visible = false;
// Hide all cost texts
peaCostTxt.visible = false;
sunCostTxt.visible = false;
wallCostTxt.visible = false;
@@ -1771,8 +1952,9 @@
cherryCostTxt.visible = false;
splitpeaCostTxt.visible = false;
chomperCostTxt.visible = false;
potatomineCosTxt.visible = false;
+ puffshroomCostTxt.visible = false;
if (currentSelectorPage === 1) {
// First page - show: Peashooter, Sunflower, Cherrypopper, Wallnut, Potatomine, Snowshooter, Chomper, and Repeater
peashooterBtn.visible = true;
sunflowerBtn.visible = true;
@@ -1791,11 +1973,15 @@
snowCostTxt.visible = true;
chomperCostTxt.visible = true;
repeaterCostTxt.visible = true;
} else if (currentSelectorPage === 2) {
- // Second page - show splitpea
+ // Second page - show splitpea and puffshroom (only from wave 11)
splitpeaBtn.visible = true;
splitpeaCostTxt.visible = true;
+ if (currentWave >= 11) {
+ puffshroomBtn.visible = true;
+ puffshroomCostTxt.visible = true;
+ }
}
}
function getGridPosition(x, y) {
var col = Math.floor((x - gridStartX) / gridWidth);
@@ -1972,8 +2158,14 @@
selectedPlantType = 'potatomine';
LK.effects.flashObject(potatomineBtn, 0x00ff00, 300);
}
};
+puffshroomBtn.down = function () {
+ if (currentWave >= 11) {
+ selectedPlantType = 'puffshroom';
+ LK.effects.flashObject(puffshroomBtn, 0x00ff00, 300);
+ }
+};
shovelBtn.down = function () {
selectedPlantType = 'shovel';
LK.effects.flashObject(shovelBtn, 0x00ff00, 300);
};
@@ -2052,8 +2244,11 @@
cost = 150;
} else if (selectedPlantType === 'potatomine' && sunPoints >= 25) {
plant = new Potatomine();
cost = 25;
+ } else if (selectedPlantType === 'puffshroom' && currentWave >= 11) {
+ plant = new Puffshroom();
+ cost = 0;
}
if (plant) {
plant.x = gridPos.x;
plant.y = gridPos.y;
@@ -2149,8 +2344,9 @@
cherrypopperBtn.alpha = sunPoints >= 150 ? 1.0 : 0.5;
splitpeaBtn.alpha = sunPoints >= 125 ? 1.0 : 0.5;
chomperBtn.alpha = sunPoints >= 150 ? 1.0 : 0.5;
potatomineBtn.alpha = sunPoints >= 25 ? 1.0 : 0.5;
+ puffshroomBtn.alpha = currentWave >= 11 ? 1.0 : 0.5;
shovelBtn.alpha = 1.0; // Shovel is always available
// Update arrow button availability
updateArrowButtons();
};