/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var HeavyPeashooterPod = Container.expand(function () {
var self = Container.call(this);
self.attackDamage = 35; // Higher damage than regular peashooter
self.attackCooldown = 0;
self.attackRate = 50; // Slightly slower attack rate
self.range = 350; // Significantly longer range
self.hp = 100; // Much higher health
self.maxHp = 100;
var podGraphics = self.attachAsset('peashooterPod', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.3,
// Make it slightly larger to show it's heavy
scaleY: 1.3,
tint: 0x444444 // Darker tint to distinguish from regular
});
self.takeDamage = function (damage) {
self.hp -= damage;
LK.effects.flashObject(self, 0xff0000, 300);
if (self.hp <= 0) {
self.hp = 0;
towersDestroyed++;
return true; // tower died
}
return false;
};
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
// Find enemy in range
if (self.attackCooldown <= 0) {
var targetEnemy = null;
var closestDistance = self.range;
for (var i = 0; i < shadowCreatures.length; i++) {
var enemy = shadowCreatures[i];
var distance = Math.sqrt(Math.pow(self.x - enemy.x, 2) + Math.pow(self.y - enemy.y, 2));
if (distance <= closestDistance) {
closestDistance = distance;
targetEnemy = enemy;
}
}
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var distance = Math.sqrt(Math.pow(self.x - zombie.x, 2) + Math.pow(self.y - zombie.y, 2));
if (distance <= closestDistance) {
closestDistance = distance;
targetEnemy = zombie;
}
}
if (targetEnemy) {
// Shoot projectile
var projectile = new Projectile();
projectile.x = self.x;
projectile.y = self.y;
projectile.targetX = targetEnemy.x;
projectile.targetY = targetEnemy.y;
projectile.damage = self.attackDamage;
game.addChild(projectile);
projectiles.push(projectile);
self.attackCooldown = self.attackRate;
LK.getSound('shoot').play();
}
}
};
return self;
});
var Hero = Container.expand(function () {
var self = Container.call(this);
self.hp = 100;
self.maxHp = 100;
self.speed = 4;
self.attackDamage = 25;
self.attackCooldown = 0;
self.attackRate = 30; // frames between attacks
self.targetX = 0;
self.targetY = 0;
self.isMoving = false;
var heroGraphics = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
self.takeDamage = function (damage) {
self.hp -= damage;
if (self.hp <= 0) {
self.hp = 0;
LK.showGameOver();
}
LK.effects.flashObject(self, 0xff0000, 300);
};
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
// Handle movement to target position
if (self.isMoving) {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
// Move towards target
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
// Reached target
self.isMoving = false;
}
}
// Gun attack - shoot projectiles at enemies
if (self.attackCooldown <= 0) {
var closestEnemy = null;
var closestDistance = 400; // Gun range
for (var i = 0; i < shadowCreatures.length; i++) {
var enemy = shadowCreatures[i];
var distance = Math.sqrt(Math.pow(self.x - enemy.x, 2) + Math.pow(self.y - enemy.y, 2));
if (distance < closestDistance) {
closestDistance = distance;
closestEnemy = enemy;
}
}
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var distance = Math.sqrt(Math.pow(self.x - zombie.x, 2) + Math.pow(self.y - zombie.y, 2));
if (distance < closestDistance) {
closestDistance = distance;
closestEnemy = zombie;
}
}
if (closestEnemy) {
// Shoot projectile at enemy
var projectile = new Projectile();
projectile.x = self.x;
projectile.y = self.y;
projectile.targetX = closestEnemy.x;
projectile.targetY = closestEnemy.y;
projectile.damage = self.attackDamage;
game.addChild(projectile);
projectiles.push(projectile);
self.attackCooldown = self.attackRate;
LK.getSound('shoot').play();
}
}
};
return self;
});
var ManaSprout = Container.expand(function () {
var self = Container.call(this);
self.hp = 50;
self.maxHp = 50;
var sproutGraphics = self.attachAsset('manaSprout', {
anchorX: 0.5,
anchorY: 0.5
});
self.takeDamage = function (damage) {
self.hp -= damage;
if (self.hp <= 0) {
self.hp = 0;
manaSproutsDestroyed++;
return true; // mana sprout died
}
LK.effects.flashObject(self, 0xff0000, 500);
return false;
};
return self;
});
var PeashooterPod = Container.expand(function () {
var self = Container.call(this);
self.attackDamage = 20;
self.attackCooldown = 0;
self.attackRate = 45;
self.range = 300;
self.hp = 60;
self.maxHp = 60;
var podGraphics = self.attachAsset('peashooterPod', {
anchorX: 0.5,
anchorY: 0.5
});
self.takeDamage = function (damage) {
self.hp -= damage;
LK.effects.flashObject(self, 0xff0000, 300);
if (self.hp <= 0) {
self.hp = 0;
towersDestroyed++;
return true; // tower died
}
return false;
};
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
// Find enemy in range
if (self.attackCooldown <= 0) {
var targetEnemy = null;
var closestDistance = self.range;
for (var i = 0; i < shadowCreatures.length; i++) {
var enemy = shadowCreatures[i];
var distance = Math.sqrt(Math.pow(self.x - enemy.x, 2) + Math.pow(self.y - enemy.y, 2));
if (distance <= closestDistance) {
closestDistance = distance;
targetEnemy = enemy;
}
}
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var distance = Math.sqrt(Math.pow(self.x - zombie.x, 2) + Math.pow(self.y - zombie.y, 2));
if (distance <= closestDistance) {
closestDistance = distance;
targetEnemy = zombie;
}
}
if (targetEnemy) {
// Shoot projectile
var projectile = new Projectile();
projectile.x = self.x;
projectile.y = self.y;
projectile.targetX = targetEnemy.x;
projectile.targetY = targetEnemy.y;
projectile.damage = self.attackDamage;
game.addChild(projectile);
projectiles.push(projectile);
self.attackCooldown = self.attackRate;
LK.getSound('shoot').play();
}
}
};
return self;
});
var Projectile = Container.expand(function () {
var self = Container.call(this);
self.speed = 8;
self.damage = 20;
self.targetX = 0;
self.targetY = 0;
self.directionX = 0;
self.directionY = 0;
var projectileGraphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
// Calculate direction on creation
self.setTarget = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.directionX = dx / distance;
self.directionY = dy / distance;
}
};
self.update = function () {
// Move towards target
if (self.directionX === 0 && self.directionY === 0) {
self.setTarget(self.targetX, self.targetY);
}
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
// Check collision with shadow creatures
for (var i = 0; i < shadowCreatures.length; i++) {
var enemy = shadowCreatures[i];
if (self.intersects(enemy)) {
if (enemy.takeDamage(self.damage)) {
// Enemy died, remove it
enemy.destroy();
shadowCreatures.splice(i, 1);
}
return true; // Projectile should be destroyed
}
}
// Check collision with zombies
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
if (self.intersects(zombie)) {
if (zombie.takeDamage(self.damage)) {
// Zombie died, remove it
zombie.destroy();
zombies.splice(i, 1);
}
return true; // Projectile should be destroyed
}
}
// Remove if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
return true; // Projectile should be destroyed
}
return false;
};
return self;
});
var ShadowCreature = Container.expand(function () {
var self = Container.call(this);
// Scale stats based on wave number for progressive difficulty
var waveScaling = Math.min(waveNumber, 10); // Cap scaling at wave 10
self.hp = 30 + waveScaling * 8;
self.maxHp = self.hp;
self.speed = 1 + waveScaling * 0.15;
self.attackDamage = 10 + waveScaling * 3;
self.attackCooldown = 0;
self.attackRate = Math.max(30, 60 - waveScaling * 3);
self.target = null;
self.lastX = 0;
self.lastY = 0;
var creatureGraphics = self.attachAsset('shadowCreature', {
anchorX: 0.5,
anchorY: 0.5
});
self.takeDamage = function (damage) {
self.hp -= damage;
LK.effects.flashObject(self, 0xffffff, 200);
if (self.hp <= 0) {
var essence = new SunlightEssence();
essence.x = self.x;
essence.y = self.y;
game.addChild(essence);
sunlightEssences.push(essence);
LK.getSound('enemyHit').play();
return true; // enemy died
}
return false;
};
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
// Find closest target
var closestDistance = Infinity;
self.target = null;
// Check mana sprouts
for (var i = 0; i < manaSprouts.length; i++) {
var sprout = manaSprouts[i];
var distance = Math.sqrt(Math.pow(self.x - sprout.x, 2) + Math.pow(self.y - sprout.y, 2));
if (distance < closestDistance) {
closestDistance = distance;
self.target = sprout;
}
}
// Check peashooter pods
for (var i = 0; i < peashooterPods.length; i++) {
var pod = peashooterPods[i];
var distance = Math.sqrt(Math.pow(self.x - pod.x, 2) + Math.pow(self.y - pod.y, 2));
if (distance < closestDistance) {
closestDistance = distance;
self.target = pod;
}
}
// Check heavy peashooter pods
for (var i = 0; i < heavyPeashooterPods.length; i++) {
var heavyPod = heavyPeashooterPods[i];
var distance = Math.sqrt(Math.pow(self.x - heavyPod.x, 2) + Math.pow(self.y - heavyPod.y, 2));
if (distance < closestDistance) {
closestDistance = distance;
self.target = heavyPod;
}
}
// Check hero
if (hero) {
var heroDistance = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
if (heroDistance < closestDistance) {
closestDistance = heroDistance;
self.target = hero;
}
}
// Move towards target
if (self.target) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 40) {
// Move towards target
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
// Attack target
if (self.attackCooldown <= 0) {
self.target.takeDamage(self.attackDamage);
self.attackCooldown = self.attackRate;
}
}
}
};
return self;
});
var SunlightEssence = Container.expand(function () {
var self = Container.call(this);
self.value = 10;
var essenceGraphics = self.attachAsset('sunlightEssence', {
anchorX: 0.5,
anchorY: 0.5
});
// Add a gentle floating animation
tween(self, {
y: self.y - 10
}, {
duration: 1000,
easing: tween.easeInOut
});
return self;
});
var Zombie = Container.expand(function () {
var self = Container.call(this);
// Scale stats based on wave number for progressive difficulty
var waveScaling = Math.min(waveNumber, 10); // Cap scaling at wave 10
self.hp = 120 + waveScaling * 15;
self.maxHp = self.hp;
self.speed = 0.5 + waveScaling * 0.1; // Slower than shadow creatures
self.attackDamage = 25 + waveScaling * 5;
self.attackCooldown = 0;
self.attackRate = Math.max(35, 75 - waveScaling * 4); // Faster attack rate than before
self.target = null;
self.lastX = 0;
self.lastY = 0;
var zombieGraphics = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.takeDamage = function (damage) {
self.hp -= damage;
LK.effects.flashObject(self, 0xffffff, 200);
if (self.hp <= 0) {
var essence = new SunlightEssence();
essence.x = self.x;
essence.y = self.y;
game.addChild(essence);
sunlightEssences.push(essence);
LK.getSound('enemyHit').play();
return true; // enemy died
}
return false;
};
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
// Find closest target
var closestDistance = Infinity;
self.target = null;
// Check mana sprouts
for (var i = 0; i < manaSprouts.length; i++) {
var sprout = manaSprouts[i];
var distance = Math.sqrt(Math.pow(self.x - sprout.x, 2) + Math.pow(self.y - sprout.y, 2));
if (distance < closestDistance) {
closestDistance = distance;
self.target = sprout;
}
}
// Check peashooter pods
for (var i = 0; i < peashooterPods.length; i++) {
var pod = peashooterPods[i];
var distance = Math.sqrt(Math.pow(self.x - pod.x, 2) + Math.pow(self.y - pod.y, 2));
if (distance < closestDistance) {
closestDistance = distance;
self.target = pod;
}
}
// Check heavy peashooter pods
for (var i = 0; i < heavyPeashooterPods.length; i++) {
var heavyPod = heavyPeashooterPods[i];
var distance = Math.sqrt(Math.pow(self.x - heavyPod.x, 2) + Math.pow(self.y - heavyPod.y, 2));
if (distance < closestDistance) {
closestDistance = distance;
self.target = heavyPod;
}
}
// Check hero
if (hero) {
var heroDistance = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
if (heroDistance < closestDistance) {
closestDistance = heroDistance;
self.target = hero;
}
}
// Move towards target
if (self.target) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 40) {
// Move towards target
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
// Attack target
if (self.attackCooldown <= 0) {
self.target.takeDamage(self.attackDamage);
self.attackCooldown = self.attackRate;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2d5016
});
/****
* Game Code
****/
// Game state variables
var hero;
var manaSprouts = [];
var shadowCreatures = [];
var zombies = [];
var peashooterPods = [];
var heavyPeashooterPods = [];
var projectiles = [];
var sunlightEssences = [];
var currentEssences = 50; // Starting essences
var waveNumber = 1;
var enemiesRemaining = 0;
var waveActive = false;
var timeBetweenWaves = 300; // frames
var waveTimer = 0;
var selectedPlantType = 'peashooter';
var plantCost = 25;
var heavyPlantCost = 50;
var towersDestroyed = 0;
var manaSproutsDestroyed = 0;
// UI Elements
var essenceText = new Text2('Essences: 50', {
size: 60,
fill: 0xFFFFFF
});
essenceText.anchor.set(0, 0);
essenceText.x = 120;
essenceText.y = 50;
LK.gui.topLeft.addChild(essenceText);
var waveText = new Text2('Wave: 1', {
size: 60,
fill: 0xFFFFFF
});
waveText.anchor.set(1, 0);
waveText.x = -50;
waveText.y = 50;
LK.gui.topRight.addChild(waveText);
var statusText = new Text2('Prepare for battle!', {
size: 80,
fill: 0xFFFF00
});
statusText.anchor.set(0.5, 0);
statusText.x = 0;
statusText.y = 120;
LK.gui.top.addChild(statusText);
var enemyBarText = new Text2('Enemies: 0', {
size: 60,
fill: 0xFF4444
});
enemyBarText.anchor.set(0.5, 0);
enemyBarText.x = 0;
enemyBarText.y = 200;
LK.gui.top.addChild(enemyBarText);
var buyButton = new Text2('Buy Tower (25)', {
size: 60,
fill: 0x00FF00
});
buyButton.anchor.set(0, 1);
buyButton.x = 120;
buyButton.y = -50;
LK.gui.bottomLeft.addChild(buyButton);
var heavyBuyButton = new Text2('Buy Heavy (50)', {
size: 60,
fill: 0x00AA00
});
heavyBuyButton.anchor.set(0, 1);
heavyBuyButton.x = 120;
heavyBuyButton.y = -130;
LK.gui.bottomLeft.addChild(heavyBuyButton);
// Add background image
var backgroundImage = game.addChild(LK.getAsset('gardenBackground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
}));
// Initialize hero
hero = game.addChild(new Hero());
hero.x = 1024;
hero.y = 1366;
// Start background music with looping
LK.playMusic('gardenTheme', {
loop: true
});
// Initialize mana sprouts
var sprout1 = game.addChild(new ManaSprout());
sprout1.x = 512;
sprout1.y = 400;
manaSprouts.push(sprout1);
var sprout2 = game.addChild(new ManaSprout());
sprout2.x = 1536;
sprout2.y = 400;
manaSprouts.push(sprout2);
var sprout3 = game.addChild(new ManaSprout());
sprout3.x = 1024;
sprout3.y = 600;
manaSprouts.push(sprout3);
// Game state functions
function startWave() {
waveActive = true;
// Progressive difficulty: more enemies each wave with exponential scaling
enemiesRemaining = Math.floor(waveNumber * 4 + Math.pow(waveNumber, 1.5) * 2);
statusText.setText('Wave ' + waveNumber + ' - Enemies: ' + enemiesRemaining);
enemyBarText.setText('Enemies: ' + (enemiesRemaining + shadowCreatures.length));
// Spawn enemies gradually
var enemySpawnTimer = LK.setInterval(function () {
if (enemiesRemaining > 0) {
spawnEnemy();
enemiesRemaining--;
statusText.setText('Wave ' + waveNumber + ' - Enemies: ' + enemiesRemaining);
enemyBarText.setText('Enemies: ' + (enemiesRemaining + shadowCreatures.length));
}
}, 60);
LK.setTimeout(function () {
LK.clearInterval(enemySpawnTimer);
}, enemiesRemaining * 60);
}
function spawnEnemy() {
var enemy;
// Increase zombie spawn rate in later waves: 30% + 5% per wave (capped at 70%)
var zombieChance = Math.min(0.7, 0.3 + waveNumber * 0.05);
if (Math.random() < zombieChance) {
enemy = new Zombie();
} else {
enemy = new ShadowCreature();
}
// Spawn from random edge
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// Top
enemy.x = Math.random() * 2048;
enemy.y = -50;
break;
case 1:
// Right
enemy.x = 2098;
enemy.y = Math.random() * 2732;
break;
case 2:
// Bottom
enemy.x = Math.random() * 2048;
enemy.y = 2782;
break;
case 3:
// Left
enemy.x = -50;
enemy.y = Math.random() * 2732;
break;
}
game.addChild(enemy);
if (enemy instanceof Zombie) {
zombies.push(enemy);
} else {
shadowCreatures.push(enemy);
}
}
function endWave() {
waveActive = false;
waveNumber++;
// Reduce time between waves as difficulty increases
waveTimer = Math.max(120, timeBetweenWaves - waveNumber * 30);
waveText.setText('Wave: ' + waveNumber);
statusText.setText('Wave Complete! Next wave in ' + Math.ceil(waveTimer / 60) + 's');
// Bonus essences for completing wave
currentEssences += 20;
updateEssenceDisplay();
}
function updateEssenceDisplay() {
essenceText.setText('Essences: ' + currentEssences);
}
function collectEssence(essence) {
currentEssences += essence.value;
updateEssenceDisplay();
LK.getSound('collect').play();
}
// Input handling
var dragNode = null;
var plantingMode = false;
// Buy button click handler
buyButton.down = function (x, y, obj) {
if (currentEssences >= plantCost && !waveActive) {
selectedPlantType = 'peashooter';
plantingMode = true;
buyButton.setText('Click to Plant!');
buyButton.fill = 0xFFFF00;
heavyBuyButton.setText('Buy Heavy (50)');
heavyBuyButton.fill = 0x00AA00;
}
};
// Heavy buy button click handler
heavyBuyButton.down = function (x, y, obj) {
if (currentEssences >= heavyPlantCost && !waveActive) {
selectedPlantType = 'heavy';
plantingMode = true;
heavyBuyButton.setText('Click to Plant!');
heavyBuyButton.fill = 0xFFFF00;
buyButton.setText('Buy Tower (25)');
buyButton.fill = 0x00FF00;
}
};
function handleMove(x, y, obj) {
if (dragNode) {
dragNode.x = x;
dragNode.y = y;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Set hero target position for movement
hero.targetX = x;
hero.targetY = y;
hero.isMoving = true;
// Check if in planting mode
if (plantingMode && !waveActive) {
var currentCost = selectedPlantType === 'heavy' ? heavyPlantCost : plantCost;
if (currentEssences >= currentCost) {
// Check if position is valid (not too close to existing plants or sprouts)
var validPosition = true;
for (var i = 0; i < peashooterPods.length; i++) {
var pod = peashooterPods[i];
var distance = Math.sqrt(Math.pow(x - pod.x, 2) + Math.pow(y - pod.y, 2));
if (distance < 80) {
validPosition = false;
break;
}
}
for (var i = 0; i < heavyPeashooterPods.length; i++) {
var heavyPod = heavyPeashooterPods[i];
var distance = Math.sqrt(Math.pow(x - heavyPod.x, 2) + Math.pow(y - heavyPod.y, 2));
if (distance < 80) {
validPosition = false;
break;
}
}
for (var i = 0; i < manaSprouts.length; i++) {
var sprout = manaSprouts[i];
var distance = Math.sqrt(Math.pow(x - sprout.x, 2) + Math.pow(y - sprout.y, 2));
if (distance < 80) {
validPosition = false;
break;
}
}
if (validPosition) {
if (selectedPlantType === 'heavy') {
var newHeavyPod = new HeavyPeashooterPod();
newHeavyPod.x = x;
newHeavyPod.y = y;
game.addChild(newHeavyPod);
heavyPeashooterPods.push(newHeavyPod);
currentEssences -= heavyPlantCost;
} else {
var newPod = new PeashooterPod();
newPod.x = x;
newPod.y = y;
game.addChild(newPod);
peashooterPods.push(newPod);
currentEssences -= plantCost;
}
updateEssenceDisplay();
LK.getSound('plant').play();
plantingMode = false;
buyButton.setText('Buy Tower (25)');
buyButton.fill = 0x00FF00;
heavyBuyButton.setText('Buy Heavy (50)');
heavyBuyButton.fill = 0x00AA00;
}
}
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Start first wave after delay
waveTimer = 180; // 3 seconds
game.update = function () {
// Handle wave timing
if (!waveActive) {
if (waveTimer > 0) {
waveTimer--;
if (waveTimer % 60 === 0) {
statusText.setText('Next wave in ' + Math.ceil(waveTimer / 60) + 's');
}
} else {
startWave();
}
} else {
// Check if wave is complete
if (enemiesRemaining <= 0 && shadowCreatures.length === 0 && zombies.length === 0) {
endWave();
}
}
// Update enemy bar display
var totalEnemies = enemiesRemaining + shadowCreatures.length + zombies.length;
enemyBarText.setText('Enemies: ' + totalEnemies);
// Update buy button availability
if (!plantingMode) {
if (waveActive) {
buyButton.setText('Buy Tower (Wave Active)');
buyButton.fill = 0xFF0000;
heavyBuyButton.setText('Buy Heavy (Wave Active)');
heavyBuyButton.fill = 0xFF0000;
} else if (currentEssences < plantCost) {
buyButton.setText('Buy Tower (Need ' + plantCost + ')');
buyButton.fill = 0xFF0000;
if (currentEssences < heavyPlantCost) {
heavyBuyButton.setText('Buy Heavy (Need ' + heavyPlantCost + ')');
heavyBuyButton.fill = 0xFF0000;
} else {
heavyBuyButton.setText('Buy Heavy (50)');
heavyBuyButton.fill = 0x00AA00;
}
} else {
buyButton.setText('Buy Tower (25)');
buyButton.fill = 0x00FF00;
if (currentEssences < heavyPlantCost) {
heavyBuyButton.setText('Buy Heavy (Need ' + heavyPlantCost + ')');
heavyBuyButton.fill = 0xFF0000;
} else {
heavyBuyButton.setText('Buy Heavy (50)');
heavyBuyButton.fill = 0x00AA00;
}
}
}
// Update projectiles
for (var i = projectiles.length - 1; i >= 0; i--) {
var projectile = projectiles[i];
var shouldDestroy = projectile.update();
if (shouldDestroy) {
projectile.destroy();
projectiles.splice(i, 1);
}
}
// Check for destroyed towers
for (var i = peashooterPods.length - 1; i >= 0; i--) {
var pod = peashooterPods[i];
if (pod.hp <= 0) {
pod.destroy();
peashooterPods.splice(i, 1);
}
}
// Check for destroyed heavy towers
for (var i = heavyPeashooterPods.length - 1; i >= 0; i--) {
var heavyPod = heavyPeashooterPods[i];
if (heavyPod.hp <= 0) {
heavyPod.destroy();
heavyPeashooterPods.splice(i, 1);
}
}
// Check for destroyed mana sprouts
for (var i = manaSprouts.length - 1; i >= 0; i--) {
var sprout = manaSprouts[i];
if (sprout.hp <= 0) {
sprout.destroy();
manaSprouts.splice(i, 1);
}
}
// Check game over condition - 3 towers destroyed
if (towersDestroyed >= 3) {
LK.showGameOver();
}
// Check defeat condition - all mana sprouts destroyed
if (manaSproutsDestroyed >= 3) {
LK.showGameOver();
}
// Check essence collection
for (var i = sunlightEssences.length - 1; i >= 0; i--) {
var essence = sunlightEssences[i];
var distance = Math.sqrt(Math.pow(hero.x - essence.x, 2) + Math.pow(hero.y - essence.y, 2));
if (distance < 40) {
collectEssence(essence);
essence.destroy();
sunlightEssences.splice(i, 1);
}
}
// Hero attack now handled in Hero class update method
// Check win condition
if (waveNumber > 5 && !waveActive && shadowCreatures.length === 0 && zombies.length === 0) {
LK.showYouWin();
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var HeavyPeashooterPod = Container.expand(function () {
var self = Container.call(this);
self.attackDamage = 35; // Higher damage than regular peashooter
self.attackCooldown = 0;
self.attackRate = 50; // Slightly slower attack rate
self.range = 350; // Significantly longer range
self.hp = 100; // Much higher health
self.maxHp = 100;
var podGraphics = self.attachAsset('peashooterPod', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.3,
// Make it slightly larger to show it's heavy
scaleY: 1.3,
tint: 0x444444 // Darker tint to distinguish from regular
});
self.takeDamage = function (damage) {
self.hp -= damage;
LK.effects.flashObject(self, 0xff0000, 300);
if (self.hp <= 0) {
self.hp = 0;
towersDestroyed++;
return true; // tower died
}
return false;
};
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
// Find enemy in range
if (self.attackCooldown <= 0) {
var targetEnemy = null;
var closestDistance = self.range;
for (var i = 0; i < shadowCreatures.length; i++) {
var enemy = shadowCreatures[i];
var distance = Math.sqrt(Math.pow(self.x - enemy.x, 2) + Math.pow(self.y - enemy.y, 2));
if (distance <= closestDistance) {
closestDistance = distance;
targetEnemy = enemy;
}
}
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var distance = Math.sqrt(Math.pow(self.x - zombie.x, 2) + Math.pow(self.y - zombie.y, 2));
if (distance <= closestDistance) {
closestDistance = distance;
targetEnemy = zombie;
}
}
if (targetEnemy) {
// Shoot projectile
var projectile = new Projectile();
projectile.x = self.x;
projectile.y = self.y;
projectile.targetX = targetEnemy.x;
projectile.targetY = targetEnemy.y;
projectile.damage = self.attackDamage;
game.addChild(projectile);
projectiles.push(projectile);
self.attackCooldown = self.attackRate;
LK.getSound('shoot').play();
}
}
};
return self;
});
var Hero = Container.expand(function () {
var self = Container.call(this);
self.hp = 100;
self.maxHp = 100;
self.speed = 4;
self.attackDamage = 25;
self.attackCooldown = 0;
self.attackRate = 30; // frames between attacks
self.targetX = 0;
self.targetY = 0;
self.isMoving = false;
var heroGraphics = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
self.takeDamage = function (damage) {
self.hp -= damage;
if (self.hp <= 0) {
self.hp = 0;
LK.showGameOver();
}
LK.effects.flashObject(self, 0xff0000, 300);
};
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
// Handle movement to target position
if (self.isMoving) {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
// Move towards target
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
// Reached target
self.isMoving = false;
}
}
// Gun attack - shoot projectiles at enemies
if (self.attackCooldown <= 0) {
var closestEnemy = null;
var closestDistance = 400; // Gun range
for (var i = 0; i < shadowCreatures.length; i++) {
var enemy = shadowCreatures[i];
var distance = Math.sqrt(Math.pow(self.x - enemy.x, 2) + Math.pow(self.y - enemy.y, 2));
if (distance < closestDistance) {
closestDistance = distance;
closestEnemy = enemy;
}
}
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var distance = Math.sqrt(Math.pow(self.x - zombie.x, 2) + Math.pow(self.y - zombie.y, 2));
if (distance < closestDistance) {
closestDistance = distance;
closestEnemy = zombie;
}
}
if (closestEnemy) {
// Shoot projectile at enemy
var projectile = new Projectile();
projectile.x = self.x;
projectile.y = self.y;
projectile.targetX = closestEnemy.x;
projectile.targetY = closestEnemy.y;
projectile.damage = self.attackDamage;
game.addChild(projectile);
projectiles.push(projectile);
self.attackCooldown = self.attackRate;
LK.getSound('shoot').play();
}
}
};
return self;
});
var ManaSprout = Container.expand(function () {
var self = Container.call(this);
self.hp = 50;
self.maxHp = 50;
var sproutGraphics = self.attachAsset('manaSprout', {
anchorX: 0.5,
anchorY: 0.5
});
self.takeDamage = function (damage) {
self.hp -= damage;
if (self.hp <= 0) {
self.hp = 0;
manaSproutsDestroyed++;
return true; // mana sprout died
}
LK.effects.flashObject(self, 0xff0000, 500);
return false;
};
return self;
});
var PeashooterPod = Container.expand(function () {
var self = Container.call(this);
self.attackDamage = 20;
self.attackCooldown = 0;
self.attackRate = 45;
self.range = 300;
self.hp = 60;
self.maxHp = 60;
var podGraphics = self.attachAsset('peashooterPod', {
anchorX: 0.5,
anchorY: 0.5
});
self.takeDamage = function (damage) {
self.hp -= damage;
LK.effects.flashObject(self, 0xff0000, 300);
if (self.hp <= 0) {
self.hp = 0;
towersDestroyed++;
return true; // tower died
}
return false;
};
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
// Find enemy in range
if (self.attackCooldown <= 0) {
var targetEnemy = null;
var closestDistance = self.range;
for (var i = 0; i < shadowCreatures.length; i++) {
var enemy = shadowCreatures[i];
var distance = Math.sqrt(Math.pow(self.x - enemy.x, 2) + Math.pow(self.y - enemy.y, 2));
if (distance <= closestDistance) {
closestDistance = distance;
targetEnemy = enemy;
}
}
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var distance = Math.sqrt(Math.pow(self.x - zombie.x, 2) + Math.pow(self.y - zombie.y, 2));
if (distance <= closestDistance) {
closestDistance = distance;
targetEnemy = zombie;
}
}
if (targetEnemy) {
// Shoot projectile
var projectile = new Projectile();
projectile.x = self.x;
projectile.y = self.y;
projectile.targetX = targetEnemy.x;
projectile.targetY = targetEnemy.y;
projectile.damage = self.attackDamage;
game.addChild(projectile);
projectiles.push(projectile);
self.attackCooldown = self.attackRate;
LK.getSound('shoot').play();
}
}
};
return self;
});
var Projectile = Container.expand(function () {
var self = Container.call(this);
self.speed = 8;
self.damage = 20;
self.targetX = 0;
self.targetY = 0;
self.directionX = 0;
self.directionY = 0;
var projectileGraphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
// Calculate direction on creation
self.setTarget = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.directionX = dx / distance;
self.directionY = dy / distance;
}
};
self.update = function () {
// Move towards target
if (self.directionX === 0 && self.directionY === 0) {
self.setTarget(self.targetX, self.targetY);
}
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
// Check collision with shadow creatures
for (var i = 0; i < shadowCreatures.length; i++) {
var enemy = shadowCreatures[i];
if (self.intersects(enemy)) {
if (enemy.takeDamage(self.damage)) {
// Enemy died, remove it
enemy.destroy();
shadowCreatures.splice(i, 1);
}
return true; // Projectile should be destroyed
}
}
// Check collision with zombies
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
if (self.intersects(zombie)) {
if (zombie.takeDamage(self.damage)) {
// Zombie died, remove it
zombie.destroy();
zombies.splice(i, 1);
}
return true; // Projectile should be destroyed
}
}
// Remove if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
return true; // Projectile should be destroyed
}
return false;
};
return self;
});
var ShadowCreature = Container.expand(function () {
var self = Container.call(this);
// Scale stats based on wave number for progressive difficulty
var waveScaling = Math.min(waveNumber, 10); // Cap scaling at wave 10
self.hp = 30 + waveScaling * 8;
self.maxHp = self.hp;
self.speed = 1 + waveScaling * 0.15;
self.attackDamage = 10 + waveScaling * 3;
self.attackCooldown = 0;
self.attackRate = Math.max(30, 60 - waveScaling * 3);
self.target = null;
self.lastX = 0;
self.lastY = 0;
var creatureGraphics = self.attachAsset('shadowCreature', {
anchorX: 0.5,
anchorY: 0.5
});
self.takeDamage = function (damage) {
self.hp -= damage;
LK.effects.flashObject(self, 0xffffff, 200);
if (self.hp <= 0) {
var essence = new SunlightEssence();
essence.x = self.x;
essence.y = self.y;
game.addChild(essence);
sunlightEssences.push(essence);
LK.getSound('enemyHit').play();
return true; // enemy died
}
return false;
};
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
// Find closest target
var closestDistance = Infinity;
self.target = null;
// Check mana sprouts
for (var i = 0; i < manaSprouts.length; i++) {
var sprout = manaSprouts[i];
var distance = Math.sqrt(Math.pow(self.x - sprout.x, 2) + Math.pow(self.y - sprout.y, 2));
if (distance < closestDistance) {
closestDistance = distance;
self.target = sprout;
}
}
// Check peashooter pods
for (var i = 0; i < peashooterPods.length; i++) {
var pod = peashooterPods[i];
var distance = Math.sqrt(Math.pow(self.x - pod.x, 2) + Math.pow(self.y - pod.y, 2));
if (distance < closestDistance) {
closestDistance = distance;
self.target = pod;
}
}
// Check heavy peashooter pods
for (var i = 0; i < heavyPeashooterPods.length; i++) {
var heavyPod = heavyPeashooterPods[i];
var distance = Math.sqrt(Math.pow(self.x - heavyPod.x, 2) + Math.pow(self.y - heavyPod.y, 2));
if (distance < closestDistance) {
closestDistance = distance;
self.target = heavyPod;
}
}
// Check hero
if (hero) {
var heroDistance = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
if (heroDistance < closestDistance) {
closestDistance = heroDistance;
self.target = hero;
}
}
// Move towards target
if (self.target) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 40) {
// Move towards target
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
// Attack target
if (self.attackCooldown <= 0) {
self.target.takeDamage(self.attackDamage);
self.attackCooldown = self.attackRate;
}
}
}
};
return self;
});
var SunlightEssence = Container.expand(function () {
var self = Container.call(this);
self.value = 10;
var essenceGraphics = self.attachAsset('sunlightEssence', {
anchorX: 0.5,
anchorY: 0.5
});
// Add a gentle floating animation
tween(self, {
y: self.y - 10
}, {
duration: 1000,
easing: tween.easeInOut
});
return self;
});
var Zombie = Container.expand(function () {
var self = Container.call(this);
// Scale stats based on wave number for progressive difficulty
var waveScaling = Math.min(waveNumber, 10); // Cap scaling at wave 10
self.hp = 120 + waveScaling * 15;
self.maxHp = self.hp;
self.speed = 0.5 + waveScaling * 0.1; // Slower than shadow creatures
self.attackDamage = 25 + waveScaling * 5;
self.attackCooldown = 0;
self.attackRate = Math.max(35, 75 - waveScaling * 4); // Faster attack rate than before
self.target = null;
self.lastX = 0;
self.lastY = 0;
var zombieGraphics = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.takeDamage = function (damage) {
self.hp -= damage;
LK.effects.flashObject(self, 0xffffff, 200);
if (self.hp <= 0) {
var essence = new SunlightEssence();
essence.x = self.x;
essence.y = self.y;
game.addChild(essence);
sunlightEssences.push(essence);
LK.getSound('enemyHit').play();
return true; // enemy died
}
return false;
};
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
// Find closest target
var closestDistance = Infinity;
self.target = null;
// Check mana sprouts
for (var i = 0; i < manaSprouts.length; i++) {
var sprout = manaSprouts[i];
var distance = Math.sqrt(Math.pow(self.x - sprout.x, 2) + Math.pow(self.y - sprout.y, 2));
if (distance < closestDistance) {
closestDistance = distance;
self.target = sprout;
}
}
// Check peashooter pods
for (var i = 0; i < peashooterPods.length; i++) {
var pod = peashooterPods[i];
var distance = Math.sqrt(Math.pow(self.x - pod.x, 2) + Math.pow(self.y - pod.y, 2));
if (distance < closestDistance) {
closestDistance = distance;
self.target = pod;
}
}
// Check heavy peashooter pods
for (var i = 0; i < heavyPeashooterPods.length; i++) {
var heavyPod = heavyPeashooterPods[i];
var distance = Math.sqrt(Math.pow(self.x - heavyPod.x, 2) + Math.pow(self.y - heavyPod.y, 2));
if (distance < closestDistance) {
closestDistance = distance;
self.target = heavyPod;
}
}
// Check hero
if (hero) {
var heroDistance = Math.sqrt(Math.pow(self.x - hero.x, 2) + Math.pow(self.y - hero.y, 2));
if (heroDistance < closestDistance) {
closestDistance = heroDistance;
self.target = hero;
}
}
// Move towards target
if (self.target) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 40) {
// Move towards target
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
// Attack target
if (self.attackCooldown <= 0) {
self.target.takeDamage(self.attackDamage);
self.attackCooldown = self.attackRate;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2d5016
});
/****
* Game Code
****/
// Game state variables
var hero;
var manaSprouts = [];
var shadowCreatures = [];
var zombies = [];
var peashooterPods = [];
var heavyPeashooterPods = [];
var projectiles = [];
var sunlightEssences = [];
var currentEssences = 50; // Starting essences
var waveNumber = 1;
var enemiesRemaining = 0;
var waveActive = false;
var timeBetweenWaves = 300; // frames
var waveTimer = 0;
var selectedPlantType = 'peashooter';
var plantCost = 25;
var heavyPlantCost = 50;
var towersDestroyed = 0;
var manaSproutsDestroyed = 0;
// UI Elements
var essenceText = new Text2('Essences: 50', {
size: 60,
fill: 0xFFFFFF
});
essenceText.anchor.set(0, 0);
essenceText.x = 120;
essenceText.y = 50;
LK.gui.topLeft.addChild(essenceText);
var waveText = new Text2('Wave: 1', {
size: 60,
fill: 0xFFFFFF
});
waveText.anchor.set(1, 0);
waveText.x = -50;
waveText.y = 50;
LK.gui.topRight.addChild(waveText);
var statusText = new Text2('Prepare for battle!', {
size: 80,
fill: 0xFFFF00
});
statusText.anchor.set(0.5, 0);
statusText.x = 0;
statusText.y = 120;
LK.gui.top.addChild(statusText);
var enemyBarText = new Text2('Enemies: 0', {
size: 60,
fill: 0xFF4444
});
enemyBarText.anchor.set(0.5, 0);
enemyBarText.x = 0;
enemyBarText.y = 200;
LK.gui.top.addChild(enemyBarText);
var buyButton = new Text2('Buy Tower (25)', {
size: 60,
fill: 0x00FF00
});
buyButton.anchor.set(0, 1);
buyButton.x = 120;
buyButton.y = -50;
LK.gui.bottomLeft.addChild(buyButton);
var heavyBuyButton = new Text2('Buy Heavy (50)', {
size: 60,
fill: 0x00AA00
});
heavyBuyButton.anchor.set(0, 1);
heavyBuyButton.x = 120;
heavyBuyButton.y = -130;
LK.gui.bottomLeft.addChild(heavyBuyButton);
// Add background image
var backgroundImage = game.addChild(LK.getAsset('gardenBackground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
}));
// Initialize hero
hero = game.addChild(new Hero());
hero.x = 1024;
hero.y = 1366;
// Start background music with looping
LK.playMusic('gardenTheme', {
loop: true
});
// Initialize mana sprouts
var sprout1 = game.addChild(new ManaSprout());
sprout1.x = 512;
sprout1.y = 400;
manaSprouts.push(sprout1);
var sprout2 = game.addChild(new ManaSprout());
sprout2.x = 1536;
sprout2.y = 400;
manaSprouts.push(sprout2);
var sprout3 = game.addChild(new ManaSprout());
sprout3.x = 1024;
sprout3.y = 600;
manaSprouts.push(sprout3);
// Game state functions
function startWave() {
waveActive = true;
// Progressive difficulty: more enemies each wave with exponential scaling
enemiesRemaining = Math.floor(waveNumber * 4 + Math.pow(waveNumber, 1.5) * 2);
statusText.setText('Wave ' + waveNumber + ' - Enemies: ' + enemiesRemaining);
enemyBarText.setText('Enemies: ' + (enemiesRemaining + shadowCreatures.length));
// Spawn enemies gradually
var enemySpawnTimer = LK.setInterval(function () {
if (enemiesRemaining > 0) {
spawnEnemy();
enemiesRemaining--;
statusText.setText('Wave ' + waveNumber + ' - Enemies: ' + enemiesRemaining);
enemyBarText.setText('Enemies: ' + (enemiesRemaining + shadowCreatures.length));
}
}, 60);
LK.setTimeout(function () {
LK.clearInterval(enemySpawnTimer);
}, enemiesRemaining * 60);
}
function spawnEnemy() {
var enemy;
// Increase zombie spawn rate in later waves: 30% + 5% per wave (capped at 70%)
var zombieChance = Math.min(0.7, 0.3 + waveNumber * 0.05);
if (Math.random() < zombieChance) {
enemy = new Zombie();
} else {
enemy = new ShadowCreature();
}
// Spawn from random edge
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// Top
enemy.x = Math.random() * 2048;
enemy.y = -50;
break;
case 1:
// Right
enemy.x = 2098;
enemy.y = Math.random() * 2732;
break;
case 2:
// Bottom
enemy.x = Math.random() * 2048;
enemy.y = 2782;
break;
case 3:
// Left
enemy.x = -50;
enemy.y = Math.random() * 2732;
break;
}
game.addChild(enemy);
if (enemy instanceof Zombie) {
zombies.push(enemy);
} else {
shadowCreatures.push(enemy);
}
}
function endWave() {
waveActive = false;
waveNumber++;
// Reduce time between waves as difficulty increases
waveTimer = Math.max(120, timeBetweenWaves - waveNumber * 30);
waveText.setText('Wave: ' + waveNumber);
statusText.setText('Wave Complete! Next wave in ' + Math.ceil(waveTimer / 60) + 's');
// Bonus essences for completing wave
currentEssences += 20;
updateEssenceDisplay();
}
function updateEssenceDisplay() {
essenceText.setText('Essences: ' + currentEssences);
}
function collectEssence(essence) {
currentEssences += essence.value;
updateEssenceDisplay();
LK.getSound('collect').play();
}
// Input handling
var dragNode = null;
var plantingMode = false;
// Buy button click handler
buyButton.down = function (x, y, obj) {
if (currentEssences >= plantCost && !waveActive) {
selectedPlantType = 'peashooter';
plantingMode = true;
buyButton.setText('Click to Plant!');
buyButton.fill = 0xFFFF00;
heavyBuyButton.setText('Buy Heavy (50)');
heavyBuyButton.fill = 0x00AA00;
}
};
// Heavy buy button click handler
heavyBuyButton.down = function (x, y, obj) {
if (currentEssences >= heavyPlantCost && !waveActive) {
selectedPlantType = 'heavy';
plantingMode = true;
heavyBuyButton.setText('Click to Plant!');
heavyBuyButton.fill = 0xFFFF00;
buyButton.setText('Buy Tower (25)');
buyButton.fill = 0x00FF00;
}
};
function handleMove(x, y, obj) {
if (dragNode) {
dragNode.x = x;
dragNode.y = y;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Set hero target position for movement
hero.targetX = x;
hero.targetY = y;
hero.isMoving = true;
// Check if in planting mode
if (plantingMode && !waveActive) {
var currentCost = selectedPlantType === 'heavy' ? heavyPlantCost : plantCost;
if (currentEssences >= currentCost) {
// Check if position is valid (not too close to existing plants or sprouts)
var validPosition = true;
for (var i = 0; i < peashooterPods.length; i++) {
var pod = peashooterPods[i];
var distance = Math.sqrt(Math.pow(x - pod.x, 2) + Math.pow(y - pod.y, 2));
if (distance < 80) {
validPosition = false;
break;
}
}
for (var i = 0; i < heavyPeashooterPods.length; i++) {
var heavyPod = heavyPeashooterPods[i];
var distance = Math.sqrt(Math.pow(x - heavyPod.x, 2) + Math.pow(y - heavyPod.y, 2));
if (distance < 80) {
validPosition = false;
break;
}
}
for (var i = 0; i < manaSprouts.length; i++) {
var sprout = manaSprouts[i];
var distance = Math.sqrt(Math.pow(x - sprout.x, 2) + Math.pow(y - sprout.y, 2));
if (distance < 80) {
validPosition = false;
break;
}
}
if (validPosition) {
if (selectedPlantType === 'heavy') {
var newHeavyPod = new HeavyPeashooterPod();
newHeavyPod.x = x;
newHeavyPod.y = y;
game.addChild(newHeavyPod);
heavyPeashooterPods.push(newHeavyPod);
currentEssences -= heavyPlantCost;
} else {
var newPod = new PeashooterPod();
newPod.x = x;
newPod.y = y;
game.addChild(newPod);
peashooterPods.push(newPod);
currentEssences -= plantCost;
}
updateEssenceDisplay();
LK.getSound('plant').play();
plantingMode = false;
buyButton.setText('Buy Tower (25)');
buyButton.fill = 0x00FF00;
heavyBuyButton.setText('Buy Heavy (50)');
heavyBuyButton.fill = 0x00AA00;
}
}
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Start first wave after delay
waveTimer = 180; // 3 seconds
game.update = function () {
// Handle wave timing
if (!waveActive) {
if (waveTimer > 0) {
waveTimer--;
if (waveTimer % 60 === 0) {
statusText.setText('Next wave in ' + Math.ceil(waveTimer / 60) + 's');
}
} else {
startWave();
}
} else {
// Check if wave is complete
if (enemiesRemaining <= 0 && shadowCreatures.length === 0 && zombies.length === 0) {
endWave();
}
}
// Update enemy bar display
var totalEnemies = enemiesRemaining + shadowCreatures.length + zombies.length;
enemyBarText.setText('Enemies: ' + totalEnemies);
// Update buy button availability
if (!plantingMode) {
if (waveActive) {
buyButton.setText('Buy Tower (Wave Active)');
buyButton.fill = 0xFF0000;
heavyBuyButton.setText('Buy Heavy (Wave Active)');
heavyBuyButton.fill = 0xFF0000;
} else if (currentEssences < plantCost) {
buyButton.setText('Buy Tower (Need ' + plantCost + ')');
buyButton.fill = 0xFF0000;
if (currentEssences < heavyPlantCost) {
heavyBuyButton.setText('Buy Heavy (Need ' + heavyPlantCost + ')');
heavyBuyButton.fill = 0xFF0000;
} else {
heavyBuyButton.setText('Buy Heavy (50)');
heavyBuyButton.fill = 0x00AA00;
}
} else {
buyButton.setText('Buy Tower (25)');
buyButton.fill = 0x00FF00;
if (currentEssences < heavyPlantCost) {
heavyBuyButton.setText('Buy Heavy (Need ' + heavyPlantCost + ')');
heavyBuyButton.fill = 0xFF0000;
} else {
heavyBuyButton.setText('Buy Heavy (50)');
heavyBuyButton.fill = 0x00AA00;
}
}
}
// Update projectiles
for (var i = projectiles.length - 1; i >= 0; i--) {
var projectile = projectiles[i];
var shouldDestroy = projectile.update();
if (shouldDestroy) {
projectile.destroy();
projectiles.splice(i, 1);
}
}
// Check for destroyed towers
for (var i = peashooterPods.length - 1; i >= 0; i--) {
var pod = peashooterPods[i];
if (pod.hp <= 0) {
pod.destroy();
peashooterPods.splice(i, 1);
}
}
// Check for destroyed heavy towers
for (var i = heavyPeashooterPods.length - 1; i >= 0; i--) {
var heavyPod = heavyPeashooterPods[i];
if (heavyPod.hp <= 0) {
heavyPod.destroy();
heavyPeashooterPods.splice(i, 1);
}
}
// Check for destroyed mana sprouts
for (var i = manaSprouts.length - 1; i >= 0; i--) {
var sprout = manaSprouts[i];
if (sprout.hp <= 0) {
sprout.destroy();
manaSprouts.splice(i, 1);
}
}
// Check game over condition - 3 towers destroyed
if (towersDestroyed >= 3) {
LK.showGameOver();
}
// Check defeat condition - all mana sprouts destroyed
if (manaSproutsDestroyed >= 3) {
LK.showGameOver();
}
// Check essence collection
for (var i = sunlightEssences.length - 1; i >= 0; i--) {
var essence = sunlightEssences[i];
var distance = Math.sqrt(Math.pow(hero.x - essence.x, 2) + Math.pow(hero.y - essence.y, 2));
if (distance < 40) {
collectEssence(essence);
essence.destroy();
sunlightEssences.splice(i, 1);
}
}
// Hero attack now handled in Hero class update method
// Check win condition
if (waveNumber > 5 && !waveActive && shadowCreatures.length === 0 && zombies.length === 0) {
LK.showYouWin();
}
};
Hero. In-Game asset. 2d. High contrast. No shadows
manaSprout. In-Game asset. 2d. High contrast. No shadows
sunlightEssence. In-Game asset. 2d. High contrast. No shadows
projectile. In-Game asset. 2d. High contrast. No shadows
peashooterPod. In-Game asset. 2d. High contrast. No shadows
shadowCreature. In-Game asset. 2d. High contrast. No shadows
Makeup green colour background in the background and grass. In-Game asset. 2d. High contrast. No shadows
zombie. In-Game asset. 2d. High contrast. No shadows