/**** * 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