/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var BigDefenseTower = Container.expand(function () { var self = Container.call(this); var towerGraphics = self.attachAsset('defenseTower', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5, tint: 0xFF4444 }); self.health = 500; self.maxHealth = 500; self.damage = 80; self.range = 450; self.shootCooldown = 0; self.update = function () { var target = self.findNearestEnemy(); if (target && self.shootCooldown <= 0) { self.shootAt(target); self.shootCooldown = 35; // Slightly faster than regular tower } if (self.shootCooldown > 0) { self.shootCooldown--; } }; self.findNearestEnemy = function () { var nearest = null; var minDistance = Infinity; // Check zombies for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var distance = Math.sqrt(Math.pow(zombie.x - self.x, 2) + Math.pow(zombie.y - self.y, 2)); if (distance < minDistance && distance < self.range) { minDistance = distance; nearest = zombie; } } // Check enemy bots for (var i = 0; i < enemyBots.length; i++) { var bot = enemyBots[i]; var distance = Math.sqrt(Math.pow(bot.x - self.x, 2) + Math.pow(bot.y - self.y, 2)); if (distance < minDistance && distance < self.range) { minDistance = distance; nearest = bot; } } return nearest; }; self.shootAt = function (target) { var bullet = new BigTowerBullet(); bullet.x = self.x; bullet.y = self.y; bullet.targetX = target.x; bullet.targetY = target.y; bullet.damage = self.damage; bigTowerBullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); }; self.takeDamage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xff0000, 200); if (self.health <= 0) { self.die(); } }; self.die = function () { self.destroy(); var index = bigDefenseTowers.indexOf(self); if (index > -1) { bigDefenseTowers.splice(index, 1); } }; return self; }); var BigTowerBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5, tint: 0xFF4444 }); self.speed = 10; self.damage = 80; self.targetX = 0; self.targetY = 0; self.update = function () { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > self.speed) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } else { self.explode(); } // Remove if off screen if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) { self.destroy(); var index = bigTowerBullets.indexOf(self); if (index > -1) { bigTowerBullets.splice(index, 1); } } }; self.explode = function () { // Check for zombie hits for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; if (self.intersects(zombie)) { zombie.takeDamage(self.damage); break; } } // Check for enemy bot hits for (var i = 0; i < enemyBots.length; i++) { var bot = enemyBots[i]; if (self.intersects(bot)) { bot.takeDamage(self.damage); break; } } self.destroy(); var index = bigTowerBullets.indexOf(self); if (index > -1) { bigTowerBullets.splice(index, 1); } }; return self; }); var Boss = Container.expand(function () { var self = Container.call(this); var bossGraphics = self.attachAsset('boss', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.0, scaleY: 2.0 }); self.health = 4000; self.maxHealth = 4000; self.speed = 0.8; // Slower than regular zombies self.damage = 100; // High damage self.target = null; self.attackCooldown = 0; self.specialAttackCooldown = 0; self.update = function () { if (!self.target) { self.target = playerBase; } // Move towards 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 > 80) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } else { // Attack target if (self.attackCooldown <= 0) { if (self.target === playerBase) { playerBase.takeDamage(self.damage); } self.attackCooldown = 90; // Slower attack rate } } // Special AOE attack every 5 seconds if (self.specialAttackCooldown <= 0) { self.aoeAttack(); self.specialAttackCooldown = 300; // 5 seconds } if (self.attackCooldown > 0) { self.attackCooldown--; } if (self.specialAttackCooldown > 0) { self.specialAttackCooldown--; } }; self.aoeAttack = function () { // AOE damage to all towers and hero in range var aoeRange = 400; LK.effects.flashObject(self, 0xFFFF00, 500); // Damage hero var distanceToHero = Math.sqrt(Math.pow(hero.x - self.x, 2) + Math.pow(hero.y - self.y, 2)); if (distanceToHero < aoeRange) { hero.takeDamage(50); } // Damage all towers in range for (var i = 0; i < defenseTowers.length; i++) { var tower = defenseTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2)); if (distance < aoeRange) { tower.takeDamage(80); } } for (var i = 0; i < bigDefenseTowers.length; i++) { var tower = bigDefenseTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2)); if (distance < aoeRange) { tower.takeDamage(80); } } for (var i = 0; i < rapidFireTowers.length; i++) { var tower = rapidFireTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2)); if (distance < aoeRange) { tower.takeDamage(80); } } for (var i = 0; i < sniperTowers.length; i++) { var tower = sniperTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2)); if (distance < aoeRange) { tower.takeDamage(80); } } LK.getSound('explosion').play(); }; self.takeDamage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xff0000, 200); if (self.health <= 0) { self.die(); } }; self.die = function () { LK.getSound('explosion').play(); LK.setScore(LK.getScore() + 500); scoreText.setText('Score: ' + LK.getScore()); // Boss gives lots of money var moneyEarned = 200; money += moneyEarned; moneyText.setText('Money: $' + money); var moneyIcon = new MoneyIcon(moneyEarned); moneyIcon.x = self.x; moneyIcon.y = self.y; moneyIcons.push(moneyIcon); game.addChild(moneyIcon); self.destroy(); var index = bosses.indexOf(self); if (index > -1) { bosses.splice(index, 1); } }; return self; }); var BuyBigTowerButton = Container.expand(function () { var self = Container.call(this); var buttonGraphics = self.attachAsset('buyButton', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2, tint: 0xFF4444 }); var towerIcon = self.attachAsset('defenseTower', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.6, tint: 0xFF4444, y: -15 }); var buttonText = new Text2('Big Tower', { size: 28, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); buttonText.y = 15; self.addChild(buttonText); var costText = new Text2('R:100 $100', { size: 22, fill: 0xFFFFFF }); costText.anchor.set(0.5, 0.5); costText.y = 35; self.addChild(costText); self.down = function (x, y, obj) { var towerCost = 100; if (resources >= 100 && money >= towerCost) { // Enable big tower placement mode placingBigTower = true; LK.effects.flashObject(self, 0x00FF00, 300); } else { // Flash red if can't afford LK.effects.flashObject(self, 0xFF0000, 300); } }; self.update = function () { var towerCost = 100 + Math.floor(wave / 2) * 15; var resourceCost = 100 + Math.floor(wave / 3) * 20; if (resources >= resourceCost && money >= towerCost) { buttonGraphics.tint = 0xFF2222; // Red when affordable buttonText.setText('Big Tower'); costText.setText('R:' + resourceCost + ' $' + towerCost); } else { buttonGraphics.tint = 0x808080; // Gray when not affordable buttonText.setText('Big Tower'); costText.setText('R:' + resourceCost + ' $' + towerCost); } }; return self; }); var BuyRapidFireTowerButton = Container.expand(function () { var self = Container.call(this); var buttonGraphics = self.attachAsset('buyButton', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0, tint: 0x00FF00 }); var towerIcon = self.attachAsset('defenseTower', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3, tint: 0x00FF00, y: -15 }); var buttonText = new Text2('Rapid', { size: 28, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); buttonText.y = 15; self.addChild(buttonText); var costText = new Text2('R:75 $40', { size: 22, fill: 0xFFFFFF }); costText.anchor.set(0.5, 0.5); costText.y = 35; self.addChild(costText); self.down = function (x, y, obj) { var towerCost = 40; if (resources >= 75 && money >= towerCost) { // Enable rapid fire tower placement mode placingRapidFireTower = true; LK.effects.flashObject(self, 0x00FF00, 300); } else { // Flash red if can't afford LK.effects.flashObject(self, 0xFF0000, 300); } }; self.update = function () { var towerCost = 40; if (resources >= 75 && money >= towerCost) { buttonGraphics.tint = 0x00AA00; // Green when affordable buttonText.setText('Rapid'); costText.setText('R:75 $40'); } else { buttonGraphics.tint = 0x808080; // Gray when not affordable buttonText.setText('Rapid'); costText.setText('R:75 $40'); } }; return self; }); var BuySniperTowerButton = Container.expand(function () { var self = Container.call(this); var buttonGraphics = self.attachAsset('buyButton', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0, tint: 0x0088FF }); var towerIcon = self.attachAsset('defenseTower', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5, tint: 0x0088FF, y: -15 }); var buttonText = new Text2('Sniper', { size: 26, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); buttonText.y = 15; self.addChild(buttonText); var costText = new Text2('R:125 $75', { size: 20, fill: 0xFFFFFF }); costText.anchor.set(0.5, 0.5); costText.y = 35; self.addChild(costText); self.down = function (x, y, obj) { var towerCost = 75; if (resources >= 125 && money >= towerCost) { // Enable sniper tower placement mode placingSniperTower = true; LK.effects.flashObject(self, 0x00FF00, 300); } else { // Flash red if can't afford LK.effects.flashObject(self, 0xFF0000, 300); } }; self.update = function () { var towerCost = 75; if (resources >= 125 && money >= towerCost) { buttonGraphics.tint = 0x0066CC; // Blue when affordable buttonText.setText('Sniper'); costText.setText('R:125 $75'); } else { buttonGraphics.tint = 0x808080; // Gray when not affordable buttonText.setText('Sniper'); costText.setText('R:125 $75'); } }; return self; }); var BuyTowerButton = Container.expand(function () { var self = Container.call(this); var buttonGraphics = self.attachAsset('buyButton', { anchorX: 0.5, anchorY: 0.5 }); var towerIcon = self.attachAsset('defenseTower', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4, y: -15 }); var buttonText = new Text2('Tower', { size: 32, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); buttonText.y = 15; self.addChild(buttonText); var costText = new Text2('R:50 $25', { size: 24, fill: 0xFFFFFF }); costText.anchor.set(0.5, 0.5); costText.y = 35; self.addChild(costText); self.down = function (x, y, obj) { var towerCost = 25; if (resources >= 50 && money >= towerCost) { // Enable tower placement mode placingTower = true; LK.effects.flashObject(self, 0x00FF00, 300); } else { // Flash red if can't afford LK.effects.flashObject(self, 0xFF0000, 300); } }; self.update = function () { var towerCost = 25 + Math.floor(wave / 2) * 5; var resourceCost = 50 + Math.floor(wave / 3) * 10; if (resources >= resourceCost && money >= towerCost) { buttonGraphics.tint = 0x4CAF50; // Green when affordable buttonText.setText('Tower'); costText.setText('R:' + resourceCost + ' $' + towerCost); } else { buttonGraphics.tint = 0x808080; // Gray when not affordable buttonText.setText('Tower'); costText.setText('R:' + resourceCost + ' $' + towerCost); } }; return self; }); var DefenseTower = Container.expand(function () { var self = Container.call(this); var towerGraphics = self.attachAsset('defenseTower', { anchorX: 0.5, anchorY: 0.5 }); self.health = 200; self.maxHealth = 200; self.damage = 30; self.range = 300; self.shootCooldown = 0; self.update = function () { var target = self.findNearestEnemy(); if (target && self.shootCooldown <= 0) { self.shootAt(target); self.shootCooldown = 45; // 0.75 second cooldown } if (self.shootCooldown > 0) { self.shootCooldown--; } }; self.findNearestEnemy = function () { var nearest = null; var minDistance = Infinity; for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var distance = Math.sqrt(Math.pow(zombie.x - self.x, 2) + Math.pow(zombie.y - self.y, 2)); if (distance < minDistance && distance < self.range) { minDistance = distance; nearest = zombie; } } // Check bosses for (var i = 0; i < bosses.length; i++) { var boss = bosses[i]; var distance = Math.sqrt(Math.pow(boss.x - self.x, 2) + Math.pow(boss.y - self.y, 2)); if (distance < minDistance && distance < self.range) { minDistance = distance; nearest = boss; } } return nearest; }; self.shootAt = function (target) { var bullet = new TowerBullet(); bullet.x = self.x; bullet.y = self.y; bullet.targetX = target.x; bullet.targetY = target.y; bullet.damage = self.damage; towerBullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); }; self.takeDamage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xff0000, 200); if (self.health <= 0) { self.die(); } }; self.die = function () { self.destroy(); var index = defenseTowers.indexOf(self); if (index > -1) { defenseTowers.splice(index, 1); } }; return self; }); var EnemyBot = Container.expand(function (botType) { var self = Container.call(this); var botGraphics = self.attachAsset(botType, { anchorX: 0.5, anchorY: 0.5 }); self.botType = botType; // Scale bot stats based on current wave var healthMultiplier = 1 + (wave - 1) * 0.25; // 25% more health per wave var damageMultiplier = 1 + (wave - 1) * 0.15; // 15% more damage per wave self.health = Math.floor(150 * healthMultiplier); self.maxHealth = Math.floor(150 * healthMultiplier); self.damage = Math.floor(25 * damageMultiplier); self.range = 350; self.shootCooldown = 0; // Set specific properties based on bot type if (botType === 'artilleryBot') { self.damage = 60; self.range = 400; self.shootCooldown = 90; // Slower but more powerful } else if (botType === 'assaultBot') { self.damage = 20; self.range = 250; self.shootCooldown = 20; // Faster firing } else if (botType === 'disruptionBot') { self.damage = 15; self.range = 300; self.shootCooldown = 120; // Special ability cooldown } else if (botType === 'repairBot') { self.damage = 0; self.range = 200; self.shootCooldown = 180; // Repair cooldown } self.update = function () { var target = self.findTarget(); if (target) { // Move towards target if not in range var dx = target.x - self.x; var dy = target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > self.range) { // Move towards target var speed = 1.5; // Bot movement speed self.x += dx / distance * speed; self.y += dy / distance * speed; } } else { // No target in range, move towards player base var dx = playerBase.x - self.x; var dy = playerBase.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 80) { // Don't get too close to base var speed = 1.2; // Slightly slower when moving to base self.x += dx / distance * speed; self.y += dy / distance * speed; } } if (target && self.shootCooldown <= 0) { if (self.botType === 'repairBot') { self.repairAllies(); } else { self.shootAt(target); } self.shootCooldown = self.getBaseCooldown(); } if (self.shootCooldown > 0) { self.shootCooldown--; } }; self.findTarget = function () { var targets = []; // Add player base as target var distanceToBase = Math.sqrt(Math.pow(playerBase.x - self.x, 2) + Math.pow(playerBase.y - self.y, 2)); if (distanceToBase < self.range) { targets.push({ target: playerBase, distance: distanceToBase }); } // Add hero as target var distanceToHero = Math.sqrt(Math.pow(hero.x - self.x, 2) + Math.pow(hero.y - self.y, 2)); if (distanceToHero < self.range) { targets.push({ target: hero, distance: distanceToHero }); } // Add defense towers as targets for (var i = 0; i < defenseTowers.length; i++) { var tower = defenseTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2)); if (distance < self.range) { targets.push({ target: tower, distance: distance }); } } // Add big defense towers as targets for (var i = 0; i < bigDefenseTowers.length; i++) { var tower = bigDefenseTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2)); if (distance < self.range) { targets.push({ target: tower, distance: distance }); } } // Add rapid fire towers as targets for (var i = 0; i < rapidFireTowers.length; i++) { var tower = rapidFireTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2)); if (distance < self.range) { targets.push({ target: tower, distance: distance }); } } // Add sniper towers as targets for (var i = 0; i < sniperTowers.length; i++) { var tower = sniperTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2)); if (distance < self.range) { targets.push({ target: tower, distance: distance }); } } // Return nearest target if (targets.length > 0) { targets.sort(function (a, b) { return a.distance - b.distance; }); return targets[0].target; } return null; }; self.shootAt = function (target) { var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y; bullet.targetX = target.x; bullet.targetY = target.y; bullet.damage = self.damage; bullet.isAOE = self.botType === 'artilleryBot'; enemyBullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); }; self.repairAllies = function () { for (var i = 0; i < enemyBots.length; i++) { var bot = enemyBots[i]; if (bot !== self) { var distance = Math.sqrt(Math.pow(bot.x - self.x, 2) + Math.pow(bot.y - self.y, 2)); if (distance < self.range) { bot.health = Math.min(bot.health + 30, bot.maxHealth); LK.effects.flashObject(bot, 0x00ff00, 300); } } } }; self.getBaseCooldown = function () { if (self.botType === 'artilleryBot') return 90; if (self.botType === 'assaultBot') return 20; if (self.botType === 'disruptionBot') return 120; if (self.botType === 'repairBot') return 180; return 60; }; self.takeDamage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xff0000, 200); if (self.health <= 0) { self.die(); } }; self.die = function () { LK.getSound('explosion').play(); LK.setScore(LK.getScore() + 25); scoreText.setText('Score: ' + LK.getScore()); // Add money and spawn money icon var moneyEarned = 15; money += moneyEarned; moneyText.setText('Money: $' + money); var moneyIcon = new MoneyIcon(moneyEarned); moneyIcon.x = self.x; moneyIcon.y = self.y; moneyIcons.push(moneyIcon); game.addChild(moneyIcon); self.destroy(); var index = enemyBots.indexOf(self); if (index > -1) { enemyBots.splice(index, 1); } }; return self; }); var EnemyBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('enemyBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 6; self.damage = 25; self.targetX = 0; self.targetY = 0; self.isAOE = false; self.update = function () { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > self.speed) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } else { self.explode(); } // Remove if off screen if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) { self.destroy(); var index = enemyBullets.indexOf(self); if (index > -1) { enemyBullets.splice(index, 1); } } }; self.explode = function () { if (self.isAOE) { // AOE damage var explosion = LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, alpha: 0.7 }); game.addChild(explosion); tween(explosion, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 500, onFinish: function onFinish() { explosion.destroy(); } }); // Damage everything in range var explosionRange = 150; if (self.intersects(playerBase)) { playerBase.takeDamage(self.damage); } if (self.intersects(hero)) { hero.takeDamage(self.damage); } for (var i = 0; i < defenseTowers.length; i++) { var tower = defenseTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2)); if (distance < explosionRange) { tower.takeDamage(self.damage); } } for (var i = 0; i < bigDefenseTowers.length; i++) { var tower = bigDefenseTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2)); if (distance < explosionRange) { tower.takeDamage(self.damage); } } for (var i = 0; i < rapidFireTowers.length; i++) { var tower = rapidFireTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2)); if (distance < explosionRange) { tower.takeDamage(self.damage); } } for (var i = 0; i < sniperTowers.length; i++) { var tower = sniperTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2)); if (distance < explosionRange) { tower.takeDamage(self.damage); } } LK.getSound('explosion').play(); } else { // Single target damage if (self.intersects(playerBase)) { playerBase.takeDamage(self.damage); } else if (self.intersects(hero)) { hero.takeDamage(self.damage); } else { for (var i = 0; i < defenseTowers.length; i++) { var tower = defenseTowers[i]; if (self.intersects(tower)) { tower.takeDamage(self.damage); break; } } for (var i = 0; i < bigDefenseTowers.length; i++) { var tower = bigDefenseTowers[i]; if (self.intersects(tower)) { tower.takeDamage(self.damage); break; } } for (var i = 0; i < rapidFireTowers.length; i++) { var tower = rapidFireTowers[i]; if (self.intersects(tower)) { tower.takeDamage(self.damage); break; } } for (var i = 0; i < sniperTowers.length; i++) { var tower = sniperTowers[i]; if (self.intersects(tower)) { tower.takeDamage(self.damage); break; } } } } self.destroy(); var index = enemyBullets.indexOf(self); if (index > -1) { enemyBullets.splice(index, 1); } }; return self; }); var Hero = Container.expand(function () { var self = Container.call(this); var heroGraphics = self.attachAsset('hero', { anchorX: 0.5, anchorY: 0.5 }); self.health = 300; self.maxHealth = 300; self.speed = 3; self.damage = 40; self.shootCooldown = 0; self.targetX = self.x; self.targetY = self.y; // Special abilities for boss waves self.specialAttackCooldown = 0; self.shieldCooldown = 0; self.isShielded = false; self.shieldDuration = 0; self.update = function () { // Only move and attack if alive if (self.health > 0) { // Move towards target position var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } // Auto-attack nearest enemy var nearestEnemy = self.findNearestEnemy(); if (nearestEnemy && self.shootCooldown <= 0) { self.shootAt(nearestEnemy); self.shootCooldown = 30; // 0.5 second cooldown } // Special abilities during boss waves (wave 9+) if (wave >= 9) { // Special attack - shoots multiple bullets in a spread if (self.specialAttackCooldown <= 0) { self.specialAttack(); self.specialAttackCooldown = 300; // 5 seconds } // Shield ability - temporary damage immunity if (self.shieldCooldown <= 0 && !self.isShielded) { self.activateShield(); self.shieldCooldown = 600; // 10 seconds } } else { // Reset special abilities when not in boss waves self.specialAttackCooldown = 0; self.shieldCooldown = 0; self.isShielded = false; self.shieldDuration = 0; self.alpha = 1.0; // Ensure normal opacity } } if (self.shootCooldown > 0) { self.shootCooldown--; } // Update special ability cooldowns if (self.specialAttackCooldown > 0) { self.specialAttackCooldown--; } if (self.shieldCooldown > 0) { self.shieldCooldown--; } if (self.isShielded) { self.shieldDuration--; if (self.shieldDuration <= 0) { self.isShielded = false; self.alpha = 1.0; // Return to normal opacity } } // Update health display var healthStatus = self.isShielded ? ' (SHIELDED)' : ''; heroHealthText.setText('Hero Health: ' + Math.max(0, self.health) + '/' + self.maxHealth + healthStatus); }; self.findNearestEnemy = function () { var nearest = null; var minDistance = Infinity; // Check zombies for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var distance = Math.sqrt(Math.pow(zombie.x - self.x, 2) + Math.pow(zombie.y - self.y, 2)); if (distance < minDistance && distance < 400) { minDistance = distance; nearest = zombie; } } // Check enemy bots for (var i = 0; i < enemyBots.length; i++) { var bot = enemyBots[i]; var distance = Math.sqrt(Math.pow(bot.x - self.x, 2) + Math.pow(bot.y - self.y, 2)); if (distance < minDistance && distance < 400) { minDistance = distance; nearest = bot; } } // Check bosses for (var i = 0; i < bosses.length; i++) { var boss = bosses[i]; var distance = Math.sqrt(Math.pow(boss.x - self.x, 2) + Math.pow(boss.y - self.y, 2)); if (distance < minDistance && distance < 400) { minDistance = distance; nearest = boss; } } return nearest; }; self.shootAt = function (target) { var bullet = new HeroBullet(); bullet.x = self.x; bullet.y = self.y; bullet.targetX = target.x; bullet.targetY = target.y; bullet.damage = self.damage; heroBullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); }; self.specialAttack = function () { // Fire 5 bullets in a spread pattern var nearestEnemy = self.findNearestEnemy(); if (nearestEnemy) { for (var i = 0; i < 5; i++) { var bullet = new HeroBullet(); bullet.x = self.x; bullet.y = self.y; // Calculate spread angle var angleOffset = (i - 2) * 0.3; // Spread bullets in a fan var dx = nearestEnemy.x - self.x; var dy = nearestEnemy.y - self.y; var angle = Math.atan2(dy, dx) + angleOffset; var distance = 400; // Target distance bullet.targetX = self.x + Math.cos(angle) * distance; bullet.targetY = self.y + Math.sin(angle) * distance; bullet.damage = self.damage * 1.5; // Increased damage for special attack heroBullets.push(bullet); game.addChild(bullet); } LK.getSound('shoot').play(); LK.effects.flashObject(self, 0xFFFF00, 500); // Yellow flash for special attack } }; self.activateShield = function () { self.isShielded = true; self.shieldDuration = 180; // 3 seconds of shield self.alpha = 0.7; // Make hero semi-transparent to show shield LK.effects.flashObject(self, 0x00FFFF, 300); // Cyan flash for shield activation }; self.moveTo = function (x, y) { if (self.health > 0) { self.targetX = x; self.targetY = y; } }; self.takeDamage = function (amount) { // Shield blocks damage during boss waves if (self.isShielded) { LK.effects.flashObject(self, 0x00FFFF, 200); // Cyan flash when shielded return; // No damage taken while shielded } self.health -= amount; // Prevent hero health from going below 1 - hero cannot die if (self.health < 1) { self.health = 1; } LK.effects.flashObject(self, 0xff0000, 300); // Remove death condition - hero can no longer die }; self.die = function () { // Hero becomes translucent and stops moving when dead self.alpha = 0.3; self.targetX = self.x; self.targetY = self.y; heroHealthText.setText('Hero Health: 0/' + self.maxHealth + ' (DEAD)'); // Attack player base when hero dies playerBase.takeDamage(self.damage * 2); // Double damage when hero dies }; return self; }); var HeroBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('heroBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 10; self.damage = 40; self.targetX = 0; self.targetY = 0; self.update = function () { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > self.speed) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } else { self.explode(); } // Remove if off screen if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) { self.destroy(); var index = heroBullets.indexOf(self); if (index > -1) { heroBullets.splice(index, 1); } } }; self.explode = function () { // Check for zombie hits for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; if (self.intersects(zombie)) { zombie.takeDamage(self.damage); self.destroy(); var index = heroBullets.indexOf(self); if (index > -1) { heroBullets.splice(index, 1); } return; } } // Check for enemy bot hits for (var i = 0; i < enemyBots.length; i++) { var bot = enemyBots[i]; if (self.intersects(bot)) { bot.takeDamage(self.damage); self.destroy(); var index = heroBullets.indexOf(self); if (index > -1) { heroBullets.splice(index, 1); } return; } } // Check for boss hits for (var i = 0; i < bosses.length; i++) { var boss = bosses[i]; if (self.intersects(boss)) { boss.takeDamage(self.damage); self.destroy(); var index = heroBullets.indexOf(self); if (index > -1) { heroBullets.splice(index, 1); } return; } } self.destroy(); var index = heroBullets.indexOf(self); if (index > -1) { heroBullets.splice(index, 1); } }; return self; }); var LeaderboardScreen = Container.expand(function () { var self = Container.call(this); var background = LK.getAsset('buyButton', { anchorX: 0.5, anchorY: 0.5, scaleX: 10, scaleY: 8, tint: 0x000000, alpha: 0.9 }); self.addChild(background); var titleText = new Text2('LEADERBOARD', { size: 80, fill: 0xFFD700 }); titleText.anchor.set(0.5, 0.5); titleText.y = -300; self.addChild(titleText); var currentScoreText = new Text2('Your Score: 0', { size: 50, fill: 0x00FF00 }); currentScoreText.anchor.set(0.5, 0.5); currentScoreText.y = -200; self.addChild(currentScoreText); var positionText = new Text2('Your Position: #1', { size: 45, fill: 0xFFFFFF }); positionText.anchor.set(0.5, 0.5); positionText.y = -150; self.addChild(positionText); var scoresText = new Text2('', { size: 35, fill: 0xFFFFFF }); scoresText.anchor.set(0.5, 0.5); scoresText.y = 50; self.addChild(scoresText); var closeText = new Text2('Tap to close', { size: 40, fill: 0xFFFFFF }); closeText.anchor.set(0.5, 0.5); closeText.y = 250; self.addChild(closeText); self.visible = false; self.down = function (x, y, obj) { self.visible = false; }; self.updateLeaderboard = function () { var currentScore = LK.getScore(); currentScoreText.setText('Your Score: ' + currentScore); // Get stored high scores or initialize empty array var highScores = storage.highScores || []; // Add current score to the list var allScores = highScores.slice(); allScores.push(currentScore); // Sort scores in descending order allScores.sort(function (a, b) { return b - a; }); // Keep only top 10 scores allScores = allScores.slice(0, 10); // Find player's position var playerPosition = 1; for (var i = 0; i < allScores.length; i++) { if (allScores[i] === currentScore) { playerPosition = i + 1; break; } } positionText.setText('Your Position: #' + playerPosition); // Display top scores var scoresDisplay = 'TOP SCORES:\n'; for (var i = 0; i < Math.min(5, allScores.length); i++) { var highlight = allScores[i] === currentScore ? ' <- YOU' : ''; scoresDisplay += '#' + (i + 1) + ': ' + allScores[i] + highlight + '\n'; } scoresText.setText(scoresDisplay); // Save high scores back to storage (without current score) storage.highScores = highScores; }; return self; }); var MoneyIcon = Container.expand(function (amount) { var self = Container.call(this); var moneyGraphics = self.attachAsset('money', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.0, scaleY: 2.0 }); var moneyText = new Text2('+$' + amount, { size: 30, fill: 0xFFD700 }); moneyText.anchor.set(0.5, 0.5); moneyText.x = 30; self.addChild(moneyText); self.lifeTime = 120; // 2 seconds at 60fps self.floatSpeed = -2; self.update = function () { self.y += self.floatSpeed; self.alpha -= 0.02; self.lifeTime--; if (self.lifeTime <= 0 || self.alpha <= 0) { self.destroy(); var index = moneyIcons.indexOf(self); if (index > -1) { moneyIcons.splice(index, 1); } } }; return self; }); var MoneyScreen = Container.expand(function () { var self = Container.call(this); var background = LK.getAsset('buyButton', { anchorX: 0.5, anchorY: 0.5, scaleX: 8, scaleY: 6, tint: 0x000000, alpha: 0.8 }); self.addChild(background); var titleText = new Text2('Money Status', { size: 80, fill: 0xFFD700 }); titleText.anchor.set(0.5, 0.5); titleText.y = -150; self.addChild(titleText); var moneyIcon = self.attachAsset('money', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 3, y: -50 }); var currentMoneyText = new Text2('$' + money, { size: 120, fill: 0xFFD700 }); currentMoneyText.anchor.set(0.5, 0.5); currentMoneyText.y = 50; self.addChild(currentMoneyText); var closeText = new Text2('Tap to close', { size: 40, fill: 0xFFFFFF }); closeText.anchor.set(0.5, 0.5); closeText.y = 150; self.addChild(closeText); self.visible = false; self.down = function (x, y, obj) { self.visible = false; }; self.update = function () { if (self.visible) { currentMoneyText.setText('$' + money); } }; return self; }); var PlayerBase = Container.expand(function () { var self = Container.call(this); var baseGraphics = self.attachAsset('playerBase', { anchorX: 0.5, anchorY: 0.5 }); self.health = 3000; self.maxHealth = 3000; self.takeDamage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xff0000, 300); healthText.setText('Base Health: ' + self.health + '/' + self.maxHealth); if (self.health <= 0) { gameOver = true; // Save high score before showing game over screen var currentScore = LK.getScore(); var highScores = storage.highScores || []; highScores.push(currentScore); highScores.sort(function (a, b) { return b - a; }); storage.highScores = highScores.slice(0, 10); // Keep top 10 LK.showGameOver(); } }; return self; }); var RapidFireBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.6, tint: 0x00FF00 }); self.speed = 12; self.damage = 15; self.targetX = 0; self.targetY = 0; self.update = function () { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > self.speed) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } else { self.explode(); } // Remove if off screen if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) { self.destroy(); var index = rapidFireBullets.indexOf(self); if (index > -1) { rapidFireBullets.splice(index, 1); } } }; self.explode = function () { // Check for zombie hits for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; if (self.intersects(zombie)) { zombie.takeDamage(self.damage); break; } } // Check for enemy bot hits for (var i = 0; i < enemyBots.length; i++) { var bot = enemyBots[i]; if (self.intersects(bot)) { bot.takeDamage(self.damage); break; } } self.destroy(); var index = rapidFireBullets.indexOf(self); if (index > -1) { rapidFireBullets.splice(index, 1); } }; return self; }); var RapidFireTower = Container.expand(function () { var self = Container.call(this); var towerGraphics = self.attachAsset('defenseTower', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8, tint: 0x00FF00 }); self.health = 150; self.maxHealth = 150; self.damage = 15; self.range = 250; self.shootCooldown = 0; self.update = function () { var target = self.findNearestEnemy(); if (target && self.shootCooldown <= 0) { self.shootAt(target); self.shootCooldown = 15; // Very fast firing } if (self.shootCooldown > 0) { self.shootCooldown--; } }; self.findNearestEnemy = function () { var nearest = null; var minDistance = Infinity; // Check zombies for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var distance = Math.sqrt(Math.pow(zombie.x - self.x, 2) + Math.pow(zombie.y - self.y, 2)); if (distance < minDistance && distance < self.range) { minDistance = distance; nearest = zombie; } } // Check enemy bots for (var i = 0; i < enemyBots.length; i++) { var bot = enemyBots[i]; var distance = Math.sqrt(Math.pow(bot.x - self.x, 2) + Math.pow(bot.y - self.y, 2)); if (distance < minDistance && distance < self.range) { minDistance = distance; nearest = bot; } } return nearest; }; self.shootAt = function (target) { var bullet = new RapidFireBullet(); bullet.x = self.x; bullet.y = self.y; bullet.targetX = target.x; bullet.targetY = target.y; bullet.damage = self.damage; rapidFireBullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); }; self.takeDamage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xff0000, 200); if (self.health <= 0) { self.die(); } }; self.die = function () { self.destroy(); var index = rapidFireTowers.indexOf(self); if (index > -1) { rapidFireTowers.splice(index, 1); } }; return self; }); var ReviveButton = Container.expand(function () { var self = Container.call(this); var buttonGraphics = self.attachAsset('buyButton', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 2.0, tint: 0xFF6666 }); var heroIcon = self.attachAsset('hero', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8, x: -80, y: -15, tint: 0xFFFFFF }); var buttonText = new Text2('Revive Hero', { size: 50, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); buttonText.y = -10; self.addChild(buttonText); var costText = new Text2('Cost: $50', { size: 35, fill: 0xFFFFFF }); costText.anchor.set(0.5, 0.5); costText.y = 30; self.addChild(costText); var moneyIcon = self.attachAsset('money', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8, x: -30, y: 30 }); self.visible = false; self.down = function (x, y, obj) { // Revive hero if dead and have enough money if (hero.health <= 0 && money >= 50) { // Revive hero hero.health = hero.maxHealth; hero.alpha = 1.0; // Make hero fully visible again money -= 50; moneyText.setText('Money: $' + money); // Flash green to show successful revive LK.effects.flashObject(self, 0x00FF00, 300); // Hide revive button after use self.visible = false; } else { // Flash red if can't revive LK.effects.flashObject(self, 0xFF0000, 300); } }; self.update = function () { // Show revive button when hero is dead and has enough money if (hero.health <= 0 && money >= 50) { self.visible = true; } else { self.visible = false; } }; return self; }); var SniperBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.8, scaleY: 1.8, tint: 0x0088FF }); self.speed = 15; self.damage = 120; self.targetX = 0; self.targetY = 0; self.update = function () { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > self.speed) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } else { self.explode(); } // Remove if off screen if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) { self.destroy(); var index = sniperBullets.indexOf(self); if (index > -1) { sniperBullets.splice(index, 1); } } }; self.explode = function () { // Check for zombie hits for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; if (self.intersects(zombie)) { zombie.takeDamage(self.damage); break; } } // Check for enemy bot hits for (var i = 0; i < enemyBots.length; i++) { var bot = enemyBots[i]; if (self.intersects(bot)) { bot.takeDamage(self.damage); break; } } self.destroy(); var index = sniperBullets.indexOf(self); if (index > -1) { sniperBullets.splice(index, 1); } }; return self; }); var SniperTower = Container.expand(function () { var self = Container.call(this); var towerGraphics = self.attachAsset('defenseTower', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2, tint: 0x0088FF }); self.health = 250; self.maxHealth = 250; self.damage = 120; self.range = 600; self.shootCooldown = 0; self.update = function () { var target = self.findNearestEnemy(); if (target && self.shootCooldown <= 0) { self.shootAt(target); self.shootCooldown = 80; // Slow but powerful } if (self.shootCooldown > 0) { self.shootCooldown--; } }; self.findNearestEnemy = function () { var nearest = null; var minDistance = Infinity; // Check zombies for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var distance = Math.sqrt(Math.pow(zombie.x - self.x, 2) + Math.pow(zombie.y - self.y, 2)); if (distance < minDistance && distance < self.range) { minDistance = distance; nearest = zombie; } } // Check enemy bots for (var i = 0; i < enemyBots.length; i++) { var bot = enemyBots[i]; var distance = Math.sqrt(Math.pow(bot.x - self.x, 2) + Math.pow(bot.y - self.y, 2)); if (distance < minDistance && distance < self.range) { minDistance = distance; nearest = bot; } } return nearest; }; self.shootAt = function (target) { var bullet = new SniperBullet(); bullet.x = self.x; bullet.y = self.y; bullet.targetX = target.x; bullet.targetY = target.y; bullet.damage = self.damage; sniperBullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); }; self.takeDamage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xff0000, 200); if (self.health <= 0) { self.die(); } }; self.die = function () { self.destroy(); var index = sniperTowers.indexOf(self); if (index > -1) { sniperTowers.splice(index, 1); } }; return self; }); var StartWaveButton = Container.expand(function () { var self = Container.call(this); var buttonGraphics = self.attachAsset('startButton', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.0, scaleY: 1.5, tint: 0x00FF00 }); var buttonText = new Text2('Start Wave ' + (wave + 1), { size: 40, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); self.addChild(buttonText); self.visible = false; self.down = function (x, y, obj) { if (self.visible) { // Start next wave wave++; zombiesSpawned = 0; zombiesPerWave += 3 + Math.floor(wave / 3); waveText.setText('Wave: ' + wave); resources += Math.max(15, 35 - wave); resourceText.setText('Resources: ' + resources); self.visible = false; waveInProgress = true; LK.effects.flashObject(self, 0x00FF00, 300); } }; self.update = function () { if (self.visible) { buttonText.setText('Start Wave ' + wave); } }; return self; }); var TowerBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.damage = 30; self.targetX = 0; self.targetY = 0; self.update = function () { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > self.speed) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } else { self.explode(); } // Remove if off screen if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) { self.destroy(); var index = towerBullets.indexOf(self); if (index > -1) { towerBullets.splice(index, 1); } } }; self.explode = function () { // Check for zombie hits for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; if (self.intersects(zombie)) { zombie.takeDamage(self.damage); break; } } // Check for boss hits for (var i = 0; i < bosses.length; i++) { var boss = bosses[i]; if (self.intersects(boss)) { boss.takeDamage(self.damage); break; } } self.destroy(); var index = towerBullets.indexOf(self); if (index > -1) { towerBullets.splice(index, 1); } }; return self; }); var Zombie = Container.expand(function () { var self = Container.call(this); var zombieGraphics = self.attachAsset('zombie', { anchorX: 0.5, anchorY: 0.5 }); // Scale health and damage based on current wave var healthMultiplier = 1 + (wave - 1) * 0.3; // 30% more health per wave var damageMultiplier = 1 + (wave - 1) * 0.2; // 20% more damage per wave var speedMultiplier = 1 + (wave - 1) * 0.1; // 10% faster per wave self.health = Math.floor(100 * healthMultiplier); self.maxHealth = Math.floor(100 * healthMultiplier); self.speed = Math.min(3, 1 * speedMultiplier); // Cap speed at 3 self.damage = Math.floor(20 * damageMultiplier); self.target = null; self.attackCooldown = 0; self.update = function () { // Find nearest target (hero, towers, or base) var target = self.findNearestTarget(); if (target) { self.target = target; } if (!self.target) { self.target = playerBase; } // Move towards 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 > 50) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } else { // Attack target if (self.attackCooldown <= 0) { if (self.target === playerBase) { playerBase.takeDamage(self.damage); } else if (self.target === hero) { hero.takeDamage(self.damage); } else if (self.target.takeDamage) { // Attack tower self.target.takeDamage(self.damage); } self.attackCooldown = 60; // 1 second cooldown } } if (self.attackCooldown > 0) { self.attackCooldown--; } }; self.findNearestTarget = function () { var targets = []; var attackRange = 400; // Range to look for targets // Add hero as target if in range and alive if (hero.health > 0) { var distanceToHero = Math.sqrt(Math.pow(hero.x - self.x, 2) + Math.pow(hero.y - self.y, 2)); if (distanceToHero < attackRange) { targets.push({ target: hero, distance: distanceToHero }); } } // Add defense towers as targets for (var i = 0; i < defenseTowers.length; i++) { var tower = defenseTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2)); if (distance < attackRange) { targets.push({ target: tower, distance: distance }); } } // Add big defense towers as targets for (var i = 0; i < bigDefenseTowers.length; i++) { var tower = bigDefenseTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2)); if (distance < attackRange) { targets.push({ target: tower, distance: distance }); } } // Add rapid fire towers as targets for (var i = 0; i < rapidFireTowers.length; i++) { var tower = rapidFireTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2)); if (distance < attackRange) { targets.push({ target: tower, distance: distance }); } } // Add sniper towers as targets for (var i = 0; i < sniperTowers.length; i++) { var tower = sniperTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2)); if (distance < attackRange) { targets.push({ target: tower, distance: distance }); } } // Add player base as target var distanceToBase = Math.sqrt(Math.pow(playerBase.x - self.x, 2) + Math.pow(playerBase.y - self.y, 2)); if (distanceToBase < attackRange) { targets.push({ target: playerBase, distance: distanceToBase }); } // Return nearest target if (targets.length > 0) { targets.sort(function (a, b) { return a.distance - b.distance; }); return targets[0].target; } return null; }; self.takeDamage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xff0000, 200); if (self.health <= 0) { self.die(); } }; self.die = function () { LK.getSound('zombieHit').play(); LK.setScore(LK.getScore() + 10); scoreText.setText('Score: ' + LK.getScore()); // Add money and spawn money icon var moneyEarned = 5; money += moneyEarned; moneyText.setText('Money: $' + money); var moneyIcon = new MoneyIcon(moneyEarned); moneyIcon.x = self.x; moneyIcon.y = self.y; moneyIcons.push(moneyIcon); game.addChild(moneyIcon); self.destroy(); var index = zombies.indexOf(self); if (index > -1) { zombies.splice(index, 1); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2a2a2a }); /**** * Game Code ****/ // Game state variables var gameOver = false; var wave = 1; var zombiesSpawned = 0; var zombiesPerWave = 5; var spawnTimer = 0; var botSpawnTimer = 0; var resources = 100; var money = 0; var placingTower = false; var placingBigTower = false; var placingRapidFireTower = false; var placingSniperTower = false; var waveInProgress = true; // Game objects arrays var zombies = []; var enemyBots = []; var bosses = []; var defenseTowers = []; var bigDefenseTowers = []; var rapidFireTowers = []; var sniperTowers = []; var towerBullets = []; var bigTowerBullets = []; var rapidFireBullets = []; var sniperBullets = []; var heroBullets = []; var enemyBullets = []; var moneyIcons = []; // Create player base var playerBase = new PlayerBase(); playerBase.x = 1024; playerBase.y = 2400; game.addChild(playerBase); // Create hero var hero = new Hero(); hero.x = 1024; hero.y = 2200; game.addChild(hero); // Create UI elements var scoreText = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreText.anchor.set(0, 0); LK.gui.topRight.addChild(scoreText); var healthText = new Text2('Base Health: 3000/3000', { size: 50, fill: 0xFFFFFF }); healthText.anchor.set(0, 0); healthText.x = -400; LK.gui.topRight.addChild(healthText); var waveText = new Text2('Wave: 1', { size: 55, fill: 0xFFFF00 }); waveText.anchor.set(0.5, 0); LK.gui.top.addChild(waveText); var resourceText = new Text2('Resources: 100', { size: 45, fill: 0x00FF00 }); resourceText.anchor.set(0, 0); LK.gui.bottomLeft.addChild(resourceText); var moneyText = new Text2('Money: $0', { size: 45, fill: 0xFFD700 }); moneyText.anchor.set(0, 0); moneyText.y = 120; moneyText.down = function (x, y, obj) { moneyScreen.visible = true; }; LK.gui.topLeft.addChild(moneyText); var heroHealthText = new Text2('Hero Health: 300/300', { size: 50, fill: 0xFF6666 }); heroHealthText.anchor.set(0, 0); heroHealthText.y = 180; LK.gui.topLeft.addChild(heroHealthText); // Create buy tower button var buyTowerButton = new BuyTowerButton(); buyTowerButton.x = 120; buyTowerButton.y = -120; LK.gui.bottomLeft.addChild(buyTowerButton); // Create buy big tower button var buyBigTowerButton = new BuyBigTowerButton(); buyBigTowerButton.x = 280; buyBigTowerButton.y = -120; LK.gui.bottomLeft.addChild(buyBigTowerButton); // Create buy rapid fire tower button var buyRapidFireTowerButton = new BuyRapidFireTowerButton(); buyRapidFireTowerButton.x = 440; buyRapidFireTowerButton.y = -120; LK.gui.bottomLeft.addChild(buyRapidFireTowerButton); // Create buy sniper tower button var buySniperTowerButton = new BuySniperTowerButton(); buySniperTowerButton.x = 600; buySniperTowerButton.y = -120; LK.gui.bottomLeft.addChild(buySniperTowerButton); // Create start wave button var startWaveButton = new StartWaveButton(); startWaveButton.x = 0; startWaveButton.y = -200; LK.gui.center.addChild(startWaveButton); // Create money screen var moneyScreen = new MoneyScreen(); moneyScreen.x = 0; moneyScreen.y = 0; LK.gui.center.addChild(moneyScreen); // Create revive button var reviveButton = new ReviveButton(); reviveButton.x = 0; reviveButton.y = 100; LK.gui.center.addChild(reviveButton); // Create leaderboard screen var leaderboardScreen = new LeaderboardScreen(); leaderboardScreen.x = 0; leaderboardScreen.y = 0; LK.gui.center.addChild(leaderboardScreen); // Create leaderboard button var leaderboardButton = new Text2('LEADERBOARD', { size: 40, fill: 0xFFD700 }); leaderboardButton.anchor.set(0.5, 0); leaderboardButton.y = 300; leaderboardButton.down = function (x, y, obj) { leaderboardScreen.updateLeaderboard(); leaderboardScreen.visible = true; }; LK.gui.topLeft.addChild(leaderboardButton); // Game control functions function spawnZombie() { var zombie = new Zombie(); // Spawn from random edge var edge = Math.floor(Math.random() * 4); if (edge === 0) { // Top zombie.x = Math.random() * 2048; zombie.y = 0; } else if (edge === 1) { // Right zombie.x = 2048; zombie.y = Math.random() * 2732; } else if (edge === 2) { // Bottom zombie.x = Math.random() * 2048; zombie.y = 2732; } else { // Left zombie.x = 0; zombie.y = Math.random() * 2732; } zombies.push(zombie); game.addChild(zombie); zombiesSpawned++; } function spawnEnemyBot() { var botTypes = ['artilleryBot', 'assaultBot', 'disruptionBot', 'repairBot']; var randomType = botTypes[Math.floor(Math.random() * botTypes.length)]; var bot = new EnemyBot(randomType); bot.x = Math.random() * 1800 + 124; bot.y = Math.random() * 1000 + 200; enemyBots.push(bot); game.addChild(bot); } function buildDefenseTower(x, y) { var towerCost = 25 + Math.floor(wave / 2) * 5; // Increase cost every 2 waves var resourceCost = 50 + Math.floor(wave / 3) * 10; // Increase resource cost every 3 waves if (resources >= resourceCost && money >= towerCost) { var tower = new DefenseTower(); tower.x = x; tower.y = y; defenseTowers.push(tower); game.addChild(tower); resources -= resourceCost; money -= towerCost; resourceText.setText('Resources: ' + resources); moneyText.setText('Money: $' + money); } } function buildBigDefenseTower(x, y) { var towerCost = 100 + Math.floor(wave / 2) * 15; // Increase cost every 2 waves var resourceCost = 100 + Math.floor(wave / 3) * 20; // Increase resource cost every 3 waves if (resources >= resourceCost && money >= towerCost) { var tower = new BigDefenseTower(); tower.x = x; tower.y = y; bigDefenseTowers.push(tower); game.addChild(tower); resources -= resourceCost; money -= towerCost; resourceText.setText('Resources: ' + resources); moneyText.setText('Money: $' + money); } } function buildRapidFireTower(x, y) { var towerCost = 40; // Money cost for rapid fire tower if (resources >= 75 && money >= towerCost) { var tower = new RapidFireTower(); tower.x = x; tower.y = y; rapidFireTowers.push(tower); game.addChild(tower); resources -= 75; money -= towerCost; resourceText.setText('Resources: ' + resources); moneyText.setText('Money: $' + money); } } function buildSniperTower(x, y) { var towerCost = 75; // Money cost for sniper tower if (resources >= 125 && money >= towerCost) { var tower = new SniperTower(); tower.x = x; tower.y = y; sniperTowers.push(tower); game.addChild(tower); resources -= 125; money -= towerCost; resourceText.setText('Resources: ' + resources); moneyText.setText('Money: $' + money); } } function spawnBoss() { var boss = new Boss(); // Spawn boss from top center boss.x = 1024; boss.y = 0; bosses.push(boss); game.addChild(boss); } // Event handlers game.down = function (x, y, obj) { if (placingTower) { // Tower placement mode var canBuild = true; var minDistance = 120; // Check distance from other towers for (var i = 0; i < defenseTowers.length; i++) { var tower = defenseTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2)); if (distance < minDistance) { canBuild = false; break; } } // Check distance from big towers for (var i = 0; i < bigDefenseTowers.length; i++) { var tower = bigDefenseTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2)); if (distance < minDistance) { canBuild = false; break; } } // Check distance from base var distanceToBase = Math.sqrt(Math.pow(playerBase.x - x, 2) + Math.pow(playerBase.y - y, 2)); if (distanceToBase < 150) { canBuild = false; } // Check distance from hero var distanceToHero = Math.sqrt(Math.pow(hero.x - x, 2) + Math.pow(hero.y - y, 2)); if (distanceToHero < 100) { canBuild = false; } if (canBuild && y < 2300) { // Don't build too close to base area buildDefenseTower(x, y); placingTower = false; // Exit placement mode after building } } else if (placingBigTower) { // Big tower placement mode var canBuild = true; var minDistance = 150; // Check distance from regular towers for (var i = 0; i < defenseTowers.length; i++) { var tower = defenseTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2)); if (distance < minDistance) { canBuild = false; break; } } // Check distance from other big towers for (var i = 0; i < bigDefenseTowers.length; i++) { var tower = bigDefenseTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2)); if (distance < minDistance) { canBuild = false; break; } } // Check distance from base var distanceToBase = Math.sqrt(Math.pow(playerBase.x - x, 2) + Math.pow(playerBase.y - y, 2)); if (distanceToBase < 200) { canBuild = false; } // Check distance from hero var distanceToHero = Math.sqrt(Math.pow(hero.x - x, 2) + Math.pow(hero.y - y, 2)); if (distanceToHero < 120) { canBuild = false; } if (canBuild && y < 2300) { // Don't build too close to base area buildBigDefenseTower(x, y); placingBigTower = false; // Exit placement mode after building } } else if (placingRapidFireTower) { // Rapid fire tower placement mode var canBuild = true; var minDistance = 100; // Check distance from all towers for (var i = 0; i < defenseTowers.length; i++) { var tower = defenseTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2)); if (distance < minDistance) { canBuild = false; break; } } for (var i = 0; i < bigDefenseTowers.length; i++) { var tower = bigDefenseTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2)); if (distance < minDistance) { canBuild = false; break; } } for (var i = 0; i < rapidFireTowers.length; i++) { var tower = rapidFireTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2)); if (distance < minDistance) { canBuild = false; break; } } for (var i = 0; i < sniperTowers.length; i++) { var tower = sniperTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2)); if (distance < minDistance) { canBuild = false; break; } } var distanceToBase = Math.sqrt(Math.pow(playerBase.x - x, 2) + Math.pow(playerBase.y - y, 2)); if (distanceToBase < 120) { canBuild = false; } var distanceToHero = Math.sqrt(Math.pow(hero.x - x, 2) + Math.pow(hero.y - y, 2)); if (distanceToHero < 80) { canBuild = false; } if (canBuild && y < 2300) { buildRapidFireTower(x, y); placingRapidFireTower = false; } } else if (placingSniperTower) { // Sniper tower placement mode var canBuild = true; var minDistance = 140; // Check distance from all towers for (var i = 0; i < defenseTowers.length; i++) { var tower = defenseTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2)); if (distance < minDistance) { canBuild = false; break; } } for (var i = 0; i < bigDefenseTowers.length; i++) { var tower = bigDefenseTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2)); if (distance < minDistance) { canBuild = false; break; } } for (var i = 0; i < rapidFireTowers.length; i++) { var tower = rapidFireTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2)); if (distance < minDistance) { canBuild = false; break; } } for (var i = 0; i < sniperTowers.length; i++) { var tower = sniperTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2)); if (distance < minDistance) { canBuild = false; break; } } var distanceToBase = Math.sqrt(Math.pow(playerBase.x - x, 2) + Math.pow(playerBase.y - y, 2)); if (distanceToBase < 180) { canBuild = false; } var distanceToHero = Math.sqrt(Math.pow(hero.x - x, 2) + Math.pow(hero.y - y, 2)); if (distanceToHero < 100) { canBuild = false; } if (canBuild && y < 2300) { buildSniperTower(x, y); placingSniperTower = false; } } else { // Normal mode - move hero (only if alive) if (hero.health > 0) { hero.moveTo(x, y); } } }; // Start background music LK.playMusic('Zombiewarmuic'); // Main game loop game.update = function () { if (gameOver) return; // Resource generation if (LK.ticks % 180 === 0) { // Every 3 seconds resources += 10; resourceText.setText('Resources: ' + resources); } // Zombie spawning - faster spawn rate at higher waves (only if wave is in progress) if (waveInProgress && zombiesSpawned < zombiesPerWave) { spawnTimer++; var zombieSpawnInterval = Math.max(60, 120 - (wave - 1) * 8); // Faster spawning each wave, minimum 1 second if (spawnTimer >= zombieSpawnInterval) { spawnZombie(); spawnTimer = 0; } } // Enemy bot spawning - faster spawn rate at higher waves (only if wave is in progress) if (waveInProgress) { botSpawnTimer++; var botSpawnInterval = Math.max(300, 600 - (wave - 1) * 30); // Faster spawning each wave, minimum 5 seconds if (botSpawnTimer >= botSpawnInterval) { spawnEnemyBot(); // Spawn additional bots at higher waves if (wave >= 5 && Math.random() < 0.3) { spawnEnemyBot(); // 30% chance for second bot } if (wave >= 8 && Math.random() < 0.2) { spawnEnemyBot(); // 20% chance for third bot } botSpawnTimer = 0; } } // Check for wave completion if (waveInProgress && zombiesSpawned >= zombiesPerWave && zombies.length === 0) { // Wave completed - check if it's time for boss if (wave === 9) { // After wave 9, spawn boss spawnBoss(); waveText.setText('BOSS WAVE!'); } else { // Regular wave completed - show start button for next wave waveInProgress = false; startWaveButton.visible = true; } } // Win condition - boss must be defeated if (wave >= 9 && zombies.length === 0 && enemyBots.length === 0 && bosses.length === 0) { // Save high score before showing win screen var currentScore = LK.getScore(); var highScores = storage.highScores || []; highScores.push(currentScore); highScores.sort(function (a, b) { return b - a; }); storage.highScores = highScores.slice(0, 10); // Keep top 10 LK.showYouWin(); } // Update all bullets for (var i = towerBullets.length - 1; i >= 0; i--) { var bullet = towerBullets[i]; if (bullet.destroyed) { towerBullets.splice(i, 1); } } for (var i = bigTowerBullets.length - 1; i >= 0; i--) { var bullet = bigTowerBullets[i]; if (bullet.destroyed) { bigTowerBullets.splice(i, 1); } } for (var i = rapidFireBullets.length - 1; i >= 0; i--) { var bullet = rapidFireBullets[i]; if (bullet.destroyed) { rapidFireBullets.splice(i, 1); } } for (var i = sniperBullets.length - 1; i >= 0; i--) { var bullet = sniperBullets[i]; if (bullet.destroyed) { sniperBullets.splice(i, 1); } } for (var i = heroBullets.length - 1; i >= 0; i--) { var bullet = heroBullets[i]; if (bullet.destroyed) { heroBullets.splice(i, 1); } } for (var i = enemyBullets.length - 1; i >= 0; i--) { var bullet = enemyBullets[i]; if (bullet.destroyed) { enemyBullets.splice(i, 1); } } // Update money icons for (var i = moneyIcons.length - 1; i >= 0; i--) { var icon = moneyIcons[i]; if (icon.destroyed) { moneyIcons.splice(i, 1); } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var BigDefenseTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('defenseTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
tint: 0xFF4444
});
self.health = 500;
self.maxHealth = 500;
self.damage = 80;
self.range = 450;
self.shootCooldown = 0;
self.update = function () {
var target = self.findNearestEnemy();
if (target && self.shootCooldown <= 0) {
self.shootAt(target);
self.shootCooldown = 35; // Slightly faster than regular tower
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.findNearestEnemy = function () {
var nearest = null;
var minDistance = Infinity;
// Check zombies
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var distance = Math.sqrt(Math.pow(zombie.x - self.x, 2) + Math.pow(zombie.y - self.y, 2));
if (distance < minDistance && distance < self.range) {
minDistance = distance;
nearest = zombie;
}
}
// Check enemy bots
for (var i = 0; i < enemyBots.length; i++) {
var bot = enemyBots[i];
var distance = Math.sqrt(Math.pow(bot.x - self.x, 2) + Math.pow(bot.y - self.y, 2));
if (distance < minDistance && distance < self.range) {
minDistance = distance;
nearest = bot;
}
}
return nearest;
};
self.shootAt = function (target) {
var bullet = new BigTowerBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.targetX = target.x;
bullet.targetY = target.y;
bullet.damage = self.damage;
bigTowerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
};
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
self.destroy();
var index = bigDefenseTowers.indexOf(self);
if (index > -1) {
bigDefenseTowers.splice(index, 1);
}
};
return self;
});
var BigTowerBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
tint: 0xFF4444
});
self.speed = 10;
self.damage = 80;
self.targetX = 0;
self.targetY = 0;
self.update = function () {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > self.speed) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
self.explode();
}
// Remove if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.destroy();
var index = bigTowerBullets.indexOf(self);
if (index > -1) {
bigTowerBullets.splice(index, 1);
}
}
};
self.explode = function () {
// Check for zombie hits
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
if (self.intersects(zombie)) {
zombie.takeDamage(self.damage);
break;
}
}
// Check for enemy bot hits
for (var i = 0; i < enemyBots.length; i++) {
var bot = enemyBots[i];
if (self.intersects(bot)) {
bot.takeDamage(self.damage);
break;
}
}
self.destroy();
var index = bigTowerBullets.indexOf(self);
if (index > -1) {
bigTowerBullets.splice(index, 1);
}
};
return self;
});
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
self.health = 4000;
self.maxHealth = 4000;
self.speed = 0.8; // Slower than regular zombies
self.damage = 100; // High damage
self.target = null;
self.attackCooldown = 0;
self.specialAttackCooldown = 0;
self.update = function () {
if (!self.target) {
self.target = playerBase;
}
// Move towards 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 > 80) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
// Attack target
if (self.attackCooldown <= 0) {
if (self.target === playerBase) {
playerBase.takeDamage(self.damage);
}
self.attackCooldown = 90; // Slower attack rate
}
}
// Special AOE attack every 5 seconds
if (self.specialAttackCooldown <= 0) {
self.aoeAttack();
self.specialAttackCooldown = 300; // 5 seconds
}
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
if (self.specialAttackCooldown > 0) {
self.specialAttackCooldown--;
}
};
self.aoeAttack = function () {
// AOE damage to all towers and hero in range
var aoeRange = 400;
LK.effects.flashObject(self, 0xFFFF00, 500);
// Damage hero
var distanceToHero = Math.sqrt(Math.pow(hero.x - self.x, 2) + Math.pow(hero.y - self.y, 2));
if (distanceToHero < aoeRange) {
hero.takeDamage(50);
}
// Damage all towers in range
for (var i = 0; i < defenseTowers.length; i++) {
var tower = defenseTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < aoeRange) {
tower.takeDamage(80);
}
}
for (var i = 0; i < bigDefenseTowers.length; i++) {
var tower = bigDefenseTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < aoeRange) {
tower.takeDamage(80);
}
}
for (var i = 0; i < rapidFireTowers.length; i++) {
var tower = rapidFireTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < aoeRange) {
tower.takeDamage(80);
}
}
for (var i = 0; i < sniperTowers.length; i++) {
var tower = sniperTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < aoeRange) {
tower.takeDamage(80);
}
}
LK.getSound('explosion').play();
};
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
LK.getSound('explosion').play();
LK.setScore(LK.getScore() + 500);
scoreText.setText('Score: ' + LK.getScore());
// Boss gives lots of money
var moneyEarned = 200;
money += moneyEarned;
moneyText.setText('Money: $' + money);
var moneyIcon = new MoneyIcon(moneyEarned);
moneyIcon.x = self.x;
moneyIcon.y = self.y;
moneyIcons.push(moneyIcon);
game.addChild(moneyIcon);
self.destroy();
var index = bosses.indexOf(self);
if (index > -1) {
bosses.splice(index, 1);
}
};
return self;
});
var BuyBigTowerButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('buyButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFF4444
});
var towerIcon = self.attachAsset('defenseTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6,
tint: 0xFF4444,
y: -15
});
var buttonText = new Text2('Big Tower', {
size: 28,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
buttonText.y = 15;
self.addChild(buttonText);
var costText = new Text2('R:100 $100', {
size: 22,
fill: 0xFFFFFF
});
costText.anchor.set(0.5, 0.5);
costText.y = 35;
self.addChild(costText);
self.down = function (x, y, obj) {
var towerCost = 100;
if (resources >= 100 && money >= towerCost) {
// Enable big tower placement mode
placingBigTower = true;
LK.effects.flashObject(self, 0x00FF00, 300);
} else {
// Flash red if can't afford
LK.effects.flashObject(self, 0xFF0000, 300);
}
};
self.update = function () {
var towerCost = 100 + Math.floor(wave / 2) * 15;
var resourceCost = 100 + Math.floor(wave / 3) * 20;
if (resources >= resourceCost && money >= towerCost) {
buttonGraphics.tint = 0xFF2222; // Red when affordable
buttonText.setText('Big Tower');
costText.setText('R:' + resourceCost + ' $' + towerCost);
} else {
buttonGraphics.tint = 0x808080; // Gray when not affordable
buttonText.setText('Big Tower');
costText.setText('R:' + resourceCost + ' $' + towerCost);
}
};
return self;
});
var BuyRapidFireTowerButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('buyButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0,
tint: 0x00FF00
});
var towerIcon = self.attachAsset('defenseTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3,
tint: 0x00FF00,
y: -15
});
var buttonText = new Text2('Rapid', {
size: 28,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
buttonText.y = 15;
self.addChild(buttonText);
var costText = new Text2('R:75 $40', {
size: 22,
fill: 0xFFFFFF
});
costText.anchor.set(0.5, 0.5);
costText.y = 35;
self.addChild(costText);
self.down = function (x, y, obj) {
var towerCost = 40;
if (resources >= 75 && money >= towerCost) {
// Enable rapid fire tower placement mode
placingRapidFireTower = true;
LK.effects.flashObject(self, 0x00FF00, 300);
} else {
// Flash red if can't afford
LK.effects.flashObject(self, 0xFF0000, 300);
}
};
self.update = function () {
var towerCost = 40;
if (resources >= 75 && money >= towerCost) {
buttonGraphics.tint = 0x00AA00; // Green when affordable
buttonText.setText('Rapid');
costText.setText('R:75 $40');
} else {
buttonGraphics.tint = 0x808080; // Gray when not affordable
buttonText.setText('Rapid');
costText.setText('R:75 $40');
}
};
return self;
});
var BuySniperTowerButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('buyButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0,
tint: 0x0088FF
});
var towerIcon = self.attachAsset('defenseTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
tint: 0x0088FF,
y: -15
});
var buttonText = new Text2('Sniper', {
size: 26,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
buttonText.y = 15;
self.addChild(buttonText);
var costText = new Text2('R:125 $75', {
size: 20,
fill: 0xFFFFFF
});
costText.anchor.set(0.5, 0.5);
costText.y = 35;
self.addChild(costText);
self.down = function (x, y, obj) {
var towerCost = 75;
if (resources >= 125 && money >= towerCost) {
// Enable sniper tower placement mode
placingSniperTower = true;
LK.effects.flashObject(self, 0x00FF00, 300);
} else {
// Flash red if can't afford
LK.effects.flashObject(self, 0xFF0000, 300);
}
};
self.update = function () {
var towerCost = 75;
if (resources >= 125 && money >= towerCost) {
buttonGraphics.tint = 0x0066CC; // Blue when affordable
buttonText.setText('Sniper');
costText.setText('R:125 $75');
} else {
buttonGraphics.tint = 0x808080; // Gray when not affordable
buttonText.setText('Sniper');
costText.setText('R:125 $75');
}
};
return self;
});
var BuyTowerButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('buyButton', {
anchorX: 0.5,
anchorY: 0.5
});
var towerIcon = self.attachAsset('defenseTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4,
y: -15
});
var buttonText = new Text2('Tower', {
size: 32,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
buttonText.y = 15;
self.addChild(buttonText);
var costText = new Text2('R:50 $25', {
size: 24,
fill: 0xFFFFFF
});
costText.anchor.set(0.5, 0.5);
costText.y = 35;
self.addChild(costText);
self.down = function (x, y, obj) {
var towerCost = 25;
if (resources >= 50 && money >= towerCost) {
// Enable tower placement mode
placingTower = true;
LK.effects.flashObject(self, 0x00FF00, 300);
} else {
// Flash red if can't afford
LK.effects.flashObject(self, 0xFF0000, 300);
}
};
self.update = function () {
var towerCost = 25 + Math.floor(wave / 2) * 5;
var resourceCost = 50 + Math.floor(wave / 3) * 10;
if (resources >= resourceCost && money >= towerCost) {
buttonGraphics.tint = 0x4CAF50; // Green when affordable
buttonText.setText('Tower');
costText.setText('R:' + resourceCost + ' $' + towerCost);
} else {
buttonGraphics.tint = 0x808080; // Gray when not affordable
buttonText.setText('Tower');
costText.setText('R:' + resourceCost + ' $' + towerCost);
}
};
return self;
});
var DefenseTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('defenseTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 200;
self.maxHealth = 200;
self.damage = 30;
self.range = 300;
self.shootCooldown = 0;
self.update = function () {
var target = self.findNearestEnemy();
if (target && self.shootCooldown <= 0) {
self.shootAt(target);
self.shootCooldown = 45; // 0.75 second cooldown
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.findNearestEnemy = function () {
var nearest = null;
var minDistance = Infinity;
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var distance = Math.sqrt(Math.pow(zombie.x - self.x, 2) + Math.pow(zombie.y - self.y, 2));
if (distance < minDistance && distance < self.range) {
minDistance = distance;
nearest = zombie;
}
}
// Check bosses
for (var i = 0; i < bosses.length; i++) {
var boss = bosses[i];
var distance = Math.sqrt(Math.pow(boss.x - self.x, 2) + Math.pow(boss.y - self.y, 2));
if (distance < minDistance && distance < self.range) {
minDistance = distance;
nearest = boss;
}
}
return nearest;
};
self.shootAt = function (target) {
var bullet = new TowerBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.targetX = target.x;
bullet.targetY = target.y;
bullet.damage = self.damage;
towerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
};
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
self.destroy();
var index = defenseTowers.indexOf(self);
if (index > -1) {
defenseTowers.splice(index, 1);
}
};
return self;
});
var EnemyBot = Container.expand(function (botType) {
var self = Container.call(this);
var botGraphics = self.attachAsset(botType, {
anchorX: 0.5,
anchorY: 0.5
});
self.botType = botType;
// Scale bot stats based on current wave
var healthMultiplier = 1 + (wave - 1) * 0.25; // 25% more health per wave
var damageMultiplier = 1 + (wave - 1) * 0.15; // 15% more damage per wave
self.health = Math.floor(150 * healthMultiplier);
self.maxHealth = Math.floor(150 * healthMultiplier);
self.damage = Math.floor(25 * damageMultiplier);
self.range = 350;
self.shootCooldown = 0;
// Set specific properties based on bot type
if (botType === 'artilleryBot') {
self.damage = 60;
self.range = 400;
self.shootCooldown = 90; // Slower but more powerful
} else if (botType === 'assaultBot') {
self.damage = 20;
self.range = 250;
self.shootCooldown = 20; // Faster firing
} else if (botType === 'disruptionBot') {
self.damage = 15;
self.range = 300;
self.shootCooldown = 120; // Special ability cooldown
} else if (botType === 'repairBot') {
self.damage = 0;
self.range = 200;
self.shootCooldown = 180; // Repair cooldown
}
self.update = function () {
var target = self.findTarget();
if (target) {
// Move towards target if not in range
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > self.range) {
// Move towards target
var speed = 1.5; // Bot movement speed
self.x += dx / distance * speed;
self.y += dy / distance * speed;
}
} else {
// No target in range, move towards player base
var dx = playerBase.x - self.x;
var dy = playerBase.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 80) {
// Don't get too close to base
var speed = 1.2; // Slightly slower when moving to base
self.x += dx / distance * speed;
self.y += dy / distance * speed;
}
}
if (target && self.shootCooldown <= 0) {
if (self.botType === 'repairBot') {
self.repairAllies();
} else {
self.shootAt(target);
}
self.shootCooldown = self.getBaseCooldown();
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.findTarget = function () {
var targets = [];
// Add player base as target
var distanceToBase = Math.sqrt(Math.pow(playerBase.x - self.x, 2) + Math.pow(playerBase.y - self.y, 2));
if (distanceToBase < self.range) {
targets.push({
target: playerBase,
distance: distanceToBase
});
}
// Add hero as target
var distanceToHero = Math.sqrt(Math.pow(hero.x - self.x, 2) + Math.pow(hero.y - self.y, 2));
if (distanceToHero < self.range) {
targets.push({
target: hero,
distance: distanceToHero
});
}
// Add defense towers as targets
for (var i = 0; i < defenseTowers.length; i++) {
var tower = defenseTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < self.range) {
targets.push({
target: tower,
distance: distance
});
}
}
// Add big defense towers as targets
for (var i = 0; i < bigDefenseTowers.length; i++) {
var tower = bigDefenseTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < self.range) {
targets.push({
target: tower,
distance: distance
});
}
}
// Add rapid fire towers as targets
for (var i = 0; i < rapidFireTowers.length; i++) {
var tower = rapidFireTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < self.range) {
targets.push({
target: tower,
distance: distance
});
}
}
// Add sniper towers as targets
for (var i = 0; i < sniperTowers.length; i++) {
var tower = sniperTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < self.range) {
targets.push({
target: tower,
distance: distance
});
}
}
// Return nearest target
if (targets.length > 0) {
targets.sort(function (a, b) {
return a.distance - b.distance;
});
return targets[0].target;
}
return null;
};
self.shootAt = function (target) {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.targetX = target.x;
bullet.targetY = target.y;
bullet.damage = self.damage;
bullet.isAOE = self.botType === 'artilleryBot';
enemyBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
};
self.repairAllies = function () {
for (var i = 0; i < enemyBots.length; i++) {
var bot = enemyBots[i];
if (bot !== self) {
var distance = Math.sqrt(Math.pow(bot.x - self.x, 2) + Math.pow(bot.y - self.y, 2));
if (distance < self.range) {
bot.health = Math.min(bot.health + 30, bot.maxHealth);
LK.effects.flashObject(bot, 0x00ff00, 300);
}
}
}
};
self.getBaseCooldown = function () {
if (self.botType === 'artilleryBot') return 90;
if (self.botType === 'assaultBot') return 20;
if (self.botType === 'disruptionBot') return 120;
if (self.botType === 'repairBot') return 180;
return 60;
};
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
LK.getSound('explosion').play();
LK.setScore(LK.getScore() + 25);
scoreText.setText('Score: ' + LK.getScore());
// Add money and spawn money icon
var moneyEarned = 15;
money += moneyEarned;
moneyText.setText('Money: $' + money);
var moneyIcon = new MoneyIcon(moneyEarned);
moneyIcon.x = self.x;
moneyIcon.y = self.y;
moneyIcons.push(moneyIcon);
game.addChild(moneyIcon);
self.destroy();
var index = enemyBots.indexOf(self);
if (index > -1) {
enemyBots.splice(index, 1);
}
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.damage = 25;
self.targetX = 0;
self.targetY = 0;
self.isAOE = false;
self.update = function () {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > self.speed) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
self.explode();
}
// Remove if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.destroy();
var index = enemyBullets.indexOf(self);
if (index > -1) {
enemyBullets.splice(index, 1);
}
}
};
self.explode = function () {
if (self.isAOE) {
// AOE damage
var explosion = LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
alpha: 0.7
});
game.addChild(explosion);
tween(explosion, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 500,
onFinish: function onFinish() {
explosion.destroy();
}
});
// Damage everything in range
var explosionRange = 150;
if (self.intersects(playerBase)) {
playerBase.takeDamage(self.damage);
}
if (self.intersects(hero)) {
hero.takeDamage(self.damage);
}
for (var i = 0; i < defenseTowers.length; i++) {
var tower = defenseTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < explosionRange) {
tower.takeDamage(self.damage);
}
}
for (var i = 0; i < bigDefenseTowers.length; i++) {
var tower = bigDefenseTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < explosionRange) {
tower.takeDamage(self.damage);
}
}
for (var i = 0; i < rapidFireTowers.length; i++) {
var tower = rapidFireTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < explosionRange) {
tower.takeDamage(self.damage);
}
}
for (var i = 0; i < sniperTowers.length; i++) {
var tower = sniperTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < explosionRange) {
tower.takeDamage(self.damage);
}
}
LK.getSound('explosion').play();
} else {
// Single target damage
if (self.intersects(playerBase)) {
playerBase.takeDamage(self.damage);
} else if (self.intersects(hero)) {
hero.takeDamage(self.damage);
} else {
for (var i = 0; i < defenseTowers.length; i++) {
var tower = defenseTowers[i];
if (self.intersects(tower)) {
tower.takeDamage(self.damage);
break;
}
}
for (var i = 0; i < bigDefenseTowers.length; i++) {
var tower = bigDefenseTowers[i];
if (self.intersects(tower)) {
tower.takeDamage(self.damage);
break;
}
}
for (var i = 0; i < rapidFireTowers.length; i++) {
var tower = rapidFireTowers[i];
if (self.intersects(tower)) {
tower.takeDamage(self.damage);
break;
}
}
for (var i = 0; i < sniperTowers.length; i++) {
var tower = sniperTowers[i];
if (self.intersects(tower)) {
tower.takeDamage(self.damage);
break;
}
}
}
}
self.destroy();
var index = enemyBullets.indexOf(self);
if (index > -1) {
enemyBullets.splice(index, 1);
}
};
return self;
});
var Hero = Container.expand(function () {
var self = Container.call(this);
var heroGraphics = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 300;
self.maxHealth = 300;
self.speed = 3;
self.damage = 40;
self.shootCooldown = 0;
self.targetX = self.x;
self.targetY = self.y;
// Special abilities for boss waves
self.specialAttackCooldown = 0;
self.shieldCooldown = 0;
self.isShielded = false;
self.shieldDuration = 0;
self.update = function () {
// Only move and attack if alive
if (self.health > 0) {
// Move towards target position
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Auto-attack nearest enemy
var nearestEnemy = self.findNearestEnemy();
if (nearestEnemy && self.shootCooldown <= 0) {
self.shootAt(nearestEnemy);
self.shootCooldown = 30; // 0.5 second cooldown
}
// Special abilities during boss waves (wave 9+)
if (wave >= 9) {
// Special attack - shoots multiple bullets in a spread
if (self.specialAttackCooldown <= 0) {
self.specialAttack();
self.specialAttackCooldown = 300; // 5 seconds
}
// Shield ability - temporary damage immunity
if (self.shieldCooldown <= 0 && !self.isShielded) {
self.activateShield();
self.shieldCooldown = 600; // 10 seconds
}
} else {
// Reset special abilities when not in boss waves
self.specialAttackCooldown = 0;
self.shieldCooldown = 0;
self.isShielded = false;
self.shieldDuration = 0;
self.alpha = 1.0; // Ensure normal opacity
}
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
// Update special ability cooldowns
if (self.specialAttackCooldown > 0) {
self.specialAttackCooldown--;
}
if (self.shieldCooldown > 0) {
self.shieldCooldown--;
}
if (self.isShielded) {
self.shieldDuration--;
if (self.shieldDuration <= 0) {
self.isShielded = false;
self.alpha = 1.0; // Return to normal opacity
}
}
// Update health display
var healthStatus = self.isShielded ? ' (SHIELDED)' : '';
heroHealthText.setText('Hero Health: ' + Math.max(0, self.health) + '/' + self.maxHealth + healthStatus);
};
self.findNearestEnemy = function () {
var nearest = null;
var minDistance = Infinity;
// Check zombies
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var distance = Math.sqrt(Math.pow(zombie.x - self.x, 2) + Math.pow(zombie.y - self.y, 2));
if (distance < minDistance && distance < 400) {
minDistance = distance;
nearest = zombie;
}
}
// Check enemy bots
for (var i = 0; i < enemyBots.length; i++) {
var bot = enemyBots[i];
var distance = Math.sqrt(Math.pow(bot.x - self.x, 2) + Math.pow(bot.y - self.y, 2));
if (distance < minDistance && distance < 400) {
minDistance = distance;
nearest = bot;
}
}
// Check bosses
for (var i = 0; i < bosses.length; i++) {
var boss = bosses[i];
var distance = Math.sqrt(Math.pow(boss.x - self.x, 2) + Math.pow(boss.y - self.y, 2));
if (distance < minDistance && distance < 400) {
minDistance = distance;
nearest = boss;
}
}
return nearest;
};
self.shootAt = function (target) {
var bullet = new HeroBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.targetX = target.x;
bullet.targetY = target.y;
bullet.damage = self.damage;
heroBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
};
self.specialAttack = function () {
// Fire 5 bullets in a spread pattern
var nearestEnemy = self.findNearestEnemy();
if (nearestEnemy) {
for (var i = 0; i < 5; i++) {
var bullet = new HeroBullet();
bullet.x = self.x;
bullet.y = self.y;
// Calculate spread angle
var angleOffset = (i - 2) * 0.3; // Spread bullets in a fan
var dx = nearestEnemy.x - self.x;
var dy = nearestEnemy.y - self.y;
var angle = Math.atan2(dy, dx) + angleOffset;
var distance = 400; // Target distance
bullet.targetX = self.x + Math.cos(angle) * distance;
bullet.targetY = self.y + Math.sin(angle) * distance;
bullet.damage = self.damage * 1.5; // Increased damage for special attack
heroBullets.push(bullet);
game.addChild(bullet);
}
LK.getSound('shoot').play();
LK.effects.flashObject(self, 0xFFFF00, 500); // Yellow flash for special attack
}
};
self.activateShield = function () {
self.isShielded = true;
self.shieldDuration = 180; // 3 seconds of shield
self.alpha = 0.7; // Make hero semi-transparent to show shield
LK.effects.flashObject(self, 0x00FFFF, 300); // Cyan flash for shield activation
};
self.moveTo = function (x, y) {
if (self.health > 0) {
self.targetX = x;
self.targetY = y;
}
};
self.takeDamage = function (amount) {
// Shield blocks damage during boss waves
if (self.isShielded) {
LK.effects.flashObject(self, 0x00FFFF, 200); // Cyan flash when shielded
return; // No damage taken while shielded
}
self.health -= amount;
// Prevent hero health from going below 1 - hero cannot die
if (self.health < 1) {
self.health = 1;
}
LK.effects.flashObject(self, 0xff0000, 300);
// Remove death condition - hero can no longer die
};
self.die = function () {
// Hero becomes translucent and stops moving when dead
self.alpha = 0.3;
self.targetX = self.x;
self.targetY = self.y;
heroHealthText.setText('Hero Health: 0/' + self.maxHealth + ' (DEAD)');
// Attack player base when hero dies
playerBase.takeDamage(self.damage * 2); // Double damage when hero dies
};
return self;
});
var HeroBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('heroBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 10;
self.damage = 40;
self.targetX = 0;
self.targetY = 0;
self.update = function () {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > self.speed) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
self.explode();
}
// Remove if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.destroy();
var index = heroBullets.indexOf(self);
if (index > -1) {
heroBullets.splice(index, 1);
}
}
};
self.explode = function () {
// Check for zombie hits
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
if (self.intersects(zombie)) {
zombie.takeDamage(self.damage);
self.destroy();
var index = heroBullets.indexOf(self);
if (index > -1) {
heroBullets.splice(index, 1);
}
return;
}
}
// Check for enemy bot hits
for (var i = 0; i < enemyBots.length; i++) {
var bot = enemyBots[i];
if (self.intersects(bot)) {
bot.takeDamage(self.damage);
self.destroy();
var index = heroBullets.indexOf(self);
if (index > -1) {
heroBullets.splice(index, 1);
}
return;
}
}
// Check for boss hits
for (var i = 0; i < bosses.length; i++) {
var boss = bosses[i];
if (self.intersects(boss)) {
boss.takeDamage(self.damage);
self.destroy();
var index = heroBullets.indexOf(self);
if (index > -1) {
heroBullets.splice(index, 1);
}
return;
}
}
self.destroy();
var index = heroBullets.indexOf(self);
if (index > -1) {
heroBullets.splice(index, 1);
}
};
return self;
});
var LeaderboardScreen = Container.expand(function () {
var self = Container.call(this);
var background = LK.getAsset('buyButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 10,
scaleY: 8,
tint: 0x000000,
alpha: 0.9
});
self.addChild(background);
var titleText = new Text2('LEADERBOARD', {
size: 80,
fill: 0xFFD700
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -300;
self.addChild(titleText);
var currentScoreText = new Text2('Your Score: 0', {
size: 50,
fill: 0x00FF00
});
currentScoreText.anchor.set(0.5, 0.5);
currentScoreText.y = -200;
self.addChild(currentScoreText);
var positionText = new Text2('Your Position: #1', {
size: 45,
fill: 0xFFFFFF
});
positionText.anchor.set(0.5, 0.5);
positionText.y = -150;
self.addChild(positionText);
var scoresText = new Text2('', {
size: 35,
fill: 0xFFFFFF
});
scoresText.anchor.set(0.5, 0.5);
scoresText.y = 50;
self.addChild(scoresText);
var closeText = new Text2('Tap to close', {
size: 40,
fill: 0xFFFFFF
});
closeText.anchor.set(0.5, 0.5);
closeText.y = 250;
self.addChild(closeText);
self.visible = false;
self.down = function (x, y, obj) {
self.visible = false;
};
self.updateLeaderboard = function () {
var currentScore = LK.getScore();
currentScoreText.setText('Your Score: ' + currentScore);
// Get stored high scores or initialize empty array
var highScores = storage.highScores || [];
// Add current score to the list
var allScores = highScores.slice();
allScores.push(currentScore);
// Sort scores in descending order
allScores.sort(function (a, b) {
return b - a;
});
// Keep only top 10 scores
allScores = allScores.slice(0, 10);
// Find player's position
var playerPosition = 1;
for (var i = 0; i < allScores.length; i++) {
if (allScores[i] === currentScore) {
playerPosition = i + 1;
break;
}
}
positionText.setText('Your Position: #' + playerPosition);
// Display top scores
var scoresDisplay = 'TOP SCORES:\n';
for (var i = 0; i < Math.min(5, allScores.length); i++) {
var highlight = allScores[i] === currentScore ? ' <- YOU' : '';
scoresDisplay += '#' + (i + 1) + ': ' + allScores[i] + highlight + '\n';
}
scoresText.setText(scoresDisplay);
// Save high scores back to storage (without current score)
storage.highScores = highScores;
};
return self;
});
var MoneyIcon = Container.expand(function (amount) {
var self = Container.call(this);
var moneyGraphics = self.attachAsset('money', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
var moneyText = new Text2('+$' + amount, {
size: 30,
fill: 0xFFD700
});
moneyText.anchor.set(0.5, 0.5);
moneyText.x = 30;
self.addChild(moneyText);
self.lifeTime = 120; // 2 seconds at 60fps
self.floatSpeed = -2;
self.update = function () {
self.y += self.floatSpeed;
self.alpha -= 0.02;
self.lifeTime--;
if (self.lifeTime <= 0 || self.alpha <= 0) {
self.destroy();
var index = moneyIcons.indexOf(self);
if (index > -1) {
moneyIcons.splice(index, 1);
}
}
};
return self;
});
var MoneyScreen = Container.expand(function () {
var self = Container.call(this);
var background = LK.getAsset('buyButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 8,
scaleY: 6,
tint: 0x000000,
alpha: 0.8
});
self.addChild(background);
var titleText = new Text2('Money Status', {
size: 80,
fill: 0xFFD700
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -150;
self.addChild(titleText);
var moneyIcon = self.attachAsset('money', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3,
y: -50
});
var currentMoneyText = new Text2('$' + money, {
size: 120,
fill: 0xFFD700
});
currentMoneyText.anchor.set(0.5, 0.5);
currentMoneyText.y = 50;
self.addChild(currentMoneyText);
var closeText = new Text2('Tap to close', {
size: 40,
fill: 0xFFFFFF
});
closeText.anchor.set(0.5, 0.5);
closeText.y = 150;
self.addChild(closeText);
self.visible = false;
self.down = function (x, y, obj) {
self.visible = false;
};
self.update = function () {
if (self.visible) {
currentMoneyText.setText('$' + money);
}
};
return self;
});
var PlayerBase = Container.expand(function () {
var self = Container.call(this);
var baseGraphics = self.attachAsset('playerBase', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 3000;
self.maxHealth = 3000;
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xff0000, 300);
healthText.setText('Base Health: ' + self.health + '/' + self.maxHealth);
if (self.health <= 0) {
gameOver = true;
// Save high score before showing game over screen
var currentScore = LK.getScore();
var highScores = storage.highScores || [];
highScores.push(currentScore);
highScores.sort(function (a, b) {
return b - a;
});
storage.highScores = highScores.slice(0, 10); // Keep top 10
LK.showGameOver();
}
};
return self;
});
var RapidFireBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6,
tint: 0x00FF00
});
self.speed = 12;
self.damage = 15;
self.targetX = 0;
self.targetY = 0;
self.update = function () {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > self.speed) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
self.explode();
}
// Remove if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.destroy();
var index = rapidFireBullets.indexOf(self);
if (index > -1) {
rapidFireBullets.splice(index, 1);
}
}
};
self.explode = function () {
// Check for zombie hits
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
if (self.intersects(zombie)) {
zombie.takeDamage(self.damage);
break;
}
}
// Check for enemy bot hits
for (var i = 0; i < enemyBots.length; i++) {
var bot = enemyBots[i];
if (self.intersects(bot)) {
bot.takeDamage(self.damage);
break;
}
}
self.destroy();
var index = rapidFireBullets.indexOf(self);
if (index > -1) {
rapidFireBullets.splice(index, 1);
}
};
return self;
});
var RapidFireTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('defenseTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
tint: 0x00FF00
});
self.health = 150;
self.maxHealth = 150;
self.damage = 15;
self.range = 250;
self.shootCooldown = 0;
self.update = function () {
var target = self.findNearestEnemy();
if (target && self.shootCooldown <= 0) {
self.shootAt(target);
self.shootCooldown = 15; // Very fast firing
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.findNearestEnemy = function () {
var nearest = null;
var minDistance = Infinity;
// Check zombies
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var distance = Math.sqrt(Math.pow(zombie.x - self.x, 2) + Math.pow(zombie.y - self.y, 2));
if (distance < minDistance && distance < self.range) {
minDistance = distance;
nearest = zombie;
}
}
// Check enemy bots
for (var i = 0; i < enemyBots.length; i++) {
var bot = enemyBots[i];
var distance = Math.sqrt(Math.pow(bot.x - self.x, 2) + Math.pow(bot.y - self.y, 2));
if (distance < minDistance && distance < self.range) {
minDistance = distance;
nearest = bot;
}
}
return nearest;
};
self.shootAt = function (target) {
var bullet = new RapidFireBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.targetX = target.x;
bullet.targetY = target.y;
bullet.damage = self.damage;
rapidFireBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
};
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
self.destroy();
var index = rapidFireTowers.indexOf(self);
if (index > -1) {
rapidFireTowers.splice(index, 1);
}
};
return self;
});
var ReviveButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('buyButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.0,
tint: 0xFF6666
});
var heroIcon = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
x: -80,
y: -15,
tint: 0xFFFFFF
});
var buttonText = new Text2('Revive Hero', {
size: 50,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
buttonText.y = -10;
self.addChild(buttonText);
var costText = new Text2('Cost: $50', {
size: 35,
fill: 0xFFFFFF
});
costText.anchor.set(0.5, 0.5);
costText.y = 30;
self.addChild(costText);
var moneyIcon = self.attachAsset('money', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
x: -30,
y: 30
});
self.visible = false;
self.down = function (x, y, obj) {
// Revive hero if dead and have enough money
if (hero.health <= 0 && money >= 50) {
// Revive hero
hero.health = hero.maxHealth;
hero.alpha = 1.0; // Make hero fully visible again
money -= 50;
moneyText.setText('Money: $' + money);
// Flash green to show successful revive
LK.effects.flashObject(self, 0x00FF00, 300);
// Hide revive button after use
self.visible = false;
} else {
// Flash red if can't revive
LK.effects.flashObject(self, 0xFF0000, 300);
}
};
self.update = function () {
// Show revive button when hero is dead and has enough money
if (hero.health <= 0 && money >= 50) {
self.visible = true;
} else {
self.visible = false;
}
};
return self;
});
var SniperBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.8,
scaleY: 1.8,
tint: 0x0088FF
});
self.speed = 15;
self.damage = 120;
self.targetX = 0;
self.targetY = 0;
self.update = function () {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > self.speed) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
self.explode();
}
// Remove if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.destroy();
var index = sniperBullets.indexOf(self);
if (index > -1) {
sniperBullets.splice(index, 1);
}
}
};
self.explode = function () {
// Check for zombie hits
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
if (self.intersects(zombie)) {
zombie.takeDamage(self.damage);
break;
}
}
// Check for enemy bot hits
for (var i = 0; i < enemyBots.length; i++) {
var bot = enemyBots[i];
if (self.intersects(bot)) {
bot.takeDamage(self.damage);
break;
}
}
self.destroy();
var index = sniperBullets.indexOf(self);
if (index > -1) {
sniperBullets.splice(index, 1);
}
};
return self;
});
var SniperTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('defenseTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2,
tint: 0x0088FF
});
self.health = 250;
self.maxHealth = 250;
self.damage = 120;
self.range = 600;
self.shootCooldown = 0;
self.update = function () {
var target = self.findNearestEnemy();
if (target && self.shootCooldown <= 0) {
self.shootAt(target);
self.shootCooldown = 80; // Slow but powerful
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.findNearestEnemy = function () {
var nearest = null;
var minDistance = Infinity;
// Check zombies
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var distance = Math.sqrt(Math.pow(zombie.x - self.x, 2) + Math.pow(zombie.y - self.y, 2));
if (distance < minDistance && distance < self.range) {
minDistance = distance;
nearest = zombie;
}
}
// Check enemy bots
for (var i = 0; i < enemyBots.length; i++) {
var bot = enemyBots[i];
var distance = Math.sqrt(Math.pow(bot.x - self.x, 2) + Math.pow(bot.y - self.y, 2));
if (distance < minDistance && distance < self.range) {
minDistance = distance;
nearest = bot;
}
}
return nearest;
};
self.shootAt = function (target) {
var bullet = new SniperBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.targetX = target.x;
bullet.targetY = target.y;
bullet.damage = self.damage;
sniperBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
};
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
self.destroy();
var index = sniperTowers.indexOf(self);
if (index > -1) {
sniperTowers.splice(index, 1);
}
};
return self;
});
var StartWaveButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('startButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 1.5,
tint: 0x00FF00
});
var buttonText = new Text2('Start Wave ' + (wave + 1), {
size: 40,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.visible = false;
self.down = function (x, y, obj) {
if (self.visible) {
// Start next wave
wave++;
zombiesSpawned = 0;
zombiesPerWave += 3 + Math.floor(wave / 3);
waveText.setText('Wave: ' + wave);
resources += Math.max(15, 35 - wave);
resourceText.setText('Resources: ' + resources);
self.visible = false;
waveInProgress = true;
LK.effects.flashObject(self, 0x00FF00, 300);
}
};
self.update = function () {
if (self.visible) {
buttonText.setText('Start Wave ' + wave);
}
};
return self;
});
var TowerBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = 30;
self.targetX = 0;
self.targetY = 0;
self.update = function () {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > self.speed) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
self.explode();
}
// Remove if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.destroy();
var index = towerBullets.indexOf(self);
if (index > -1) {
towerBullets.splice(index, 1);
}
}
};
self.explode = function () {
// Check for zombie hits
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
if (self.intersects(zombie)) {
zombie.takeDamage(self.damage);
break;
}
}
// Check for boss hits
for (var i = 0; i < bosses.length; i++) {
var boss = bosses[i];
if (self.intersects(boss)) {
boss.takeDamage(self.damage);
break;
}
}
self.destroy();
var index = towerBullets.indexOf(self);
if (index > -1) {
towerBullets.splice(index, 1);
}
};
return self;
});
var Zombie = Container.expand(function () {
var self = Container.call(this);
var zombieGraphics = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
// Scale health and damage based on current wave
var healthMultiplier = 1 + (wave - 1) * 0.3; // 30% more health per wave
var damageMultiplier = 1 + (wave - 1) * 0.2; // 20% more damage per wave
var speedMultiplier = 1 + (wave - 1) * 0.1; // 10% faster per wave
self.health = Math.floor(100 * healthMultiplier);
self.maxHealth = Math.floor(100 * healthMultiplier);
self.speed = Math.min(3, 1 * speedMultiplier); // Cap speed at 3
self.damage = Math.floor(20 * damageMultiplier);
self.target = null;
self.attackCooldown = 0;
self.update = function () {
// Find nearest target (hero, towers, or base)
var target = self.findNearestTarget();
if (target) {
self.target = target;
}
if (!self.target) {
self.target = playerBase;
}
// Move towards 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 > 50) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
// Attack target
if (self.attackCooldown <= 0) {
if (self.target === playerBase) {
playerBase.takeDamage(self.damage);
} else if (self.target === hero) {
hero.takeDamage(self.damage);
} else if (self.target.takeDamage) {
// Attack tower
self.target.takeDamage(self.damage);
}
self.attackCooldown = 60; // 1 second cooldown
}
}
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
};
self.findNearestTarget = function () {
var targets = [];
var attackRange = 400; // Range to look for targets
// Add hero as target if in range and alive
if (hero.health > 0) {
var distanceToHero = Math.sqrt(Math.pow(hero.x - self.x, 2) + Math.pow(hero.y - self.y, 2));
if (distanceToHero < attackRange) {
targets.push({
target: hero,
distance: distanceToHero
});
}
}
// Add defense towers as targets
for (var i = 0; i < defenseTowers.length; i++) {
var tower = defenseTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < attackRange) {
targets.push({
target: tower,
distance: distance
});
}
}
// Add big defense towers as targets
for (var i = 0; i < bigDefenseTowers.length; i++) {
var tower = bigDefenseTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < attackRange) {
targets.push({
target: tower,
distance: distance
});
}
}
// Add rapid fire towers as targets
for (var i = 0; i < rapidFireTowers.length; i++) {
var tower = rapidFireTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < attackRange) {
targets.push({
target: tower,
distance: distance
});
}
}
// Add sniper towers as targets
for (var i = 0; i < sniperTowers.length; i++) {
var tower = sniperTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < attackRange) {
targets.push({
target: tower,
distance: distance
});
}
}
// Add player base as target
var distanceToBase = Math.sqrt(Math.pow(playerBase.x - self.x, 2) + Math.pow(playerBase.y - self.y, 2));
if (distanceToBase < attackRange) {
targets.push({
target: playerBase,
distance: distanceToBase
});
}
// Return nearest target
if (targets.length > 0) {
targets.sort(function (a, b) {
return a.distance - b.distance;
});
return targets[0].target;
}
return null;
};
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
LK.getSound('zombieHit').play();
LK.setScore(LK.getScore() + 10);
scoreText.setText('Score: ' + LK.getScore());
// Add money and spawn money icon
var moneyEarned = 5;
money += moneyEarned;
moneyText.setText('Money: $' + money);
var moneyIcon = new MoneyIcon(moneyEarned);
moneyIcon.x = self.x;
moneyIcon.y = self.y;
moneyIcons.push(moneyIcon);
game.addChild(moneyIcon);
self.destroy();
var index = zombies.indexOf(self);
if (index > -1) {
zombies.splice(index, 1);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2a2a2a
});
/****
* Game Code
****/
// Game state variables
var gameOver = false;
var wave = 1;
var zombiesSpawned = 0;
var zombiesPerWave = 5;
var spawnTimer = 0;
var botSpawnTimer = 0;
var resources = 100;
var money = 0;
var placingTower = false;
var placingBigTower = false;
var placingRapidFireTower = false;
var placingSniperTower = false;
var waveInProgress = true;
// Game objects arrays
var zombies = [];
var enemyBots = [];
var bosses = [];
var defenseTowers = [];
var bigDefenseTowers = [];
var rapidFireTowers = [];
var sniperTowers = [];
var towerBullets = [];
var bigTowerBullets = [];
var rapidFireBullets = [];
var sniperBullets = [];
var heroBullets = [];
var enemyBullets = [];
var moneyIcons = [];
// Create player base
var playerBase = new PlayerBase();
playerBase.x = 1024;
playerBase.y = 2400;
game.addChild(playerBase);
// Create hero
var hero = new Hero();
hero.x = 1024;
hero.y = 2200;
game.addChild(hero);
// Create UI elements
var scoreText = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreText);
var healthText = new Text2('Base Health: 3000/3000', {
size: 50,
fill: 0xFFFFFF
});
healthText.anchor.set(0, 0);
healthText.x = -400;
LK.gui.topRight.addChild(healthText);
var waveText = new Text2('Wave: 1', {
size: 55,
fill: 0xFFFF00
});
waveText.anchor.set(0.5, 0);
LK.gui.top.addChild(waveText);
var resourceText = new Text2('Resources: 100', {
size: 45,
fill: 0x00FF00
});
resourceText.anchor.set(0, 0);
LK.gui.bottomLeft.addChild(resourceText);
var moneyText = new Text2('Money: $0', {
size: 45,
fill: 0xFFD700
});
moneyText.anchor.set(0, 0);
moneyText.y = 120;
moneyText.down = function (x, y, obj) {
moneyScreen.visible = true;
};
LK.gui.topLeft.addChild(moneyText);
var heroHealthText = new Text2('Hero Health: 300/300', {
size: 50,
fill: 0xFF6666
});
heroHealthText.anchor.set(0, 0);
heroHealthText.y = 180;
LK.gui.topLeft.addChild(heroHealthText);
// Create buy tower button
var buyTowerButton = new BuyTowerButton();
buyTowerButton.x = 120;
buyTowerButton.y = -120;
LK.gui.bottomLeft.addChild(buyTowerButton);
// Create buy big tower button
var buyBigTowerButton = new BuyBigTowerButton();
buyBigTowerButton.x = 280;
buyBigTowerButton.y = -120;
LK.gui.bottomLeft.addChild(buyBigTowerButton);
// Create buy rapid fire tower button
var buyRapidFireTowerButton = new BuyRapidFireTowerButton();
buyRapidFireTowerButton.x = 440;
buyRapidFireTowerButton.y = -120;
LK.gui.bottomLeft.addChild(buyRapidFireTowerButton);
// Create buy sniper tower button
var buySniperTowerButton = new BuySniperTowerButton();
buySniperTowerButton.x = 600;
buySniperTowerButton.y = -120;
LK.gui.bottomLeft.addChild(buySniperTowerButton);
// Create start wave button
var startWaveButton = new StartWaveButton();
startWaveButton.x = 0;
startWaveButton.y = -200;
LK.gui.center.addChild(startWaveButton);
// Create money screen
var moneyScreen = new MoneyScreen();
moneyScreen.x = 0;
moneyScreen.y = 0;
LK.gui.center.addChild(moneyScreen);
// Create revive button
var reviveButton = new ReviveButton();
reviveButton.x = 0;
reviveButton.y = 100;
LK.gui.center.addChild(reviveButton);
// Create leaderboard screen
var leaderboardScreen = new LeaderboardScreen();
leaderboardScreen.x = 0;
leaderboardScreen.y = 0;
LK.gui.center.addChild(leaderboardScreen);
// Create leaderboard button
var leaderboardButton = new Text2('LEADERBOARD', {
size: 40,
fill: 0xFFD700
});
leaderboardButton.anchor.set(0.5, 0);
leaderboardButton.y = 300;
leaderboardButton.down = function (x, y, obj) {
leaderboardScreen.updateLeaderboard();
leaderboardScreen.visible = true;
};
LK.gui.topLeft.addChild(leaderboardButton);
// Game control functions
function spawnZombie() {
var zombie = new Zombie();
// Spawn from random edge
var edge = Math.floor(Math.random() * 4);
if (edge === 0) {
// Top
zombie.x = Math.random() * 2048;
zombie.y = 0;
} else if (edge === 1) {
// Right
zombie.x = 2048;
zombie.y = Math.random() * 2732;
} else if (edge === 2) {
// Bottom
zombie.x = Math.random() * 2048;
zombie.y = 2732;
} else {
// Left
zombie.x = 0;
zombie.y = Math.random() * 2732;
}
zombies.push(zombie);
game.addChild(zombie);
zombiesSpawned++;
}
function spawnEnemyBot() {
var botTypes = ['artilleryBot', 'assaultBot', 'disruptionBot', 'repairBot'];
var randomType = botTypes[Math.floor(Math.random() * botTypes.length)];
var bot = new EnemyBot(randomType);
bot.x = Math.random() * 1800 + 124;
bot.y = Math.random() * 1000 + 200;
enemyBots.push(bot);
game.addChild(bot);
}
function buildDefenseTower(x, y) {
var towerCost = 25 + Math.floor(wave / 2) * 5; // Increase cost every 2 waves
var resourceCost = 50 + Math.floor(wave / 3) * 10; // Increase resource cost every 3 waves
if (resources >= resourceCost && money >= towerCost) {
var tower = new DefenseTower();
tower.x = x;
tower.y = y;
defenseTowers.push(tower);
game.addChild(tower);
resources -= resourceCost;
money -= towerCost;
resourceText.setText('Resources: ' + resources);
moneyText.setText('Money: $' + money);
}
}
function buildBigDefenseTower(x, y) {
var towerCost = 100 + Math.floor(wave / 2) * 15; // Increase cost every 2 waves
var resourceCost = 100 + Math.floor(wave / 3) * 20; // Increase resource cost every 3 waves
if (resources >= resourceCost && money >= towerCost) {
var tower = new BigDefenseTower();
tower.x = x;
tower.y = y;
bigDefenseTowers.push(tower);
game.addChild(tower);
resources -= resourceCost;
money -= towerCost;
resourceText.setText('Resources: ' + resources);
moneyText.setText('Money: $' + money);
}
}
function buildRapidFireTower(x, y) {
var towerCost = 40; // Money cost for rapid fire tower
if (resources >= 75 && money >= towerCost) {
var tower = new RapidFireTower();
tower.x = x;
tower.y = y;
rapidFireTowers.push(tower);
game.addChild(tower);
resources -= 75;
money -= towerCost;
resourceText.setText('Resources: ' + resources);
moneyText.setText('Money: $' + money);
}
}
function buildSniperTower(x, y) {
var towerCost = 75; // Money cost for sniper tower
if (resources >= 125 && money >= towerCost) {
var tower = new SniperTower();
tower.x = x;
tower.y = y;
sniperTowers.push(tower);
game.addChild(tower);
resources -= 125;
money -= towerCost;
resourceText.setText('Resources: ' + resources);
moneyText.setText('Money: $' + money);
}
}
function spawnBoss() {
var boss = new Boss();
// Spawn boss from top center
boss.x = 1024;
boss.y = 0;
bosses.push(boss);
game.addChild(boss);
}
// Event handlers
game.down = function (x, y, obj) {
if (placingTower) {
// Tower placement mode
var canBuild = true;
var minDistance = 120;
// Check distance from other towers
for (var i = 0; i < defenseTowers.length; i++) {
var tower = defenseTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2));
if (distance < minDistance) {
canBuild = false;
break;
}
}
// Check distance from big towers
for (var i = 0; i < bigDefenseTowers.length; i++) {
var tower = bigDefenseTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2));
if (distance < minDistance) {
canBuild = false;
break;
}
}
// Check distance from base
var distanceToBase = Math.sqrt(Math.pow(playerBase.x - x, 2) + Math.pow(playerBase.y - y, 2));
if (distanceToBase < 150) {
canBuild = false;
}
// Check distance from hero
var distanceToHero = Math.sqrt(Math.pow(hero.x - x, 2) + Math.pow(hero.y - y, 2));
if (distanceToHero < 100) {
canBuild = false;
}
if (canBuild && y < 2300) {
// Don't build too close to base area
buildDefenseTower(x, y);
placingTower = false; // Exit placement mode after building
}
} else if (placingBigTower) {
// Big tower placement mode
var canBuild = true;
var minDistance = 150;
// Check distance from regular towers
for (var i = 0; i < defenseTowers.length; i++) {
var tower = defenseTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2));
if (distance < minDistance) {
canBuild = false;
break;
}
}
// Check distance from other big towers
for (var i = 0; i < bigDefenseTowers.length; i++) {
var tower = bigDefenseTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2));
if (distance < minDistance) {
canBuild = false;
break;
}
}
// Check distance from base
var distanceToBase = Math.sqrt(Math.pow(playerBase.x - x, 2) + Math.pow(playerBase.y - y, 2));
if (distanceToBase < 200) {
canBuild = false;
}
// Check distance from hero
var distanceToHero = Math.sqrt(Math.pow(hero.x - x, 2) + Math.pow(hero.y - y, 2));
if (distanceToHero < 120) {
canBuild = false;
}
if (canBuild && y < 2300) {
// Don't build too close to base area
buildBigDefenseTower(x, y);
placingBigTower = false; // Exit placement mode after building
}
} else if (placingRapidFireTower) {
// Rapid fire tower placement mode
var canBuild = true;
var minDistance = 100;
// Check distance from all towers
for (var i = 0; i < defenseTowers.length; i++) {
var tower = defenseTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2));
if (distance < minDistance) {
canBuild = false;
break;
}
}
for (var i = 0; i < bigDefenseTowers.length; i++) {
var tower = bigDefenseTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2));
if (distance < minDistance) {
canBuild = false;
break;
}
}
for (var i = 0; i < rapidFireTowers.length; i++) {
var tower = rapidFireTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2));
if (distance < minDistance) {
canBuild = false;
break;
}
}
for (var i = 0; i < sniperTowers.length; i++) {
var tower = sniperTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2));
if (distance < minDistance) {
canBuild = false;
break;
}
}
var distanceToBase = Math.sqrt(Math.pow(playerBase.x - x, 2) + Math.pow(playerBase.y - y, 2));
if (distanceToBase < 120) {
canBuild = false;
}
var distanceToHero = Math.sqrt(Math.pow(hero.x - x, 2) + Math.pow(hero.y - y, 2));
if (distanceToHero < 80) {
canBuild = false;
}
if (canBuild && y < 2300) {
buildRapidFireTower(x, y);
placingRapidFireTower = false;
}
} else if (placingSniperTower) {
// Sniper tower placement mode
var canBuild = true;
var minDistance = 140;
// Check distance from all towers
for (var i = 0; i < defenseTowers.length; i++) {
var tower = defenseTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2));
if (distance < minDistance) {
canBuild = false;
break;
}
}
for (var i = 0; i < bigDefenseTowers.length; i++) {
var tower = bigDefenseTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2));
if (distance < minDistance) {
canBuild = false;
break;
}
}
for (var i = 0; i < rapidFireTowers.length; i++) {
var tower = rapidFireTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2));
if (distance < minDistance) {
canBuild = false;
break;
}
}
for (var i = 0; i < sniperTowers.length; i++) {
var tower = sniperTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2));
if (distance < minDistance) {
canBuild = false;
break;
}
}
var distanceToBase = Math.sqrt(Math.pow(playerBase.x - x, 2) + Math.pow(playerBase.y - y, 2));
if (distanceToBase < 180) {
canBuild = false;
}
var distanceToHero = Math.sqrt(Math.pow(hero.x - x, 2) + Math.pow(hero.y - y, 2));
if (distanceToHero < 100) {
canBuild = false;
}
if (canBuild && y < 2300) {
buildSniperTower(x, y);
placingSniperTower = false;
}
} else {
// Normal mode - move hero (only if alive)
if (hero.health > 0) {
hero.moveTo(x, y);
}
}
};
// Start background music
LK.playMusic('Zombiewarmuic');
// Main game loop
game.update = function () {
if (gameOver) return;
// Resource generation
if (LK.ticks % 180 === 0) {
// Every 3 seconds
resources += 10;
resourceText.setText('Resources: ' + resources);
}
// Zombie spawning - faster spawn rate at higher waves (only if wave is in progress)
if (waveInProgress && zombiesSpawned < zombiesPerWave) {
spawnTimer++;
var zombieSpawnInterval = Math.max(60, 120 - (wave - 1) * 8); // Faster spawning each wave, minimum 1 second
if (spawnTimer >= zombieSpawnInterval) {
spawnZombie();
spawnTimer = 0;
}
}
// Enemy bot spawning - faster spawn rate at higher waves (only if wave is in progress)
if (waveInProgress) {
botSpawnTimer++;
var botSpawnInterval = Math.max(300, 600 - (wave - 1) * 30); // Faster spawning each wave, minimum 5 seconds
if (botSpawnTimer >= botSpawnInterval) {
spawnEnemyBot();
// Spawn additional bots at higher waves
if (wave >= 5 && Math.random() < 0.3) {
spawnEnemyBot(); // 30% chance for second bot
}
if (wave >= 8 && Math.random() < 0.2) {
spawnEnemyBot(); // 20% chance for third bot
}
botSpawnTimer = 0;
}
}
// Check for wave completion
if (waveInProgress && zombiesSpawned >= zombiesPerWave && zombies.length === 0) {
// Wave completed - check if it's time for boss
if (wave === 9) {
// After wave 9, spawn boss
spawnBoss();
waveText.setText('BOSS WAVE!');
} else {
// Regular wave completed - show start button for next wave
waveInProgress = false;
startWaveButton.visible = true;
}
}
// Win condition - boss must be defeated
if (wave >= 9 && zombies.length === 0 && enemyBots.length === 0 && bosses.length === 0) {
// Save high score before showing win screen
var currentScore = LK.getScore();
var highScores = storage.highScores || [];
highScores.push(currentScore);
highScores.sort(function (a, b) {
return b - a;
});
storage.highScores = highScores.slice(0, 10); // Keep top 10
LK.showYouWin();
}
// Update all bullets
for (var i = towerBullets.length - 1; i >= 0; i--) {
var bullet = towerBullets[i];
if (bullet.destroyed) {
towerBullets.splice(i, 1);
}
}
for (var i = bigTowerBullets.length - 1; i >= 0; i--) {
var bullet = bigTowerBullets[i];
if (bullet.destroyed) {
bigTowerBullets.splice(i, 1);
}
}
for (var i = rapidFireBullets.length - 1; i >= 0; i--) {
var bullet = rapidFireBullets[i];
if (bullet.destroyed) {
rapidFireBullets.splice(i, 1);
}
}
for (var i = sniperBullets.length - 1; i >= 0; i--) {
var bullet = sniperBullets[i];
if (bullet.destroyed) {
sniperBullets.splice(i, 1);
}
}
for (var i = heroBullets.length - 1; i >= 0; i--) {
var bullet = heroBullets[i];
if (bullet.destroyed) {
heroBullets.splice(i, 1);
}
}
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
if (bullet.destroyed) {
enemyBullets.splice(i, 1);
}
}
// Update money icons
for (var i = moneyIcons.length - 1; i >= 0; i--) {
var icon = moneyIcons[i];
if (icon.destroyed) {
moneyIcons.splice(i, 1);
}
}
};
bullet. 2d. High contrast. No shadows
zombie. 2d. High contrast. No shadows
playerBase. In-Game asset. 2d. High contrast. No shadows
hero. In-Game asset. 2d. High contrast. No shadows
artilleryBot. In-Game asset. 2d. High contrast. No shadows
defenseTower. In-Game asset. 2d. High contrast. No shadows
enemyBullet. In-Game asset. 2d. High contrast. No shadows
assaultBot. In-Game asset. 2d. High contrast. No shadows
repairBot. In-Game asset. 2d. High contrast. No shadows
explosion. In-Game asset. 2d. High contrast. No shadows
disruptionBot. In-Game asset. 2d. High contrast. No shadows
heroBullet. In-Game asset. 2d. High contrast. No shadows
Coin. In-Game asset. 2d. High contrast. No shadows
buyButton. In-Game asset. 2d. High contrast. No shadows
Zombie boss. In-Game asset. 2d. High contrast. No shadows
Start button. In-Game asset. 2d. High contrast. No shadows