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