/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var AreaTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('areaTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
// Add explosion indicator
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -50;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -50;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
var explosionIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
explosionIndicator.x = 0;
explosionIndicator.y = 0;
explosionIndicator.tint = 0xff0000; // Red explosion symbol
explosionIndicator.scaleX = 1.3;
explosionIndicator.scaleY = 1.3;
self.towerType = 'area';
self.health = 140;
self.maxHealth = 140;
self.range = 180;
self.damage = 25; // Base damage per unit
self.areaRadius = 80; // Area effect radius
self.shootCooldown = 0;
self.target = null;
self.cost = 130;
self.canUpgrade = function () {
return false; // Area towers cannot be upgraded
};
self.getUpgradeCost = function () {
return 0; // Area towers cannot be upgraded
};
self.update = function () {
// Find target (center of enemy group)
if (!self.target || !self.target.parent) {
self.target = null;
var bestTarget = null;
var maxUnitsInArea = 0;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.range) {
// Count how many units would be hit if we target this unit
var unitsInArea = 0;
for (var j = 0; j < playerUnits.length; j++) {
var otherUnit = playerUnits[j];
var areaDistance = Math.sqrt(Math.pow(otherUnit.x - unit.x, 2) + Math.pow(otherUnit.y - unit.y, 2));
if (areaDistance <= self.areaRadius) {
unitsInArea++;
}
}
if (unitsInArea > maxUnitsInArea) {
maxUnitsInArea = unitsInArea;
bestTarget = unit;
}
}
}
self.target = bestTarget;
}
// Shoot area effect
if (self.target && self.shootCooldown <= 0) {
// Create area explosion at target location
var targetX = self.target.x;
var targetY = self.target.y;
// Damage all units in area
var unitsHit = 0;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - targetX, 2) + Math.pow(unit.y - targetY, 2));
if (distance <= self.areaRadius) {
unit.takeDamage(self.damage);
LK.effects.flashObject(unit, 0xff6600, 300);
unitsHit++;
}
}
if (unitsHit > 0) {
// Visual explosion effect
tween(explosionIndicator, {
scaleX: 3.0,
scaleY: 3.0,
alpha: 0.3
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(explosionIndicator, {
scaleX: 1.3,
scaleY: 1.3,
alpha: 1
}, {
duration: 200,
easing: tween.easeIn
});
}
});
LK.getSound('shoot').play();
}
self.shootCooldown = 150; // 2.5 second cooldown
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var AttackUnit = Container.expand(function () {
var self = Container.call(this);
var unitGraphics = self.attachAsset('basicUnitAsset', {
anchorX: 0.5,
anchorY: 0.5
});
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -40;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -40;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
self.health = 100;
self.maxHealth = 100;
self.speed = 2;
self.damage = 25;
self.range = 150;
self.shootCooldown = 0;
self.target = null;
self.lastX = 0;
self.lastY = 0;
self.currentWaypoint = 0;
self.isMovingToWaypoint = false;
// Create path through tower grid
var pathWaypoints = [];
pathWaypoints.push({
x: 400,
y: self.y
});
// Tower grid parameters
var gridStartX = 800;
var gridStartY = 600;
var gridSpacingX = 200;
var gridSpacingY = 200;
var gridRows = 8;
var gridCols = 4;
// Serpentine path through towers
for (var col = 0; col < gridCols; col++) {
if (col % 2 === 0) {
for (var row = 0; row < gridRows; row++) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
} else {
for (var row = gridRows - 1; row >= 0; row--) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
}
}
pathWaypoints.push({
x: 1600,
y: 1000
});
self.pathWaypoints = pathWaypoints;
self.followPath = function () {
if (self.isMovingToWaypoint) return;
if (self.currentWaypoint >= self.pathWaypoints.length) {
// Reached end of path, move directly to base
self.x += self.speed;
return;
}
var waypoint = self.pathWaypoints[self.currentWaypoint];
var distance = Math.sqrt(Math.pow(waypoint.x - self.x, 2) + Math.pow(waypoint.y - self.y, 2));
var duration = distance / self.speed * 16.67; // Convert speed to ms (60fps = 16.67ms per frame)
self.isMovingToWaypoint = true;
tween(self, {
x: waypoint.x,
y: waypoint.y
}, {
duration: duration,
easing: tween.linear,
onFinish: function onFinish() {
self.currentWaypoint++;
self.isMovingToWaypoint = false;
}
});
};
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
// Find target (prioritize base, then towers)
if (!self.target || !self.target.parent) {
self.target = null;
var closestTarget = null;
var closestDistance = Infinity;
// Check distance to enemy base first
if (enemyBase && enemyBase.parent) {
var baseDistance = Math.sqrt(Math.pow(enemyBase.x - self.x, 2) + Math.pow(enemyBase.y - self.y, 2));
if (baseDistance <= self.range) {
closestTarget = enemyBase;
closestDistance = baseDistance;
}
}
// Check towers if no base target or if tower is closer
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower && tower.parent) {
var towerDistance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (towerDistance <= self.range && towerDistance < closestDistance) {
closestDistance = towerDistance;
closestTarget = tower;
}
}
}
self.target = closestTarget;
}
// Attack target if in range
if (self.target && self.shootCooldown <= 0) {
var bullet = new UnitBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = self.target;
bullet.damage = self.damage;
unitBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 60; // 1 second cooldown at 60fps
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
// Move towards enemy base using waypoints only if no target in range
if (!self.target) {
self.followPath();
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
return true; // Unit is dead
}
return false;
};
return self;
});
var BasicTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('basicTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -50;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -50;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
self.towerType = 'basic';
self.health = 100;
self.maxHealth = 100;
self.range = 150;
self.damage = 20;
self.shootCooldown = 0;
self.target = null;
self.cost = 75;
// Upgrade indicator (initially hidden)
var upgradeIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
upgradeIndicator.x = 30;
upgradeIndicator.y = -30;
upgradeIndicator.visible = false;
self.level = 1;
self.maxLevel = 3;
self.baseRange = 150;
self.baseDamage = 20;
self.range = self.baseRange;
self.damage = self.baseDamage;
self.updateStats = function () {
var multiplier = 1 + (self.level - 1) * 0.6; // 60% increase per level
self.range = Math.floor(self.baseRange * multiplier);
self.damage = Math.floor(self.baseDamage * multiplier);
self.maxHealth = Math.floor(100 * multiplier);
self.health = Math.min(self.health, self.maxHealth);
// Special abilities for max level
if (self.level === 3) {
self.shieldActive = false;
self.shieldCooldown = 0;
self.shieldDuration = 0;
}
// Update visual appearance based on level
if (self.level === 2) {
towerGraphics.tint = 0x4169E1; // Royal blue
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xffd700; // Gold
} else if (self.level === 3) {
towerGraphics.tint = 0x0000FF; // Pure blue
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xff4500; // Orange red
}
};
self.canUpgrade = function () {
return self.level < self.maxLevel;
};
self.getUpgradeCost = function () {
return 50 + (self.level - 1) * 25; // Cost increases with level
};
self.upgrade = function () {
if (self.canUpgrade()) {
self.level++;
self.updateStats();
LK.effects.flashObject(self, 0xffd700, 500);
return true;
}
return false;
};
self.update = function () {
// Shield ability for level 3
if (self.level === 3) {
// Activate shield when health is low and not on cooldown
if (self.health < self.maxHealth * 0.3 && !self.shieldActive && self.shieldCooldown <= 0) {
self.shieldActive = true;
self.shieldDuration = 300; // 5 seconds of invulnerability
towerGraphics.tint = 0xFFD700; // Golden shield color
LK.effects.flashObject(self, 0xFFD700, 300);
}
// Handle shield duration
if (self.shieldActive) {
self.shieldDuration--;
if (self.shieldDuration <= 0) {
self.shieldActive = false;
self.shieldCooldown = 1200; // 20 second cooldown
towerGraphics.tint = 0x0000FF; // Back to normal level 3 color
}
}
// Update cooldown
if (self.shieldCooldown > 0) {
self.shieldCooldown--;
}
}
// Find target
if (!self.target || !self.target.parent) {
self.target = null;
var closestUnit = null;
var closestDistance = Infinity;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.range && distance < closestDistance) {
closestDistance = distance;
closestUnit = unit;
}
}
self.target = closestUnit;
}
// Shoot at target (if not frozen)
if (self.target && self.shootCooldown <= 0 && !self.frozenUntil) {
var bullet = new TowerBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = self.target;
bullet.damage = self.damage;
towerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 90; // 1.5 second cooldown
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.takeDamage = function (damage) {
// Shield ability blocks damage at level 3
if (self.level === 3 && self.shieldActive) {
LK.effects.flashObject(self, 0xFFD700, 200); // Gold flash for blocked damage
return false; // No damage taken
}
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var BasicUnit = Container.expand(function () {
var self = Container.call(this);
var unitGraphics = self.attachAsset('basicUnitAsset', {
anchorX: 0.5,
anchorY: 0.5
});
unitGraphics.tint = 0x87CEEB; // Sky blue color for basic unit
unitGraphics.scaleX = 0.9;
unitGraphics.scaleY = 0.9;
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -35;
healthBarBg.scaleX = 0.8;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -35;
healthBar.scaleX = 0.8;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent * 0.8;
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00;
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00;
} else {
healthBar.tint = 0xff0000;
}
};
self.unitType = 'basic';
self.health = 60;
self.maxHealth = 60;
self.speed = 1.8;
self.damage = 15;
self.range = 140;
self.cost = 40;
self.shootCooldown = 0;
self.target = null;
self.lastX = 0;
self.lastY = 0;
self.currentWaypoint = 0;
self.isMovingToWaypoint = false;
// Create path through tower grid
var pathWaypoints = [];
pathWaypoints.push({
x: 400,
y: self.y
});
// Tower grid parameters
var gridStartX = 800;
var gridStartY = 600;
var gridSpacingX = 200;
var gridSpacingY = 200;
var gridRows = 8;
var gridCols = 4;
// Serpentine path through towers
for (var col = 0; col < gridCols; col++) {
if (col % 2 === 0) {
for (var row = 0; row < gridRows; row++) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
} else {
for (var row = gridRows - 1; row >= 0; row--) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
}
}
pathWaypoints.push({
x: 1600,
y: 1000
});
self.pathWaypoints = pathWaypoints;
self.followPath = function () {
if (self.isMovingToWaypoint) return;
if (self.currentWaypoint >= self.pathWaypoints.length) {
// Check for walls blocking movement
var canMove = true;
var nextX = self.x + self.speed;
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower.towerType === 'wall') {
var distance = Math.sqrt(Math.pow(tower.x - nextX, 2) + Math.pow(tower.y - self.y, 2));
if (distance <= tower.blockRadius) {
canMove = false;
break;
}
}
}
if (canMove) {
self.x += self.speed;
}
return;
}
var waypoint = self.pathWaypoints[self.currentWaypoint];
// Check if path to waypoint is blocked by walls
var pathBlocked = false;
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower.towerType === 'wall') {
var wallDistance = Math.sqrt(Math.pow(tower.x - waypoint.x, 2) + Math.pow(tower.y - waypoint.y, 2));
if (wallDistance <= tower.blockRadius) {
pathBlocked = true;
break;
}
}
}
if (pathBlocked) {
// Skip this waypoint if blocked
self.currentWaypoint++;
return;
}
var distance = Math.sqrt(Math.pow(waypoint.x - self.x, 2) + Math.pow(waypoint.y - self.y, 2));
var duration = distance / self.speed * 16.67;
self.isMovingToWaypoint = true;
tween(self, {
x: waypoint.x,
y: waypoint.y
}, {
duration: duration,
easing: tween.linear,
onFinish: function onFinish() {
self.currentWaypoint++;
self.isMovingToWaypoint = false;
}
});
};
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
if (!self.target || !self.target.parent) {
self.target = null;
var closestTarget = null;
var closestDistance = Infinity;
if (enemyBase && enemyBase.parent) {
var baseDistance = Math.sqrt(Math.pow(enemyBase.x - self.x, 2) + Math.pow(enemyBase.y - self.y, 2));
if (baseDistance <= self.range) {
closestTarget = enemyBase;
closestDistance = baseDistance;
}
}
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower && tower.parent) {
var towerDistance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (towerDistance <= self.range && towerDistance < closestDistance) {
closestDistance = towerDistance;
closestTarget = tower;
}
}
}
self.target = closestTarget;
}
if (self.target && self.shootCooldown <= 0) {
var bullet = new UnitBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = self.target;
bullet.damage = self.damage;
unitBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 75;
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
if (!self.target) {
self.followPath();
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
return true;
}
return false;
};
return self;
});
var EnemyBase = Container.expand(function () {
var self = Container.call(this);
var baseGraphics = self.attachAsset('enemyBase', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 800;
self.maxHealth = 800;
self.range = 300; // Base shooting range
self.damage = 60; // Base damage per shot
self.shootCooldown = 0;
self.target = null;
self.update = function () {
// Find target within range
if (!self.target || !self.target.parent) {
self.target = null;
var closestUnit = null;
var closestDistance = Infinity;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.range && distance < closestDistance) {
closestDistance = distance;
closestUnit = unit;
}
}
self.target = closestUnit;
}
// Shoot at target (if not frozen)
if (self.target && self.shootCooldown <= 0 && !self.frozenUntil) {
var bullet = new TowerBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = self.target;
bullet.damage = self.damage;
towerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 90; // 1.5 second cooldown
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 300);
if (self.health <= 0) {
return true; // Base destroyed - player wins!
}
return false;
};
return self;
});
// Game arrays to track entities
var EnemyTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('enemyTower', {
anchorX: 0.5,
anchorY: 0.5
});
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -50;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -50;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
// Upgrade indicator (initially hidden)
var upgradeIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
upgradeIndicator.x = 30;
upgradeIndicator.y = -30;
upgradeIndicator.visible = false;
self.level = 1;
self.maxLevel = 3;
self.health = 250;
self.maxHealth = 250;
self.baseRange = 220;
self.baseDamage = 45;
self.range = self.baseRange;
self.damage = self.baseDamage;
self.shootCooldown = 0;
self.target = null;
self.updateStats = function () {
var multiplier = 1 + (self.level - 1) * 0.5; // 50% increase per level
self.range = Math.floor(self.baseRange * multiplier);
self.damage = Math.floor(self.baseDamage * multiplier);
self.maxHealth = Math.floor(150 * multiplier);
self.health = Math.min(self.health, self.maxHealth);
// Update visual appearance based on level
if (self.level === 2) {
towerGraphics.tint = 0xc0392b; // Darker red
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xffd700; // Gold
} else if (self.level === 3) {
towerGraphics.tint = 0x8b0000; // Dark red
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xff4500; // Orange red
}
};
self.canUpgrade = function () {
return self.level < self.maxLevel;
};
self.getUpgradeCost = function () {
return 75 * self.level; // Cost increases with level
};
self.upgrade = function () {
if (self.canUpgrade()) {
self.level++;
self.updateStats();
LK.effects.flashObject(self, 0xffd700, 500);
return true;
}
return false;
};
self.update = function () {
// Increase range and damage if base is low on health
var baseHealthPercent = enemyBase ? enemyBase.health / enemyBase.maxHealth : 1;
var bonusRange = baseHealthPercent < 0.5 ? 100 : 0;
var bonusDamage = baseHealthPercent < 0.3 ? 20 : 0;
var currentRange = self.range + bonusRange;
var currentDamage = self.damage + bonusDamage;
// Find target
if (!self.target || !self.target.parent) {
self.target = null;
var closestUnit = null;
var closestDistance = Infinity;
// Prioritize units closer to base
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
var distanceToBase = Math.sqrt(Math.pow(unit.x - enemyBase.x, 2) + Math.pow(unit.y - enemyBase.y, 2));
if (distance <= currentRange) {
// Prioritize units closer to base
var priority = distance - distanceToBase * 0.3;
if (priority < closestDistance) {
closestDistance = priority;
closestUnit = unit;
}
}
}
self.target = closestUnit;
}
// Shoot at target
if (self.target && self.shootCooldown <= 0) {
var bullet = new TowerBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = self.target;
bullet.damage = currentDamage;
towerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
// Shoot faster when base is in danger
var shootCooldown = baseHealthPercent < 0.5 ? 60 : 90;
self.shootCooldown = shootCooldown;
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var FastTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('fastTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -40;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -40;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
self.towerType = 'fast';
self.health = 80;
self.maxHealth = 80;
self.range = 180;
self.damage = 15;
self.shootCooldown = 0;
self.target = null;
self.cost = 100;
// Upgrade indicator (initially hidden)
var upgradeIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
upgradeIndicator.x = 25;
upgradeIndicator.y = -25;
upgradeIndicator.visible = false;
self.level = 1;
self.maxLevel = 3;
self.baseRange = 180;
self.baseDamage = 15;
self.baseCooldown = 30;
self.multiShot = 1; // Number of bullets per shot
self.range = self.baseRange;
self.damage = self.baseDamage;
self.updateStats = function () {
var multiplier = 1 + (self.level - 1) * 0.4; // 40% increase per level
self.range = Math.floor(self.baseRange * multiplier);
self.damage = Math.floor(self.baseDamage * multiplier);
self.maxHealth = Math.floor(80 * multiplier);
self.health = Math.min(self.health, self.maxHealth);
self.multiShot = self.level; // Shoot multiple bullets at higher levels
// Special abilities for max level
if (self.level === 3) {
self.burstMode = false;
self.burstCooldown = 0;
self.burstDuration = 0;
self.burstTriggerDistance = 100; // Distance to activate burst mode
}
// Update visual appearance based on level
if (self.level === 2) {
towerGraphics.tint = 0x00FF7F; // Spring green
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xffd700; // Gold
} else if (self.level === 3) {
towerGraphics.tint = 0x00FF00; // Pure green
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xff4500; // Orange red
}
};
self.canUpgrade = function () {
return self.level < self.maxLevel;
};
self.getUpgradeCost = function () {
return 60 + (self.level - 1) * 30; // Cost increases with level
};
self.upgrade = function () {
if (self.canUpgrade()) {
self.level++;
self.updateStats();
LK.effects.flashObject(self, 0xffd700, 500);
return true;
}
return false;
};
self.update = function () {
// Burst mode ability for level 3
if (self.level === 3) {
// Check if any enemies are very close to activate burst mode
var hasCloseEnemies = false;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.burstTriggerDistance) {
hasCloseEnemies = true;
break;
}
}
// Activate burst mode when enemies are close and not on cooldown
if (hasCloseEnemies && !self.burstMode && self.burstCooldown <= 0) {
self.burstMode = true;
self.burstDuration = 180; // 3 seconds of burst fire
towerGraphics.tint = 0xFFFF00; // Yellow burst color
LK.effects.flashObject(self, 0xFFFF00, 300);
}
// Handle burst duration
if (self.burstMode) {
self.burstDuration--;
if (self.burstDuration <= 0) {
self.burstMode = false;
self.burstCooldown = 600; // 10 second cooldown
towerGraphics.tint = 0x00FF00; // Back to normal level 3 color
}
}
// Update cooldown
if (self.burstCooldown > 0) {
self.burstCooldown--;
}
}
// Find target
if (!self.target || !self.target.parent) {
self.target = null;
var closestUnit = null;
var closestDistance = Infinity;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.range && distance < closestDistance) {
closestDistance = distance;
closestUnit = unit;
}
}
self.target = closestUnit;
}
// Shoot at target with fast rate and multi-shot
if (self.target && self.shootCooldown <= 0) {
// Determine shot count - burst mode doubles the shots
var shotCount = self.multiShot;
if (self.level === 3 && self.burstMode) {
shotCount = self.multiShot * 2; // Double shots in burst mode
}
// Shoot multiple bullets based on level and burst mode
for (var shotIndex = 0; shotIndex < shotCount; shotIndex++) {
var bullet = new TowerBullet();
bullet.x = self.x;
bullet.y = self.y;
// For multi-shot, slightly spread the bullets
if (shotCount > 1) {
var angleOffset = (shotIndex - (shotCount - 1) / 2) * 0.2; // Smaller angle spread for more bullets
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var angle = Math.atan2(dy, dx) + angleOffset;
var distance = Math.sqrt(dx * dx + dy * dy);
// Create a virtual target with slight offset
bullet.target = {
x: self.x + Math.cos(angle) * distance,
y: self.y + Math.sin(angle) * distance,
parent: self.target.parent,
takeDamage: self.target.takeDamage.bind(self.target)
};
} else {
bullet.target = self.target;
}
bullet.damage = self.damage;
towerBullets.push(bullet);
game.addChild(bullet);
}
LK.getSound('shoot').play();
// Faster shooting in burst mode
var cooldownTime = self.level === 3 && self.burstMode ? 15 : 30;
self.shootCooldown = cooldownTime;
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var FastUnit = Container.expand(function () {
var self = Container.call(this);
var unitGraphics = self.attachAsset('fastUnitAsset', {
anchorX: 0.5,
anchorY: 0.5
});
unitGraphics.tint = 0x32CD32; // Lime green color for fast unit
unitGraphics.scaleX = 0.8;
unitGraphics.scaleY = 0.8;
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -32;
healthBarBg.scaleX = 0.7;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -32;
healthBar.scaleX = 0.7;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent * 0.7;
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00;
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00;
} else {
healthBar.tint = 0xff0000;
}
};
self.unitType = 'fast';
self.health = 45;
self.maxHealth = 45;
self.speed = 3.5;
self.damage = 12;
self.range = 120;
self.cost = 60;
self.shootCooldown = 0;
self.target = null;
self.lastX = 0;
self.lastY = 0;
self.currentWaypoint = 0;
self.isMovingToWaypoint = false;
// Create path through tower grid
var pathWaypoints = [];
pathWaypoints.push({
x: 400,
y: self.y
});
// Tower grid parameters
var gridStartX = 800;
var gridStartY = 600;
var gridSpacingX = 200;
var gridSpacingY = 200;
var gridRows = 8;
var gridCols = 4;
// Serpentine path through towers
for (var col = 0; col < gridCols; col++) {
if (col % 2 === 0) {
for (var row = 0; row < gridRows; row++) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
} else {
for (var row = gridRows - 1; row >= 0; row--) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
}
}
pathWaypoints.push({
x: 1600,
y: 1000
});
self.pathWaypoints = pathWaypoints;
self.followPath = function () {
if (self.isMovingToWaypoint) return;
if (self.currentWaypoint >= self.pathWaypoints.length) {
self.x += self.speed;
return;
}
var waypoint = self.pathWaypoints[self.currentWaypoint];
var distance = Math.sqrt(Math.pow(waypoint.x - self.x, 2) + Math.pow(waypoint.y - self.y, 2));
var duration = distance / self.speed * 16.67;
self.isMovingToWaypoint = true;
tween(self, {
x: waypoint.x,
y: waypoint.y
}, {
duration: duration,
easing: tween.linear,
onFinish: function onFinish() {
self.currentWaypoint++;
self.isMovingToWaypoint = false;
}
});
};
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
if (!self.target || !self.target.parent) {
self.target = null;
var closestTarget = null;
var closestDistance = Infinity;
if (enemyBase && enemyBase.parent) {
var baseDistance = Math.sqrt(Math.pow(enemyBase.x - self.x, 2) + Math.pow(enemyBase.y - self.y, 2));
if (baseDistance <= self.range) {
closestTarget = enemyBase;
closestDistance = baseDistance;
}
}
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower && tower.parent) {
var towerDistance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (towerDistance <= self.range && towerDistance < closestDistance) {
closestDistance = towerDistance;
closestTarget = tower;
}
}
}
self.target = closestTarget;
}
if (self.target && self.shootCooldown <= 0) {
var bullet = new UnitBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = self.target;
bullet.damage = self.damage;
unitBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 45; // Faster shooting
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
if (!self.target) {
self.followPath();
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
return true;
}
return false;
};
return self;
});
var HealTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('healTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -50;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -50;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
// Add a cross symbol effect
var crossIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
crossIndicator.x = 0;
crossIndicator.y = 0;
crossIndicator.tint = 0xffffff; // White cross
crossIndicator.scaleX = 1.5;
crossIndicator.scaleY = 1.5;
self.towerType = 'heal';
self.health = 120;
self.maxHealth = 120;
self.range = 200; // Healing range
self.healAmount = 15;
self.healCooldown = 0;
self.target = null;
self.cost = 110;
self.canUpgrade = function () {
return false; // Heal towers cannot be upgraded
};
self.getUpgradeCost = function () {
return 0; // Heal towers cannot be upgraded
};
self.update = function () {
// Find damaged towers to heal
if (self.healCooldown <= 0) {
self.target = null;
var bestTarget = null;
var lowestHealthPercent = 1;
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower !== self && tower.parent) {
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance <= self.range) {
var healthPercent = tower.health / tower.maxHealth;
if (healthPercent < 1 && healthPercent < lowestHealthPercent) {
lowestHealthPercent = healthPercent;
bestTarget = tower;
}
}
}
}
self.target = bestTarget;
}
// Heal target
if (self.target && self.healCooldown <= 0) {
var healingAmount = Math.min(self.healAmount, self.target.maxHealth - self.target.health);
if (healingAmount > 0) {
self.target.health += healingAmount;
// Update target's health bar after healing
if (self.target.updateHealthBar) {
self.target.updateHealthBar();
}
LK.effects.flashObject(self.target, 0x00ff00, 400);
// Create healing visual effect
tween(crossIndicator, {
scaleX: 2.0,
scaleY: 2.0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(crossIndicator, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeIn
});
}
});
self.healCooldown = 120; // 2 second cooldown
}
}
if (self.healCooldown > 0) {
self.healCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var HeavyTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('heavyTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -60;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -60;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
self.towerType = 'heavy';
self.health = 200;
self.maxHealth = 200;
self.range = 160;
self.damage = 50;
self.shootCooldown = 0;
self.target = null;
self.cost = 150;
// Upgrade indicator (initially hidden)
var upgradeIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
upgradeIndicator.x = 35;
upgradeIndicator.y = -35;
upgradeIndicator.visible = false;
self.level = 1;
self.maxLevel = 3;
self.baseRange = 160;
self.baseDamage = 50;
self.splashRadius = 0; // Splash damage radius
self.range = self.baseRange;
self.damage = self.baseDamage;
self.updateStats = function () {
var multiplier = 1 + (self.level - 1) * 0.5; // 50% increase per level
self.range = Math.floor(self.baseRange * multiplier);
self.damage = Math.floor(self.baseDamage * multiplier);
self.maxHealth = Math.floor(200 * multiplier);
self.health = Math.min(self.health, self.maxHealth);
self.splashRadius = (self.level - 1) * 60; // Splash damage at higher levels
// Special abilities for max level
if (self.level === 3) {
self.mineActive = false;
self.mineCooldown = 0;
self.mineRadius = 100;
self.mineDamage = 120;
}
// Update visual appearance based on level
if (self.level === 2) {
towerGraphics.tint = 0x8B4513; // Saddle brown
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xffd700; // Gold
} else if (self.level === 3) {
towerGraphics.tint = 0x654321; // Dark brown
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xff4500; // Orange red
}
};
self.canUpgrade = function () {
return self.level < self.maxLevel;
};
self.getUpgradeCost = function () {
return 75 + (self.level - 1) * 40; // Cost increases with level
};
self.upgrade = function () {
if (self.canUpgrade()) {
self.level++;
self.updateStats();
LK.effects.flashObject(self, 0xffd700, 500);
return true;
}
return false;
};
self.update = function () {
// Earthquake ability for level 3
if (self.level === 3 && self.earthquakeCooldown <= 0) {
// Count enemies within earthquake range
var enemiesInRange = 0;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.earthquakeRadius) {
enemiesInRange++;
}
}
// Trigger earthquake if 3+ enemies are nearby
if (enemiesInRange >= 3) {
// Damage all enemies in range
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.earthquakeRadius) {
unit.takeDamage(self.earthquakeDamage);
LK.effects.flashObject(unit, 0x8B4513, 400);
}
}
// Visual earthquake effect
towerGraphics.tint = 0xFFD700; // Golden earthquake color
LK.effects.flashObject(self, 0x8B4513, 600);
// Screen shake effect simulation
LK.effects.flashScreen(0x8B4513, 300);
self.earthquakeCooldown = 900; // 15 second cooldown
// Return to normal color after effect
var self_ref = self;
LK.setTimeout(function () {
self_ref.towerGraphics.tint = 0x654321; // Back to level 3 color
}, 600);
}
}
// Update earthquake cooldown
if (self.earthquakeCooldown > 0) {
self.earthquakeCooldown--;
}
// Find target
if (!self.target || !self.target.parent) {
self.target = null;
var closestUnit = null;
var closestDistance = Infinity;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.range && distance < closestDistance) {
closestDistance = distance;
closestUnit = unit;
}
}
self.target = closestUnit;
}
// Shoot at target with heavy damage and potential splash
if (self.target && self.shootCooldown <= 0) {
var bullet = new TowerBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = self.target;
bullet.damage = self.damage;
bullet.splashRadius = self.splashRadius; // Add splash capability
bullet.splashDamage = Math.floor(self.damage * 0.6); // 60% splash damage
towerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 120; // 2 second cooldown (slow)
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var HeavyUnit = Container.expand(function () {
var self = Container.call(this);
var unitGraphics = self.attachAsset('heavyUnitAsset', {
anchorX: 0.5,
anchorY: 0.5
});
unitGraphics.tint = 0x8B4513; // Brown color for heavy unit
unitGraphics.scaleX = 1.2;
unitGraphics.scaleY = 1.2;
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -45;
healthBarBg.scaleX = 1.0;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -45;
healthBar.scaleX = 1.0;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent * 1.0;
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00;
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00;
} else {
healthBar.tint = 0xff0000;
}
};
self.unitType = 'heavy';
self.health = 120;
self.maxHealth = 120;
self.speed = 0.8;
self.damage = 30;
self.range = 160;
self.cost = 90;
self.shootCooldown = 0;
self.target = null;
self.lastX = 0;
self.lastY = 0;
self.currentWaypoint = 0;
self.isMovingToWaypoint = false;
// Create path through tower grid
var pathWaypoints = [];
pathWaypoints.push({
x: 400,
y: self.y
});
// Tower grid parameters
var gridStartX = 800;
var gridStartY = 600;
var gridSpacingX = 200;
var gridSpacingY = 200;
var gridRows = 8;
var gridCols = 4;
// Serpentine path through towers
for (var col = 0; col < gridCols; col++) {
if (col % 2 === 0) {
for (var row = 0; row < gridRows; row++) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
} else {
for (var row = gridRows - 1; row >= 0; row--) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
}
}
pathWaypoints.push({
x: 1600,
y: 1000
});
self.pathWaypoints = pathWaypoints;
self.followPath = function () {
if (self.isMovingToWaypoint) return;
if (self.currentWaypoint >= self.pathWaypoints.length) {
self.x += self.speed;
return;
}
var waypoint = self.pathWaypoints[self.currentWaypoint];
var distance = Math.sqrt(Math.pow(waypoint.x - self.x, 2) + Math.pow(waypoint.y - self.y, 2));
var duration = distance / self.speed * 16.67;
self.isMovingToWaypoint = true;
tween(self, {
x: waypoint.x,
y: waypoint.y
}, {
duration: duration,
easing: tween.linear,
onFinish: function onFinish() {
self.currentWaypoint++;
self.isMovingToWaypoint = false;
}
});
};
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
if (!self.target || !self.target.parent) {
self.target = null;
var closestTarget = null;
var closestDistance = Infinity;
if (enemyBase && enemyBase.parent) {
var baseDistance = Math.sqrt(Math.pow(enemyBase.x - self.x, 2) + Math.pow(enemyBase.y - self.y, 2));
if (baseDistance <= self.range) {
closestTarget = enemyBase;
closestDistance = baseDistance;
}
}
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower && tower.parent) {
var towerDistance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (towerDistance <= self.range && towerDistance < closestDistance) {
closestDistance = towerDistance;
closestTarget = tower;
}
}
}
self.target = closestTarget;
}
if (self.target && self.shootCooldown <= 0) {
var bullet = new UnitBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = self.target;
bullet.damage = self.damage;
unitBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 100; // Slower shooting
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
if (!self.target) {
self.followPath();
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
return true;
}
return false;
};
return self;
});
var LaserTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('laserTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
towerGraphics.tint = 0xFF1493; // Deep pink for laser tower
towerGraphics.scaleX = 1.2;
towerGraphics.scaleY = 1.2;
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -45;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -45;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
// Add laser beam indicator
var laserIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
laserIndicator.x = 0;
laserIndicator.y = 0;
laserIndicator.tint = 0xFF1493; // Pink laser symbol
laserIndicator.scaleX = 1.3;
laserIndicator.scaleY = 1.3;
self.towerType = 'laser';
self.health = 100;
self.maxHealth = 100;
self.range = 220;
self.damage = 8; // Continuous damage per frame
self.chargingTime = 60; // 1 second to charge
self.maxBeamTime = 180; // 3 seconds max beam
self.cooldownTime = 120; // 2 second cooldown
self.beamState = 'idle'; // 'idle', 'charging', 'firing', 'cooling'
self.stateTimer = 0;
self.target = null;
self.cost = 140;
self.canUpgrade = function () {
return false; // Laser towers cannot be upgraded
};
self.getUpgradeCost = function () {
return 0; // Laser towers cannot be upgraded
};
self.update = function () {
// Find target when idle
if (self.beamState === 'idle') {
if (!self.target || !self.target.parent) {
self.target = null;
var closestUnit = null;
var closestDistance = Infinity;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.range && distance < closestDistance) {
closestDistance = distance;
closestUnit = unit;
}
}
self.target = closestUnit;
}
if (self.target) {
self.beamState = 'charging';
self.stateTimer = self.chargingTime;
// Start charging effect
towerGraphics.tint = 0xFF69B4; // Lighter pink while charging
}
}
// Handle beam states
if (self.beamState === 'charging') {
self.stateTimer--;
if (self.stateTimer <= 0) {
self.beamState = 'firing';
self.stateTimer = self.maxBeamTime;
towerGraphics.tint = 0xFF0000; // Red while firing
}
} else if (self.beamState === 'firing') {
// Continuous damage while firing
if (self.target && self.target.parent) {
var distance = Math.sqrt(Math.pow(self.target.x - self.x, 2) + Math.pow(self.target.y - self.y, 2));
if (distance <= self.range) {
self.target.takeDamage(self.damage);
// Visual beam effect
if (LK.ticks % 10 === 0) {
LK.effects.flashObject(self.target, 0xFF1493, 100);
}
} else {
self.target = null; // Lost target
}
}
// Laser beam visual effect
tween(laserIndicator, {
scaleX: 1.8,
scaleY: 1.8,
alpha: 0.7
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(laserIndicator, {
scaleX: 1.3,
scaleY: 1.3,
alpha: 1
}, {
duration: 100,
easing: tween.easeIn
});
}
});
self.stateTimer--;
if (self.stateTimer <= 0 || !self.target || !self.target.parent) {
self.beamState = 'cooling';
self.stateTimer = self.cooldownTime;
self.target = null;
towerGraphics.tint = 0x808080; // Gray while cooling
}
} else if (self.beamState === 'cooling') {
self.stateTimer--;
if (self.stateTimer <= 0) {
self.beamState = 'idle';
towerGraphics.tint = 0xFF1493; // Back to normal color
}
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var MeleeUnit = Container.expand(function () {
var self = Container.call(this);
var unitGraphics = self.attachAsset('meleeUnitAsset', {
anchorX: 0.5,
anchorY: 0.5
});
unitGraphics.tint = 0xFF6347; // Tomato red color for melee unit
unitGraphics.scaleX = 1.1;
unitGraphics.scaleY = 1.1;
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -40;
healthBarBg.scaleX = 0.9;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -40;
healthBar.scaleX = 0.9;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent * 0.9;
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00;
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00;
} else {
healthBar.tint = 0xff0000;
}
};
self.unitType = 'melee';
self.health = 90;
self.maxHealth = 90;
self.speed = 1.3;
self.damage = 25;
self.range = 50; // Very short range for melee
self.cost = 30;
self.attackCooldown = 0;
self.target = null;
self.lastX = 0;
self.lastY = 0;
self.currentWaypoint = 0;
self.isMovingToWaypoint = false;
// Create path through tower grid
var pathWaypoints = [];
pathWaypoints.push({
x: 400,
y: self.y
});
// Tower grid parameters
var gridStartX = 800;
var gridStartY = 600;
var gridSpacingX = 200;
var gridSpacingY = 200;
var gridRows = 8;
var gridCols = 4;
// Serpentine path through towers
for (var col = 0; col < gridCols; col++) {
if (col % 2 === 0) {
for (var row = 0; row < gridRows; row++) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
} else {
for (var row = gridRows - 1; row >= 0; row--) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
}
}
pathWaypoints.push({
x: 1600,
y: 1000
});
self.pathWaypoints = pathWaypoints;
self.followPath = function () {
if (self.isMovingToWaypoint) return;
if (self.currentWaypoint >= self.pathWaypoints.length) {
self.x += self.speed;
return;
}
var waypoint = self.pathWaypoints[self.currentWaypoint];
var distance = Math.sqrt(Math.pow(waypoint.x - self.x, 2) + Math.pow(waypoint.y - self.y, 2));
var duration = distance / self.speed * 16.67;
self.isMovingToWaypoint = true;
tween(self, {
x: waypoint.x,
y: waypoint.y
}, {
duration: duration,
easing: tween.linear,
onFinish: function onFinish() {
self.currentWaypoint++;
self.isMovingToWaypoint = false;
}
});
};
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
// Find closest target (prioritize base, then towers)
if (!self.target || !self.target.parent) {
self.target = null;
var closestTarget = null;
var closestDistance = Infinity;
// Check distance to enemy base first
if (enemyBase && enemyBase.parent) {
var baseDistance = Math.sqrt(Math.pow(enemyBase.x - self.x, 2) + Math.pow(enemyBase.y - self.y, 2));
if (baseDistance <= self.range) {
closestTarget = enemyBase;
closestDistance = baseDistance;
}
}
// Check towers if no base target or if tower is closer
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower && tower.parent) {
var towerDistance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (towerDistance <= self.range && towerDistance < closestDistance) {
closestDistance = towerDistance;
closestTarget = tower;
}
}
}
self.target = closestTarget;
}
// Melee attack - no bullets, direct damage
if (self.target && self.attackCooldown <= 0) {
// Flash effect for melee attack
LK.effects.flashObject(self, 0xFF6347, 200);
LK.effects.flashObject(self.target, 0xFF0000, 200);
// Direct damage to target
self.target.takeDamage(self.damage);
LK.getSound('hit').play();
self.attackCooldown = 90; // 1.5 second cooldown
}
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
// Move towards target or follow path
if (self.target) {
// Move directly towards target if one is found and not in attack range
if (!self.isMovingToWaypoint) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > self.range) {
// Move towards target only if outside attack range
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
}
} else {
// Follow path if no target
self.followPath();
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
return true;
}
return false;
};
return self;
});
var PoisonTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('poisonTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
towerGraphics.tint = 0x228B22; // Forest green for poison tower
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -50;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -50;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
// Add poison cloud indicator
var poisonIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
poisonIndicator.x = 0;
poisonIndicator.y = 0;
poisonIndicator.tint = 0x228B22; // Green poison symbol
poisonIndicator.scaleX = 1.4;
poisonIndicator.scaleY = 1.4;
self.towerType = 'poison';
self.health = 110;
self.maxHealth = 110;
self.range = 160;
self.damage = 15; // Initial damage
self.poisonDamage = 5; // Damage per tick
self.poisonDuration = 480; // 8 seconds at 60fps
self.shootCooldown = 0;
self.target = null;
self.cost = 95;
self.canUpgrade = function () {
return false; // Poison towers cannot be upgraded
};
self.getUpgradeCost = function () {
return 0; // Poison towers cannot be upgraded
};
self.update = function () {
// Find target
if (!self.target || !self.target.parent) {
self.target = null;
var closestUnit = null;
var closestDistance = Infinity;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.range && distance < closestDistance) {
closestDistance = distance;
closestUnit = unit;
}
}
self.target = closestUnit;
}
// Apply poison effect
if (self.target && self.shootCooldown <= 0) {
// Initial damage
self.target.takeDamage(self.damage);
// Apply poison effect
self.target.poisonDamage = self.poisonDamage;
self.target.poisonEndTime = LK.ticks + self.poisonDuration;
self.target.lastPoisonTick = LK.ticks;
// Visual effects
LK.effects.flashObject(self.target, 0x228B22, 400);
tween(poisonIndicator, {
scaleX: 2.2,
scaleY: 2.2,
alpha: 0.6
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(poisonIndicator, {
scaleX: 1.4,
scaleY: 1.4,
alpha: 1
}, {
duration: 200,
easing: tween.easeIn
});
}
});
LK.getSound('shoot').play();
self.shootCooldown = 120; // 2 second cooldown
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var ResurrectionTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('resurrectionTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
towerGraphics.tint = 0xFFD700; // Gold color for resurrection tower
towerGraphics.scaleX = 1.3;
towerGraphics.scaleY = 1.3;
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -60;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -60;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
// Add resurrection symbol effect
var resIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
resIndicator.x = 0;
resIndicator.y = 0;
resIndicator.tint = 0xFFFFFF; // White resurrection symbol
resIndicator.scaleX = 2.0;
resIndicator.scaleY = 2.0;
self.towerType = 'resurrection';
self.health = 150;
self.maxHealth = 150;
self.range = 250; // Resurrection range
self.resurrectionCooldown = 0;
self.cost = 200;
self.canUpgrade = function () {
return false; // Resurrection towers cannot be upgraded
};
self.getUpgradeCost = function () {
return 0; // Resurrection towers cannot be upgraded
};
self.update = function () {
// Only attempt resurrection when not on cooldown
if (self.resurrectionCooldown <= 0) {
// Look for empty slots within range that used to have towers
var bestSlot = null;
var bestPriority = 0;
for (var i = 0; i < towerSlots.length; i++) {
var slot = towerSlots[i];
if (!slot.occupied && slot.lastTowerType) {
// Check if slot is within resurrection range
var distance = Math.sqrt(Math.pow(slot.x - self.x, 2) + Math.pow(slot.y - self.y, 2));
if (distance <= self.range) {
// Calculate priority based on tower type and nearby units
var priority = 1;
var nearbyUnits = 0;
for (var j = 0; j < playerUnits.length; j++) {
var unit = playerUnits[j];
var unitDistance = Math.sqrt(Math.pow(unit.x - slot.x, 2) + Math.pow(unit.y - slot.y, 2));
if (unitDistance <= 300) {
nearbyUnits++;
}
}
// Higher priority for slots with nearby enemies
priority += nearbyUnits * 2;
// Higher priority for certain tower types
if (slot.lastTowerType === 'basic' || slot.lastTowerType === 'fast') {
priority += 3;
} else if (slot.lastTowerType === 'heavy' || slot.lastTowerType === 'sniper') {
priority += 5;
}
if (priority > bestPriority) {
bestPriority = priority;
bestSlot = slot;
}
}
}
}
// Resurrect tower at best slot
if (bestSlot) {
var newTower = null;
// Recreate the tower based on its last type
switch (bestSlot.lastTowerType) {
case 'basic':
newTower = new BasicTower();
break;
case 'fast':
newTower = new FastTower();
break;
case 'heavy':
newTower = new HeavyTower();
break;
case 'sniper':
newTower = new SniperTower();
break;
case 'slow':
newTower = new SlowTower();
break;
case 'poison':
newTower = new PoisonTower();
break;
case 'laser':
newTower = new LaserTower();
break;
case 'area':
newTower = new AreaTower();
break;
case 'heal':
newTower = new HealTower();
break;
case 'wall':
newTower = new Wall();
break;
case 'trap':
newTower = new Trap();
break;
}
if (newTower) {
// Place the resurrected tower
newTower.x = bestSlot.x;
newTower.y = bestSlot.y;
newTower.slot = bestSlot;
// Resurrect at 50% health
newTower.health = Math.floor(newTower.maxHealth * 0.5);
enemyTowers.push(newTower);
game.addChild(newTower);
bestSlot.occupied = true;
bestSlot.tower = newTower;
bestSlot.attachAsset('towerSlot', {}).alpha = 0.1;
// Clear the resurrection memory
bestSlot.lastTowerType = null;
// Initialize health bar
if (newTower.updateHealthBar) {
newTower.updateHealthBar();
}
// Visual resurrection effect
tween(resIndicator, {
scaleX: 3.5,
scaleY: 3.5,
alpha: 0.3
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(resIndicator, {
scaleX: 2.0,
scaleY: 2.0,
alpha: 1
}, {
duration: 300,
easing: tween.easeIn
});
}
});
LK.effects.flashObject(newTower, 0xFFD700, 800);
LK.effects.flashObject(self, 0xFFD700, 400);
self.resurrectionCooldown = 1200; // 20 second cooldown
}
}
}
if (self.resurrectionCooldown > 0) {
self.resurrectionCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var SlowTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('slowTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
towerGraphics.tint = 0x9932CC; // Purple color for slow tower
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -50;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -50;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
// Add slow effect indicator
var slowIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
slowIndicator.x = 0;
slowIndicator.y = 0;
slowIndicator.tint = 0x9932CC; // Purple indicator
slowIndicator.scaleX = 1.2;
slowIndicator.scaleY = 1.2;
self.towerType = 'slow';
self.health = 90;
self.maxHealth = 90;
self.range = 200; // Large range for slowing
self.damage = 10; // Low damage
self.slowEffect = 0.5; // Reduces speed by 50%
self.slowDuration = 300; // 5 seconds at 60fps
self.shootCooldown = 0;
self.target = null;
self.cost = 85;
self.canUpgrade = function () {
return false; // Slow towers cannot be upgraded
};
self.getUpgradeCost = function () {
return 0; // Slow towers cannot be upgraded
};
self.update = function () {
// Find target
if (!self.target || !self.target.parent) {
self.target = null;
var closestUnit = null;
var closestDistance = Infinity;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.range && distance < closestDistance) {
closestDistance = distance;
closestUnit = unit;
}
}
self.target = closestUnit;
}
// Apply slow effect and light damage
if (self.target && self.shootCooldown <= 0) {
// Apply slow effect
if (!self.target.originalSpeed) {
self.target.originalSpeed = self.target.speed;
}
self.target.speed = self.target.originalSpeed * self.slowEffect;
self.target.slowEndTime = LK.ticks + self.slowDuration;
// Light damage
self.target.takeDamage(self.damage);
// Visual effects
LK.effects.flashObject(self.target, 0x9932CC, 300);
tween(slowIndicator, {
scaleX: 1.8,
scaleY: 1.8
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(slowIndicator, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeIn
});
}
});
LK.getSound('shoot').play();
self.shootCooldown = 60; // 1 second cooldown
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var SniperTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('sniperTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -55;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -55;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
self.towerType = 'sniper';
self.health = 120;
self.maxHealth = 120;
self.range = 300; // Very long range
self.damage = 40;
self.shootCooldown = 0;
self.target = null;
self.cost = 125;
// Upgrade indicator (initially hidden)
var upgradeIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
upgradeIndicator.x = 30;
upgradeIndicator.y = -35;
upgradeIndicator.visible = false;
self.level = 1;
self.maxLevel = 3;
self.baseRange = 300;
self.baseDamage = 40;
self.criticalChance = 0; // Chance for critical hits
self.pierceCount = 0; // Number of units bullet can pierce through
self.range = self.baseRange;
self.damage = self.baseDamage;
self.updateStats = function () {
var multiplier = 1 + (self.level - 1) * 0.7; // 70% increase per level
self.range = Math.floor(self.baseRange * multiplier);
self.damage = Math.floor(self.baseDamage * multiplier);
self.maxHealth = Math.floor(120 * multiplier);
self.health = Math.min(self.health, self.maxHealth);
self.criticalChance = (self.level - 1) * 0.25; // 25% crit chance per level above 1
self.pierceCount = self.level - 1; // Pierce through multiple enemies
// Special abilities for max level
if (self.level === 3) {
self.executeThreshold = 0.25; // Execute enemies below 25% health
}
// Update visual appearance based on level
if (self.level === 2) {
towerGraphics.tint = 0x708090; // Slate gray
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xffd700; // Gold
} else if (self.level === 3) {
towerGraphics.tint = 0x2F4F4F; // Dark slate gray
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xff4500; // Orange red
}
};
self.canUpgrade = function () {
return self.level < self.maxLevel;
};
self.getUpgradeCost = function () {
return 80 + (self.level - 1) * 50; // Cost increases with level
};
self.upgrade = function () {
if (self.canUpgrade()) {
self.level++;
self.updateStats();
LK.effects.flashObject(self, 0xffd700, 500);
return true;
}
return false;
};
self.update = function () {
// Find target (prioritize units furthest away)
if (!self.target || !self.target.parent) {
self.target = null;
var bestUnit = null;
var longestDistance = 0;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.range && distance > longestDistance) {
longestDistance = distance;
bestUnit = unit;
}
}
self.target = bestUnit;
}
// Shoot at target with precision, critical hits, and piercing
if (self.target && self.shootCooldown <= 0) {
var bullet = new TowerBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = self.target;
// Check for critical hit
var isCritical = Math.random() < self.criticalChance;
bullet.damage = isCritical ? self.damage * 2 : self.damage;
// Execute ability for level 3 - instantly kill low health enemies
if (self.level === 3) {
var targetHealthPercent = self.target.health / self.target.maxHealth;
if (targetHealthPercent <= self.executeThreshold) {
bullet.execute = true; // Mark bullet for execution
bullet.damage = self.target.health + 1000; // Ensure instant kill
LK.effects.flashObject(self, 0xFF0000, 500); // Red flash for execute
}
}
bullet.speed = 10; // Faster bullet
bullet.pierceCount = self.pierceCount; // Add pierce capability
bullet.pierceRemaining = self.pierceCount;
if (isCritical) {
// Visual effect for critical hit
LK.effects.flashObject(self, 0xFFD700, 300);
}
towerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 150; // 2.5 second cooldown
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var SniperUnit = Container.expand(function () {
var self = Container.call(this);
var unitGraphics = self.attachAsset('sniperUnitAsset', {
anchorX: 0.5,
anchorY: 0.5
});
unitGraphics.tint = 0x4B0082; // Indigo color for sniper unit
unitGraphics.scaleX = 1.0;
unitGraphics.scaleY = 1.3; // Taller appearance
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -42;
healthBarBg.scaleX = 0.9;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -42;
healthBar.scaleX = 0.9;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent * 0.9;
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00;
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00;
} else {
healthBar.tint = 0xff0000;
}
};
self.unitType = 'sniper';
self.health = 50;
self.maxHealth = 50;
self.speed = 1.2;
self.damage = 35;
self.range = 250; // Very long range
self.cost = 80;
self.shootCooldown = 0;
self.target = null;
self.lastX = 0;
self.lastY = 0;
self.currentWaypoint = 0;
self.isMovingToWaypoint = false;
// Create path through tower grid
var pathWaypoints = [];
pathWaypoints.push({
x: 400,
y: self.y
});
// Tower grid parameters
var gridStartX = 800;
var gridStartY = 600;
var gridSpacingX = 200;
var gridSpacingY = 200;
var gridRows = 8;
var gridCols = 4;
// Serpentine path through towers
for (var col = 0; col < gridCols; col++) {
if (col % 2 === 0) {
for (var row = 0; row < gridRows; row++) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
} else {
for (var row = gridRows - 1; row >= 0; row--) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
}
}
pathWaypoints.push({
x: 1600,
y: 1000
});
self.pathWaypoints = pathWaypoints;
self.followPath = function () {
if (self.isMovingToWaypoint) return;
if (self.currentWaypoint >= self.pathWaypoints.length) {
self.x += self.speed;
return;
}
var waypoint = self.pathWaypoints[self.currentWaypoint];
var distance = Math.sqrt(Math.pow(waypoint.x - self.x, 2) + Math.pow(waypoint.y - self.y, 2));
var duration = distance / self.speed * 16.67;
self.isMovingToWaypoint = true;
tween(self, {
x: waypoint.x,
y: waypoint.y
}, {
duration: duration,
easing: tween.linear,
onFinish: function onFinish() {
self.currentWaypoint++;
self.isMovingToWaypoint = false;
}
});
};
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
if (!self.target || !self.target.parent) {
self.target = null;
var closestTarget = null;
var closestDistance = Infinity;
// Prioritize towers over base due to long range
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower && tower.parent) {
var towerDistance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (towerDistance <= self.range && towerDistance < closestDistance) {
closestDistance = towerDistance;
closestTarget = tower;
}
}
}
// Check base if no towers in range
if (!closestTarget && enemyBase && enemyBase.parent) {
var baseDistance = Math.sqrt(Math.pow(enemyBase.x - self.x, 2) + Math.pow(enemyBase.y - self.y, 2));
if (baseDistance <= self.range) {
closestTarget = enemyBase;
closestDistance = baseDistance;
}
}
self.target = closestTarget;
}
if (self.target && self.shootCooldown <= 0) {
var bullet = new UnitBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = self.target;
bullet.damage = self.damage;
bullet.speed = 12; // Faster bullet
unitBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 120; // Slow shooting rate
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
if (!self.target) {
self.followPath();
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
return true;
}
return false;
};
return self;
});
var TowerBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('towerBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.target = null;
self.damage = 30;
self.update = function () {
if (!self.target || !self.target.parent) {
// Target destroyed, remove bullet
return;
}
// 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 < 10) {
// Hit target
if (self.target.takeDamage) {
self.target.takeDamage(self.damage);
LK.getSound('hit').play();
// Handle splash damage
if (self.splashRadius && self.splashRadius > 0) {
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
if (unit !== self.target && unit.parent) {
var splashDistance = Math.sqrt(Math.pow(unit.x - self.target.x, 2) + Math.pow(unit.y - self.target.y, 2));
if (splashDistance <= self.splashRadius) {
unit.takeDamage(self.splashDamage || Math.floor(self.damage * 0.6));
LK.effects.flashObject(unit, 0xFF6600, 200);
}
}
}
// Visual splash effect
LK.effects.flashObject(self.target, 0xFF6600, 400);
}
}
// Handle piercing
if (self.pierceRemaining && self.pierceRemaining > 0) {
self.pierceRemaining--;
// Find next target in line
var nextTarget = null;
var minDistance = Infinity;
var bulletDirection = {
x: (self.target.x - self.x) / distance,
y: (self.target.y - self.y) / distance
};
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
if (unit !== self.target && unit.parent) {
var unitDistance = Math.sqrt(Math.pow(unit.x - self.target.x, 2) + Math.pow(unit.y - self.target.y, 2));
// Check if unit is roughly in the same direction
var unitDirection = {
x: (unit.x - self.target.x) / unitDistance,
y: (unit.y - self.target.y) / unitDistance
};
var dotProduct = bulletDirection.x * unitDirection.x + bulletDirection.y * unitDirection.y;
if (dotProduct > 0.7 && unitDistance < minDistance && unitDistance < 150) {
// Within piercing range
minDistance = unitDistance;
nextTarget = unit;
}
}
}
if (nextTarget) {
self.target = nextTarget;
// Continue moving towards new target
return;
}
}
return; // Bullet will be removed
}
// Normalize and move
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
};
return self;
});
var TowerSlot = Container.expand(function () {
var self = Container.call(this);
var slotGraphics = self.attachAsset('towerSlot', {
anchorX: 0.5,
anchorY: 0.5
});
slotGraphics.alpha = 0.3;
self.occupied = false;
self.tower = null;
self.placeTower = function () {
if (!self.occupied) {
var tower = new EnemyTower();
tower.x = self.x;
tower.y = self.y;
tower.slot = self;
tower.updateStats(); // Initialize stats properly
enemyTowers.push(tower);
game.addChild(tower);
self.occupied = true;
self.tower = tower;
slotGraphics.alpha = 0.1;
return tower;
}
return null;
};
self.removeTower = function () {
if (self.occupied && self.tower) {
self.occupied = false;
self.tower = null;
slotGraphics.alpha = 0.3;
}
};
return self;
});
var Trap = Container.expand(function () {
var self = Container.call(this);
var trapGraphics = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
trapGraphics.tint = 0x8B4513; // Brown color for trap
trapGraphics.scaleX = 0.8;
trapGraphics.scaleY = 0.8;
trapGraphics.alpha = 0.6; // Semi-transparent to show it's hidden
// Upgrade indicator (initially hidden)
var upgradeIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
upgradeIndicator.x = 15;
upgradeIndicator.y = -15;
upgradeIndicator.visible = false;
self.towerType = 'trap';
self.level = 1;
self.maxLevel = 3;
self.health = 1; // Traps are destroyed after one use
self.maxHealth = 1;
self.baseDamage = 60;
self.baseRange = 40;
self.damage = self.baseDamage; // High damage when triggered
self.range = self.baseRange; // Detection range for triggering
self.triggered = false;
self.cost = 50;
self.updateStats = function () {
var multiplier = 1 + (self.level - 1) * 0.8; // 80% increase per level
self.damage = Math.floor(self.baseDamage * multiplier);
self.range = Math.floor(self.baseRange * multiplier);
// Update visual appearance based on level
if (self.level === 2) {
trapGraphics.tint = 0xA0522D; // Darker brown
trapGraphics.scaleX = 1.0;
trapGraphics.scaleY = 1.0;
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xffd700; // Gold
} else if (self.level === 3) {
trapGraphics.tint = 0x8B0000; // Dark red
trapGraphics.scaleX = 1.2;
trapGraphics.scaleY = 1.2;
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xff4500; // Orange red
}
};
self.canUpgrade = function () {
return self.level < self.maxLevel && !self.triggered;
};
self.getUpgradeCost = function () {
return 30 + (self.level - 1) * 20; // Cost increases with level
};
self.upgrade = function () {
if (self.canUpgrade()) {
self.level++;
self.updateStats();
LK.effects.flashObject(self, 0xffd700, 500);
return true;
}
return false;
};
self.update = function () {
if (self.triggered) return; // Already triggered
// Check for player units in range
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.range) {
// Trigger trap
self.triggered = true;
unit.takeDamage(self.damage);
// Visual explosion effect
LK.effects.flashObject(self, 0xFF6600, 400);
LK.effects.flashObject(unit, 0xFF0000, 300);
// Create explosion visual
trapGraphics.tint = 0xFF6600;
trapGraphics.alpha = 1.0;
tween(trapGraphics, {
scaleX: 2.0,
scaleY: 2.0,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut
});
LK.getSound('hit').play();
return true; // Trap is destroyed
}
}
};
self.takeDamage = function (damage) {
// Traps take minimal damage from ranged attacks
self.health -= Math.max(1, Math.floor(damage * 0.1));
if (self.health <= 0) {
return true; // Trap is destroyed
}
return false;
};
return self;
});
var UnitBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('unitBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.target = null;
self.damage = 25;
self.update = function () {
if (!self.target || !self.target.parent) {
// Target destroyed, remove bullet
return;
}
// 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 < 10) {
// Hit target
if (self.target.takeDamage) {
self.target.takeDamage(self.damage);
LK.getSound('hit').play();
}
return; // Bullet will be removed
}
// Normalize and move
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
};
return self;
});
var Wall = Container.expand(function () {
var self = Container.call(this);
var wallGraphics = self.attachAsset('basicTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
wallGraphics.tint = 0x696969; // Gray color for wall
wallGraphics.scaleX = 1.2;
wallGraphics.scaleY = 0.8; // Make it look more like a wall
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -40;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -40;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
// Upgrade indicator (initially hidden)
var upgradeIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
upgradeIndicator.x = 25;
upgradeIndicator.y = -25;
upgradeIndicator.visible = false;
self.towerType = 'wall';
self.level = 1;
self.maxLevel = 3;
self.baseHealth = 300;
self.baseBlockRadius = 60;
self.health = self.baseHealth; // High health to absorb damage
self.maxHealth = self.baseHealth;
self.blockRadius = self.baseBlockRadius; // Radius that blocks unit movement
self.cost = 40;
self.updateStats = function () {
var multiplier = 1 + (self.level - 1) * 0.6; // 60% increase per level
self.maxHealth = Math.floor(self.baseHealth * multiplier);
self.health = Math.min(self.health, self.maxHealth);
self.blockRadius = Math.floor(self.baseBlockRadius * multiplier);
// Update visual appearance based on level
if (self.level === 2) {
wallGraphics.tint = 0x808080; // Darker gray
wallGraphics.scaleX = 1.4;
wallGraphics.scaleY = 1.0;
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xffd700; // Gold
} else if (self.level === 3) {
wallGraphics.tint = 0x2F4F4F; // Dark slate gray
wallGraphics.scaleX = 1.6;
wallGraphics.scaleY = 1.2;
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xff4500; // Orange red
}
};
self.canUpgrade = function () {
return self.level < self.maxLevel;
};
self.getUpgradeCost = function () {
return 25 + (self.level - 1) * 15; // Cost increases with level
};
self.upgrade = function () {
if (self.canUpgrade()) {
self.level++;
self.updateStats();
LK.effects.flashObject(self, 0xffd700, 500);
return true;
}
return false;
};
self.update = function () {
// Mine ability for level 3
if (self.level === 3 && self.mineCooldown <= 0) {
// Check if any enemies are touching the wall
var enemiesInContact = 0;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.blockRadius + 20) {
enemiesInContact++;
}
}
// Trigger mine explosion if enemies are touching the wall
if (enemiesInContact > 0) {
// Damage all enemies within mine radius
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.mineRadius) {
unit.takeDamage(self.mineDamage);
LK.effects.flashObject(unit, 0xFF6600, 400);
}
}
// Visual mine explosion effect
wallGraphics.tint = 0xFF6600; // Orange explosion color
LK.effects.flashObject(self, 0xFF6600, 600);
// Screen flash effect for mine explosion
LK.effects.flashScreen(0xFF6600, 200);
self.mineCooldown = 600; // 10 second cooldown
// Return to normal color after effect
var self_ref = self;
LK.setTimeout(function () {
self_ref.wallGraphics.tint = 0x2F4F4F; // Back to level 3 color
}, 600);
}
}
// Update mine cooldown
if (self.mineCooldown > 0) {
self.mineCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Wall is destroyed
}
return false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2d5016
});
/****
* Game Code
****/
// Game arrays to track entities
// Initialize game assets
var playerUnits = [];
var enemyTowers = [];
var unitBullets = [];
var towerBullets = [];
var towerSlots = [];
var enemyBase;
// Game resources
var coins = 100;
var unitCost = 250;
// Ability system
var abilities = {
lightning: {
name: 'Lightning Strike',
cooldown: 900,
// 15 seconds
currentCooldown: 0,
description: 'Strikes all towers with lightning'
},
heal: {
name: 'Mass Heal',
cooldown: 600,
// 10 seconds
currentCooldown: 0,
description: 'Heals all your units to full health'
},
freeze: {
name: 'Time Freeze',
cooldown: 1200,
// 20 seconds
currentCooldown: 0,
description: 'Freezes all towers for 5 seconds'
},
boost: {
name: 'Unit Boost',
cooldown: 480,
// 8 seconds
currentCooldown: 0,
description: 'Doubles unit damage for 8 seconds'
}
};
// UI Elements
var coinsText = new Text2('Coins: ' + coins, {
size: 80,
fill: 0xFFD700
});
coinsText.anchor.set(1, 0);
coinsText.x = -20; // Small margin from right edge
coinsText.y = 20; // Small margin from top
LK.gui.topRight.addChild(coinsText);
var coinIcon = LK.getAsset('coinIcon', {
anchorX: 0.5,
anchorY: 0.5
});
coinIcon.x = -coinsText.width - 60; // Position to left of text
coinIcon.y = 45; // Align with text
LK.gui.topRight.addChild(coinIcon);
var healthText = new Text2('Base Health: 500', {
size: 50,
fill: 0xFFFFFF
});
healthText.anchor.set(0.5, 0);
LK.gui.top.addChild(healthText);
// Unit spawn buttons
var basicButton = new Text2('Basic (300)', {
size: 40,
fill: 0x87CEEB
});
basicButton.anchor.set(0.5, 1);
basicButton.x = -300;
basicButton.interactive = true;
basicButton.down = function () {
if (coins >= 300) {
spawnUnit('basic');
}
};
LK.gui.bottom.addChild(basicButton);
var fastButton = new Text2('Fast (450)', {
size: 40,
fill: 0x32CD32
});
fastButton.anchor.set(0.5, 1);
fastButton.x = -100;
fastButton.interactive = true;
fastButton.down = function () {
if (coins >= 450) {
spawnUnit('fast');
}
};
LK.gui.bottom.addChild(fastButton);
var heavyButton = new Text2('Heavy (650)', {
size: 40,
fill: 0x8B4513
});
heavyButton.anchor.set(0.5, 1);
heavyButton.x = 100;
heavyButton.interactive = true;
heavyButton.down = function () {
if (coins >= 650) {
spawnUnit('heavy');
}
};
LK.gui.bottom.addChild(heavyButton);
var sniperButton = new Text2('Sniper (600)', {
size: 40,
fill: 0x4B0082
});
sniperButton.anchor.set(0.5, 1);
sniperButton.x = 200;
sniperButton.interactive = true;
sniperButton.down = function () {
if (coins >= 600) {
spawnUnit('sniper');
}
};
LK.gui.bottom.addChild(sniperButton);
var meleeButton = new Text2('Melee (250)', {
size: 40,
fill: 0xFF6347
});
meleeButton.anchor.set(0.5, 1);
meleeButton.x = 400;
meleeButton.interactive = true;
meleeButton.down = function () {
if (coins >= 250) {
spawnUnit('melee');
}
};
LK.gui.bottom.addChild(meleeButton);
// Ability buttons
var lightningButton = new Text2('Lightning', {
size: 35,
fill: 0xFFFF00
});
lightningButton.anchor.set(0, 0.5);
lightningButton.x = 20;
lightningButton.y = -200;
lightningButton.interactive = true;
lightningButton.down = function () {
useAbility('lightning');
};
LK.gui.left.addChild(lightningButton);
var healButton = new Text2('Heal All', {
size: 35,
fill: 0x00FF00
});
healButton.anchor.set(0, 0.5);
healButton.x = 20;
healButton.y = -100;
healButton.interactive = true;
healButton.down = function () {
useAbility('heal');
};
LK.gui.left.addChild(healButton);
var freezeButton = new Text2('Freeze', {
size: 35,
fill: 0x00FFFF
});
freezeButton.anchor.set(0, 0.5);
freezeButton.x = 20;
freezeButton.y = 0;
freezeButton.interactive = true;
freezeButton.down = function () {
useAbility('freeze');
};
LK.gui.left.addChild(freezeButton);
var boostButton = new Text2('Boost', {
size: 35,
fill: 0xFF8C00
});
boostButton.anchor.set(0, 0.5);
boostButton.x = 20;
boostButton.y = 100;
boostButton.interactive = true;
boostButton.down = function () {
useAbility('boost');
};
LK.gui.left.addChild(boostButton);
// Create enemy base
enemyBase = game.addChild(new EnemyBase());
enemyBase.x = 1800;
enemyBase.y = 2732 / 2;
// Create tower slots in a grid
var gridStartX = 800;
var gridStartY = 600;
var gridSpacingX = 200;
var gridSpacingY = 200;
var gridRows = 8;
var gridCols = 4;
for (var row = 0; row < gridRows; row++) {
for (var col = 0; col < gridCols; col++) {
var slot = new TowerSlot();
slot.x = gridStartX + col * gridSpacingX;
slot.y = gridStartY + row * gridSpacingY;
towerSlots.push(slot);
game.addChild(slot);
}
}
// Initially place some towers in random slots
var initialTowerCount = 8;
var placedTowers = 0;
while (placedTowers < initialTowerCount && placedTowers < towerSlots.length) {
var randomSlotIndex = Math.floor(Math.random() * towerSlots.length);
var slot = towerSlots[randomSlotIndex];
if (slot.placeTower()) {
// Initialize health bar for the newly placed tower
if (slot.tower && slot.tower.updateHealthBar) {
slot.tower.updateHealthBar();
}
placedTowers++;
}
}
function useAbility(abilityType) {
var ability = abilities[abilityType];
if (!ability || ability.currentCooldown > 0) {
return false;
}
ability.currentCooldown = ability.cooldown;
// Enhanced visual feedback - pulse effect on ability button and coins
switch (abilityType) {
case 'lightning':
// Enhanced lightning visual effects
tween(lightningButton, {
scaleX: 1.3,
scaleY: 1.3,
alpha: 0.7
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(lightningButton, {
scaleX: 1.0,
scaleY: 1.0,
alpha: 1.0
}, {
duration: 300,
easing: tween.bounceOut
});
}
});
// Strike all towers with lightning
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
tower.takeDamage(80);
LK.effects.flashObject(tower, 0xFFFF00, 500);
// Add scaling effect to towers when hit by lightning
tween(tower, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(tower, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.elasticOut
});
}
});
}
// Strike the base too
if (enemyBase) {
enemyBase.takeDamage(120);
LK.effects.flashObject(enemyBase, 0xFFFF00, 800);
tween(enemyBase, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(enemyBase, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.elasticOut
});
}
});
}
LK.effects.flashScreen(0xFFFF00, 300);
break;
case 'heal':
// Enhanced heal visual effects
tween(healButton, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0x00FFFF
}, {
duration: 250,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(healButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0x00FF00
}, {
duration: 400,
easing: tween.bounceOut
});
}
});
// Heal all units to full health
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
unit.health = unit.maxHealth;
unit.updateHealthBar();
LK.effects.flashObject(unit, 0x00FF00, 400);
// Add pulsing effect to healed units
tween(unit, {
scaleX: 1.15,
scaleY: 1.15,
alpha: 0.8
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(unit, {
scaleX: 1.0,
scaleY: 1.0,
alpha: 1.0
}, {
duration: 300,
easing: tween.bounceOut
});
}
});
}
LK.effects.flashScreen(0x00FF00, 200);
break;
case 'freeze':
// Enhanced freeze visual effects
tween(freezeButton, {
scaleX: 1.4,
scaleY: 1.4,
rotation: 0.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(freezeButton, {
scaleX: 1.0,
scaleY: 1.0,
rotation: 0
}, {
duration: 500,
easing: tween.elasticOut
});
}
});
// Freeze all towers for 5 seconds
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
tower.frozenUntil = LK.ticks + 300; // 5 seconds
tower.originalTint = tower.tint || 0xFFFFFF;
LK.effects.flashObject(tower, 0x00FFFF, 600);
// Add shaking effect to frozen towers
tween(tower, {
x: tower.x + 5,
y: tower.y + 5
}, {
duration: 100,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(tower, {
x: tower.x - 5,
y: tower.y - 5
}, {
duration: 100,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(tower, {
x: tower.x,
y: tower.y
}, {
duration: 100,
easing: tween.easeOut
});
}
});
}
});
}
// Freeze the base too
if (enemyBase) {
enemyBase.frozenUntil = LK.ticks + 300;
LK.effects.flashObject(enemyBase, 0x00FFFF, 600);
}
LK.effects.flashScreen(0x00FFFF, 400);
break;
case 'boost':
// Enhanced boost visual effects
tween(boostButton, {
scaleX: 1.5,
scaleY: 1.5,
tint: 0xFFD700
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(boostButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFF8C00
}, {
duration: 400,
easing: tween.bounceOut
});
}
});
// Double unit damage for 8 seconds
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
if (!unit.boostedUntil) {
unit.originalDamage = unit.damage;
}
unit.damage = unit.originalDamage * 2;
unit.boostedUntil = LK.ticks + 480; // 8 seconds
LK.effects.flashObject(unit, 0xFF8C00, 500);
// Add growing effect to boosted units
tween(unit, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFFD700
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(unit, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 200,
easing: tween.easeInOut
});
}
});
}
LK.effects.flashScreen(0xFF8C00, 300);
break;
}
return true;
}
function spawnUnit(unitType) {
if (!unitType) unitType = 'basic';
var unitClass = null;
var cost = 40;
switch (unitType) {
case 'basic':
unitClass = BasicUnit;
cost = 300;
break;
case 'fast':
unitClass = FastUnit;
cost = 450;
break;
case 'heavy':
unitClass = HeavyUnit;
cost = 650;
break;
case 'sniper':
unitClass = SniperUnit;
cost = 600;
break;
case 'melee':
unitClass = MeleeUnit;
cost = 250;
break;
default:
unitClass = BasicUnit;
cost = 200;
break;
}
if (coins >= cost && unitClass) {
coins -= cost;
coinsText.setText('Coins: ' + coins);
coinIcon.x = -coinsText.width - 60;
// Spawn 5 units
for (var unitIndex = 0; unitIndex < 5; unitIndex++) {
var unit = new unitClass();
unit.x = 100;
unit.y = 500 + (playerUnits.length + unitIndex) % 8 * 100;
// Create path that goes through all tower positions
var pathWaypoints = [];
// Start from spawn position
pathWaypoints.push({
x: 400,
y: unit.y
});
// Add waypoints to pass through tower grid
var gridStartX = 800;
var gridStartY = 600;
var gridSpacingX = 200;
var gridSpacingY = 200;
var gridRows = 8;
var gridCols = 4;
// Create serpentine path through tower grid
for (var col = 0; col < gridCols; col++) {
if (col % 2 === 0) {
// Go down for even columns
for (var row = 0; row < gridRows; row++) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
} else {
// Go up for odd columns
for (var row = gridRows - 1; row >= 0; row--) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
}
}
// Final waypoint towards the base
pathWaypoints.push({
x: 1600,
y: 1366
});
unit.pathWaypoints = pathWaypoints;
playerUnits.push(unit);
game.addChild(unit);
unit.updateHealthBar();
}
LK.getSound('unitSpawn').play();
}
}
// Coin generation timer
var coinTimer = 0;
// Enemy AI system
var enemyAI = {
coins: 400,
towerCost: 75,
lastTowerPlacement: 0,
placementCooldown: 180,
// 3 seconds at 60fps
update: function update() {
// Generate coins for enemy AI
if (LK.ticks % 120 === 0) {
// Every 2 seconds
this.coins += 75;
}
// Force spending if AI has too many coins
var forceSpend = this.coins >= 1000;
// Check if should place new towers
if (this.coins >= this.towerCost && LK.ticks - this.lastTowerPlacement > this.placementCooldown || forceSpend) {
this.considerTowerPlacement();
}
// Upgrade existing towers (with forced spending)
this.considerTowerUpgrades(forceSpend);
// Repair damaged towers
this.repairTowers();
},
considerTowerPlacement: function considerTowerPlacement() {
// First priority: Check if we need heal towers near damaged towers
var needsHealTower = false;
var healSlot = null;
var damagedTowerNeedingHeal = null;
// Find damaged towers that need healing
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower.health < tower.maxHealth * 0.6) {
// If tower is below 60% health
// Check if there's already a heal tower nearby
var hasHealerNearby = false;
for (var j = 0; j < enemyTowers.length; j++) {
var otherTower = enemyTowers[j];
if (otherTower.towerType === 'heal') {
var distance = Math.sqrt(Math.pow(tower.x - otherTower.x, 2) + Math.pow(tower.y - otherTower.y, 2));
if (distance <= 200) {
// Within healing range
hasHealerNearby = true;
break;
}
}
}
if (!hasHealerNearby) {
damagedTowerNeedingHeal = tower;
needsHealTower = true;
break;
}
}
}
// If we need a heal tower and can afford it, find best slot near damaged tower
if (needsHealTower && damagedTowerNeedingHeal && this.coins >= 110) {
var bestDistance = Infinity;
var forceSpendHeal = this.coins >= 1000;
for (var i = 0; i < towerSlots.length; i++) {
var slot = towerSlots[i];
if (!slot.occupied) {
// Check if there are any units too close to this heal slot (within 120 pixels)
var hasUnitsNearbyHeal = false;
for (var k = 0; k < playerUnits.length; k++) {
var unit = playerUnits[k];
var unitDistance = Math.sqrt(Math.pow(unit.x - slot.x, 2) + Math.pow(unit.y - slot.y, 2));
if (unitDistance < 120) {
// Don't build heal tower if units are too close
hasUnitsNearbyHeal = true;
break;
}
}
// Skip this slot if units are too close (unless forced to spend)
if (hasUnitsNearbyHeal && !forceSpendHeal) {
continue;
}
var distance = Math.sqrt(Math.pow(damagedTowerNeedingHeal.x - slot.x, 2) + Math.pow(damagedTowerNeedingHeal.y - slot.y, 2));
if (distance < bestDistance && distance <= 200) {
// Within heal range
bestDistance = distance;
healSlot = slot;
}
}
}
// Place heal tower if we found a good slot
if (healSlot) {
var tower = new HealTower();
tower.x = healSlot.x;
tower.y = healSlot.y;
tower.slot = healSlot;
enemyTowers.push(tower);
game.addChild(tower);
healSlot.occupied = true;
healSlot.tower = tower;
healSlot.attachAsset('towerSlot', {}).alpha = 0.1;
this.coins -= 110;
this.lastTowerPlacement = LK.ticks;
// Initialize health bar
if (tower.updateHealthBar) {
tower.updateHealthBar();
}
// Visual feedback
LK.effects.flashObject(healSlot, 0x00ff00, 500);
return; // Exit early after placing heal tower
}
}
// Second priority: Find empty slots closest to player units for combat towers
var bestSlot = null;
var minDistance = Infinity;
var forceSpend = this.coins >= 1000;
for (var i = 0; i < towerSlots.length; i++) {
var slot = towerSlots[i];
if (!slot.occupied) {
// Check if there are any units too close to this slot (within 120 pixels)
var hasUnitsNearby = false;
for (var k = 0; k < playerUnits.length; k++) {
var unit = playerUnits[k];
var unitDistance = Math.sqrt(Math.pow(unit.x - slot.x, 2) + Math.pow(unit.y - slot.y, 2));
if (unitDistance < 120) {
// Don't build if units are too close
hasUnitsNearby = true;
break;
}
}
// Skip this slot if units are too close (unless forced to spend)
if (hasUnitsNearby && !forceSpend) {
continue;
}
// Calculate average distance to all player units
var totalDistance = 0;
var unitCount = 0;
for (var j = 0; j < playerUnits.length; j++) {
var unit = playerUnits[j];
var distance = Math.sqrt(Math.pow(unit.x - slot.x, 2) + Math.pow(unit.y - slot.y, 2));
totalDistance += distance;
unitCount++;
}
if (unitCount > 0) {
var avgDistance = totalDistance / unitCount;
// Force placement if we have too many coins, otherwise use distance restriction
if (avgDistance < minDistance && (avgDistance < 400 || forceSpend)) {
minDistance = avgDistance;
bestSlot = slot;
}
} else if (forceSpend && !bestSlot) {
// If forced to spend and no players around, pick any empty slot
bestSlot = slot;
}
}
}
// Place tower at best location
if (bestSlot) {
// Strategic placement: Check if we should place walls or traps based on unit density
var nearbyUnits = 0;
var averageUnitDistance = 0;
for (var k = 0; k < playerUnits.length; k++) {
var unit = playerUnits[k];
var unitDist = Math.sqrt(Math.pow(unit.x - bestSlot.x, 2) + Math.pow(unit.y - bestSlot.y, 2));
if (unitDist <= 300) {
nearbyUnits++;
averageUnitDistance += unitDist;
}
}
if (nearbyUnits > 0) {
averageUnitDistance /= nearbyUnits;
}
// Choose tower type based on strategy and available coins
var towerTypes = [];
// Add defensive structures first if units are close
if (nearbyUnits >= 3 && this.coins >= 40) towerTypes.push('wall');
if (nearbyUnits >= 2 && averageUnitDistance < 150 && this.coins >= 50) towerTypes.push('trap');
// Add combat towers
if (this.coins >= 75) towerTypes.push('basic');
if (this.coins >= 85) towerTypes.push('slow');
if (this.coins >= 95) towerTypes.push('poison');
if (this.coins >= 100) towerTypes.push('fast');
if (this.coins >= 125) towerTypes.push('sniper');
if (this.coins >= 130) towerTypes.push('area');
if (this.coins >= 140) towerTypes.push('laser');
if (this.coins >= 150) towerTypes.push('heavy');
if (this.coins >= 200) towerTypes.push('resurrection');
if (towerTypes.length > 0) {
var randomType = towerTypes[Math.floor(Math.random() * towerTypes.length)];
var tower = null;
var cost = 75;
switch (randomType) {
case 'wall':
tower = new Wall();
cost = 40;
break;
case 'trap':
tower = new Trap();
cost = 50;
break;
case 'basic':
tower = new BasicTower();
cost = 75;
break;
case 'slow':
tower = new SlowTower();
cost = 85;
break;
case 'poison':
tower = new PoisonTower();
cost = 95;
break;
case 'fast':
tower = new FastTower();
cost = 100;
break;
case 'sniper':
tower = new SniperTower();
cost = 125;
break;
case 'area':
tower = new AreaTower();
cost = 130;
break;
case 'laser':
tower = new LaserTower();
cost = 140;
break;
case 'heavy':
tower = new HeavyTower();
cost = 150;
break;
case 'resurrection':
tower = new ResurrectionTower();
cost = 200;
break;
}
if (tower) {
tower.x = bestSlot.x;
tower.y = bestSlot.y;
tower.slot = bestSlot;
enemyTowers.push(tower);
game.addChild(tower);
bestSlot.occupied = true;
bestSlot.tower = tower;
bestSlot.attachAsset('towerSlot', {}).alpha = 0.1;
this.coins -= cost;
this.lastTowerPlacement = LK.ticks;
// Initialize health bar for towers that have them
if (tower.updateHealthBar) {
tower.updateHealthBar();
}
// Visual feedback
var feedbackColor = 0xff0000; // Red for combat towers
if (randomType === 'wall') feedbackColor = 0x696969; // Gray for walls
if (randomType === 'trap') feedbackColor = 0x8B4513; // Brown for traps
LK.effects.flashObject(bestSlot, feedbackColor, 500);
}
}
}
},
considerTowerUpgrades: function considerTowerUpgrades(forceSpend) {
// Find towers that can be upgraded and are in combat zones
var upgradeTargets = [];
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower.canUpgrade && tower.canUpgrade()) {
var upgradeCost = tower.getUpgradeCost();
if (this.coins >= upgradeCost) {
// Check if tower is in an active combat zone (has nearby player units)
var nearbyUnits = 0;
var priority = 0;
for (var j = 0; j < playerUnits.length; j++) {
var unit = playerUnits[j];
var distance = Math.sqrt(Math.pow(unit.x - tower.x, 2) + Math.pow(unit.y - tower.y, 2));
// Special range consideration for traps and walls
var checkRange = tower.range + 100;
if (tower.towerType === 'trap') {
checkRange = tower.range + 50; // Shorter range check for traps
} else if (tower.towerType === 'wall') {
checkRange = tower.blockRadius + 80; // Check block radius for walls
}
if (distance <= checkRange) {
nearbyUnits++;
}
}
// Calculate upgrade priority with special consideration for traps and walls
if (nearbyUnits > 0) {
priority = nearbyUnits * 10 + (tower.maxLevel - tower.level + 1) * 5;
// Higher priority for traps and walls in active zones
if (tower.towerType === 'trap' || tower.towerType === 'wall') {
priority += nearbyUnits * 5; // Extra priority for defensive structures
}
} else if (this.coins > upgradeCost * 3 || forceSpend) {
priority = 1; // Low priority for non-combat upgrades
}
if (priority > 0) {
upgradeTargets.push({
tower: tower,
cost: upgradeCost,
priority: priority
});
}
}
}
}
// Sort by priority and upgrade the best candidate
if (upgradeTargets.length > 0) {
upgradeTargets.sort(function (a, b) {
return b.priority - a.priority;
});
var bestTarget = upgradeTargets[0];
if (bestTarget.tower.upgrade && bestTarget.tower.upgrade()) {
this.coins -= bestTarget.cost;
// Visual feedback
LK.effects.flashObject(bestTarget.tower, 0xFFD700, 600);
}
}
},
repairTowers: function repairTowers() {
// Find damaged towers and repair them
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower.health < tower.maxHealth * 0.5 && this.coins >= 25) {
tower.health = Math.min(tower.health + 25, tower.maxHealth);
this.coins -= 25;
LK.effects.flashObject(tower, 0x00ff00, 300);
break; // Only repair one tower per update
}
}
}
};
// Touch controls for spawning units
game.down = function (x, y, obj) {
if (coins >= 300) {
spawnUnit('basic');
}
};
game.update = function () {
// Update enemy AI
enemyAI.update();
// Generate coins over time
coinTimer++;
if (coinTimer >= 180) {
// Every 3 seconds
coins += 50;
coinsText.setText('Coins: ' + coins);
coinIcon.x = -coinsText.width - 60; // Update icon position
coinTimer = 0;
}
// Update unit bullets
for (var i = unitBullets.length - 1; i >= 0; i--) {
var bullet = unitBullets[i];
if (!bullet.target || !bullet.target.parent) {
bullet.destroy();
unitBullets.splice(i, 1);
continue;
}
var dx = bullet.target.x - bullet.x;
var dy = bullet.target.y - bullet.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 15) {
// Hit target
var isDead = bullet.target.takeDamage(bullet.damage);
if (isDead) {
// Remove from appropriate array
if (bullet.target === enemyBase) {
// Player wins!
LK.effects.flashScreen(0x00ff00, 2000);
LK.showYouWin();
return;
} else {
// Remove tower
for (var j = 0; j < enemyTowers.length; j++) {
if (enemyTowers[j] === bullet.target) {
// Calculate money reward based on tower type and level
var moneyReward = 225; // Base reward (75 * 3)
if (enemyTowers[j].towerType) {
switch (enemyTowers[j].towerType) {
case 'wall':
moneyReward = 60 + (enemyTowers[j].level ? (enemyTowers[j].level - 1) * 45 : 0);
break;
case 'trap':
moneyReward = 75 + (enemyTowers[j].level ? (enemyTowers[j].level - 1) * 60 : 0);
break;
case 'slow':
moneyReward = 255;
break;
case 'poison':
moneyReward = 285;
break;
case 'fast':
moneyReward = 300;
break;
case 'heal':
moneyReward = 330;
break;
case 'sniper':
moneyReward = 375;
break;
case 'area':
moneyReward = 390;
break;
case 'laser':
moneyReward = 420;
break;
case 'heavy':
moneyReward = 450;
break;
case 'resurrection':
moneyReward = 600;
break;
default:
moneyReward = 225;
break;
}
} else if (enemyTowers[j].level) {
// For upgradeable towers, reward based on level
moneyReward = 225 + (enemyTowers[j].level - 1) * 150;
}
// Clean up slot if tower has one
if (enemyTowers[j].slot) {
// Remember tower type for potential resurrection
enemyTowers[j].slot.lastTowerType = enemyTowers[j].towerType;
enemyTowers[j].slot.removeTower();
}
enemyTowers[j].destroy();
enemyTowers.splice(j, 1);
coins += moneyReward; // Money reward for destroying tower
coinsText.setText('Coins: ' + coins);
coinIcon.x = -coinsText.width - 60; // Update icon position
break;
}
}
}
}
bullet.destroy();
unitBullets.splice(i, 1);
}
}
// Handle unit status effects (slow, poison)
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
// Handle slow effect restoration
if (unit.slowEndTime && LK.ticks >= unit.slowEndTime) {
if (unit.originalSpeed) {
unit.speed = unit.originalSpeed;
unit.originalSpeed = null;
}
unit.slowEndTime = null;
}
// Handle poison damage over time
if (unit.poisonEndTime && LK.ticks >= unit.poisonEndTime) {
unit.poisonDamage = null;
unit.poisonEndTime = null;
unit.lastPoisonTick = null;
} else if (unit.poisonDamage && unit.lastPoisonTick && LK.ticks - unit.lastPoisonTick >= 60) {
// Apply poison damage every second
unit.takeDamage(unit.poisonDamage);
unit.lastPoisonTick = LK.ticks;
LK.effects.flashObject(unit, 0x228B22, 200);
}
}
// Update tower bullets
for (var i = towerBullets.length - 1; i >= 0; i--) {
var bullet = towerBullets[i];
if (!bullet.target || !bullet.target.parent) {
bullet.destroy();
towerBullets.splice(i, 1);
continue;
}
var dx = bullet.target.x - bullet.x;
var dy = bullet.target.y - bullet.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 15) {
// Hit target
var isDead = bullet.target.takeDamage(bullet.damage);
if (isDead) {
// Remove unit
for (var j = 0; j < playerUnits.length; j++) {
if (playerUnits[j] === bullet.target) {
playerUnits[j].destroy();
playerUnits.splice(j, 1);
break;
}
}
}
bullet.destroy();
towerBullets.splice(i, 1);
}
}
// Lose condition removed - player can no longer lose
// Process trap damage and cleanup
for (var i = enemyTowers.length - 1; i >= 0; i--) {
var tower = enemyTowers[i];
if (tower.towerType === 'trap' && tower.triggered) {
// Clean up slot if tower has one
if (tower.slot) {
tower.slot.removeTower();
}
tower.destroy();
enemyTowers.splice(i, 1);
}
}
// Update base health display
if (enemyBase) {
healthText.setText('Base Health: ' + enemyBase.health);
}
// Update ability cooldowns and button display
for (var abilityName in abilities) {
var ability = abilities[abilityName];
if (ability.currentCooldown > 0) {
ability.currentCooldown--;
}
}
// Update ability button texts and colors
var canUseLightning = abilities.lightning.currentCooldown <= 0;
lightningButton.fill = canUseLightning ? 0xFFFF00 : 0x666666;
lightningButton.setText(abilities.lightning.currentCooldown > 0 ? 'Lightning (' + Math.ceil(abilities.lightning.currentCooldown / 60) + 's)' : 'Lightning');
var canUseHeal = abilities.heal.currentCooldown <= 0;
healButton.fill = canUseHeal ? 0x00FF00 : 0x666666;
healButton.setText(abilities.heal.currentCooldown > 0 ? 'Heal (' + Math.ceil(abilities.heal.currentCooldown / 60) + 's)' : 'Heal All');
var canUseFreeze = abilities.freeze.currentCooldown <= 0;
freezeButton.fill = canUseFreeze ? 0x00FFFF : 0x666666;
freezeButton.setText(abilities.freeze.currentCooldown > 0 ? 'Freeze (' + Math.ceil(abilities.freeze.currentCooldown / 60) + 's)' : 'Freeze');
var canUseBoost = abilities.boost.currentCooldown <= 0;
boostButton.fill = canUseBoost ? 0xFF8C00 : 0x666666;
boostButton.setText(abilities.boost.currentCooldown > 0 ? 'Boost (' + Math.ceil(abilities.boost.currentCooldown / 60) + 's)' : 'Boost');
// Handle frozen towers (disable shooting)
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower.frozenUntil && LK.ticks >= tower.frozenUntil) {
tower.frozenUntil = null;
}
}
// Handle frozen base
if (enemyBase && enemyBase.frozenUntil && LK.ticks >= enemyBase.frozenUntil) {
enemyBase.frozenUntil = null;
}
// Handle unit boost expiration
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
if (unit.boostedUntil && LK.ticks >= unit.boostedUntil) {
unit.damage = unit.originalDamage || unit.damage / 2;
unit.boostedUntil = null;
}
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var AreaTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('areaTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
// Add explosion indicator
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -50;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -50;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
var explosionIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
explosionIndicator.x = 0;
explosionIndicator.y = 0;
explosionIndicator.tint = 0xff0000; // Red explosion symbol
explosionIndicator.scaleX = 1.3;
explosionIndicator.scaleY = 1.3;
self.towerType = 'area';
self.health = 140;
self.maxHealth = 140;
self.range = 180;
self.damage = 25; // Base damage per unit
self.areaRadius = 80; // Area effect radius
self.shootCooldown = 0;
self.target = null;
self.cost = 130;
self.canUpgrade = function () {
return false; // Area towers cannot be upgraded
};
self.getUpgradeCost = function () {
return 0; // Area towers cannot be upgraded
};
self.update = function () {
// Find target (center of enemy group)
if (!self.target || !self.target.parent) {
self.target = null;
var bestTarget = null;
var maxUnitsInArea = 0;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.range) {
// Count how many units would be hit if we target this unit
var unitsInArea = 0;
for (var j = 0; j < playerUnits.length; j++) {
var otherUnit = playerUnits[j];
var areaDistance = Math.sqrt(Math.pow(otherUnit.x - unit.x, 2) + Math.pow(otherUnit.y - unit.y, 2));
if (areaDistance <= self.areaRadius) {
unitsInArea++;
}
}
if (unitsInArea > maxUnitsInArea) {
maxUnitsInArea = unitsInArea;
bestTarget = unit;
}
}
}
self.target = bestTarget;
}
// Shoot area effect
if (self.target && self.shootCooldown <= 0) {
// Create area explosion at target location
var targetX = self.target.x;
var targetY = self.target.y;
// Damage all units in area
var unitsHit = 0;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - targetX, 2) + Math.pow(unit.y - targetY, 2));
if (distance <= self.areaRadius) {
unit.takeDamage(self.damage);
LK.effects.flashObject(unit, 0xff6600, 300);
unitsHit++;
}
}
if (unitsHit > 0) {
// Visual explosion effect
tween(explosionIndicator, {
scaleX: 3.0,
scaleY: 3.0,
alpha: 0.3
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(explosionIndicator, {
scaleX: 1.3,
scaleY: 1.3,
alpha: 1
}, {
duration: 200,
easing: tween.easeIn
});
}
});
LK.getSound('shoot').play();
}
self.shootCooldown = 150; // 2.5 second cooldown
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var AttackUnit = Container.expand(function () {
var self = Container.call(this);
var unitGraphics = self.attachAsset('basicUnitAsset', {
anchorX: 0.5,
anchorY: 0.5
});
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -40;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -40;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
self.health = 100;
self.maxHealth = 100;
self.speed = 2;
self.damage = 25;
self.range = 150;
self.shootCooldown = 0;
self.target = null;
self.lastX = 0;
self.lastY = 0;
self.currentWaypoint = 0;
self.isMovingToWaypoint = false;
// Create path through tower grid
var pathWaypoints = [];
pathWaypoints.push({
x: 400,
y: self.y
});
// Tower grid parameters
var gridStartX = 800;
var gridStartY = 600;
var gridSpacingX = 200;
var gridSpacingY = 200;
var gridRows = 8;
var gridCols = 4;
// Serpentine path through towers
for (var col = 0; col < gridCols; col++) {
if (col % 2 === 0) {
for (var row = 0; row < gridRows; row++) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
} else {
for (var row = gridRows - 1; row >= 0; row--) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
}
}
pathWaypoints.push({
x: 1600,
y: 1000
});
self.pathWaypoints = pathWaypoints;
self.followPath = function () {
if (self.isMovingToWaypoint) return;
if (self.currentWaypoint >= self.pathWaypoints.length) {
// Reached end of path, move directly to base
self.x += self.speed;
return;
}
var waypoint = self.pathWaypoints[self.currentWaypoint];
var distance = Math.sqrt(Math.pow(waypoint.x - self.x, 2) + Math.pow(waypoint.y - self.y, 2));
var duration = distance / self.speed * 16.67; // Convert speed to ms (60fps = 16.67ms per frame)
self.isMovingToWaypoint = true;
tween(self, {
x: waypoint.x,
y: waypoint.y
}, {
duration: duration,
easing: tween.linear,
onFinish: function onFinish() {
self.currentWaypoint++;
self.isMovingToWaypoint = false;
}
});
};
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
// Find target (prioritize base, then towers)
if (!self.target || !self.target.parent) {
self.target = null;
var closestTarget = null;
var closestDistance = Infinity;
// Check distance to enemy base first
if (enemyBase && enemyBase.parent) {
var baseDistance = Math.sqrt(Math.pow(enemyBase.x - self.x, 2) + Math.pow(enemyBase.y - self.y, 2));
if (baseDistance <= self.range) {
closestTarget = enemyBase;
closestDistance = baseDistance;
}
}
// Check towers if no base target or if tower is closer
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower && tower.parent) {
var towerDistance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (towerDistance <= self.range && towerDistance < closestDistance) {
closestDistance = towerDistance;
closestTarget = tower;
}
}
}
self.target = closestTarget;
}
// Attack target if in range
if (self.target && self.shootCooldown <= 0) {
var bullet = new UnitBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = self.target;
bullet.damage = self.damage;
unitBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 60; // 1 second cooldown at 60fps
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
// Move towards enemy base using waypoints only if no target in range
if (!self.target) {
self.followPath();
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
return true; // Unit is dead
}
return false;
};
return self;
});
var BasicTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('basicTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -50;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -50;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
self.towerType = 'basic';
self.health = 100;
self.maxHealth = 100;
self.range = 150;
self.damage = 20;
self.shootCooldown = 0;
self.target = null;
self.cost = 75;
// Upgrade indicator (initially hidden)
var upgradeIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
upgradeIndicator.x = 30;
upgradeIndicator.y = -30;
upgradeIndicator.visible = false;
self.level = 1;
self.maxLevel = 3;
self.baseRange = 150;
self.baseDamage = 20;
self.range = self.baseRange;
self.damage = self.baseDamage;
self.updateStats = function () {
var multiplier = 1 + (self.level - 1) * 0.6; // 60% increase per level
self.range = Math.floor(self.baseRange * multiplier);
self.damage = Math.floor(self.baseDamage * multiplier);
self.maxHealth = Math.floor(100 * multiplier);
self.health = Math.min(self.health, self.maxHealth);
// Special abilities for max level
if (self.level === 3) {
self.shieldActive = false;
self.shieldCooldown = 0;
self.shieldDuration = 0;
}
// Update visual appearance based on level
if (self.level === 2) {
towerGraphics.tint = 0x4169E1; // Royal blue
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xffd700; // Gold
} else if (self.level === 3) {
towerGraphics.tint = 0x0000FF; // Pure blue
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xff4500; // Orange red
}
};
self.canUpgrade = function () {
return self.level < self.maxLevel;
};
self.getUpgradeCost = function () {
return 50 + (self.level - 1) * 25; // Cost increases with level
};
self.upgrade = function () {
if (self.canUpgrade()) {
self.level++;
self.updateStats();
LK.effects.flashObject(self, 0xffd700, 500);
return true;
}
return false;
};
self.update = function () {
// Shield ability for level 3
if (self.level === 3) {
// Activate shield when health is low and not on cooldown
if (self.health < self.maxHealth * 0.3 && !self.shieldActive && self.shieldCooldown <= 0) {
self.shieldActive = true;
self.shieldDuration = 300; // 5 seconds of invulnerability
towerGraphics.tint = 0xFFD700; // Golden shield color
LK.effects.flashObject(self, 0xFFD700, 300);
}
// Handle shield duration
if (self.shieldActive) {
self.shieldDuration--;
if (self.shieldDuration <= 0) {
self.shieldActive = false;
self.shieldCooldown = 1200; // 20 second cooldown
towerGraphics.tint = 0x0000FF; // Back to normal level 3 color
}
}
// Update cooldown
if (self.shieldCooldown > 0) {
self.shieldCooldown--;
}
}
// Find target
if (!self.target || !self.target.parent) {
self.target = null;
var closestUnit = null;
var closestDistance = Infinity;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.range && distance < closestDistance) {
closestDistance = distance;
closestUnit = unit;
}
}
self.target = closestUnit;
}
// Shoot at target (if not frozen)
if (self.target && self.shootCooldown <= 0 && !self.frozenUntil) {
var bullet = new TowerBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = self.target;
bullet.damage = self.damage;
towerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 90; // 1.5 second cooldown
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.takeDamage = function (damage) {
// Shield ability blocks damage at level 3
if (self.level === 3 && self.shieldActive) {
LK.effects.flashObject(self, 0xFFD700, 200); // Gold flash for blocked damage
return false; // No damage taken
}
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var BasicUnit = Container.expand(function () {
var self = Container.call(this);
var unitGraphics = self.attachAsset('basicUnitAsset', {
anchorX: 0.5,
anchorY: 0.5
});
unitGraphics.tint = 0x87CEEB; // Sky blue color for basic unit
unitGraphics.scaleX = 0.9;
unitGraphics.scaleY = 0.9;
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -35;
healthBarBg.scaleX = 0.8;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -35;
healthBar.scaleX = 0.8;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent * 0.8;
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00;
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00;
} else {
healthBar.tint = 0xff0000;
}
};
self.unitType = 'basic';
self.health = 60;
self.maxHealth = 60;
self.speed = 1.8;
self.damage = 15;
self.range = 140;
self.cost = 40;
self.shootCooldown = 0;
self.target = null;
self.lastX = 0;
self.lastY = 0;
self.currentWaypoint = 0;
self.isMovingToWaypoint = false;
// Create path through tower grid
var pathWaypoints = [];
pathWaypoints.push({
x: 400,
y: self.y
});
// Tower grid parameters
var gridStartX = 800;
var gridStartY = 600;
var gridSpacingX = 200;
var gridSpacingY = 200;
var gridRows = 8;
var gridCols = 4;
// Serpentine path through towers
for (var col = 0; col < gridCols; col++) {
if (col % 2 === 0) {
for (var row = 0; row < gridRows; row++) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
} else {
for (var row = gridRows - 1; row >= 0; row--) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
}
}
pathWaypoints.push({
x: 1600,
y: 1000
});
self.pathWaypoints = pathWaypoints;
self.followPath = function () {
if (self.isMovingToWaypoint) return;
if (self.currentWaypoint >= self.pathWaypoints.length) {
// Check for walls blocking movement
var canMove = true;
var nextX = self.x + self.speed;
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower.towerType === 'wall') {
var distance = Math.sqrt(Math.pow(tower.x - nextX, 2) + Math.pow(tower.y - self.y, 2));
if (distance <= tower.blockRadius) {
canMove = false;
break;
}
}
}
if (canMove) {
self.x += self.speed;
}
return;
}
var waypoint = self.pathWaypoints[self.currentWaypoint];
// Check if path to waypoint is blocked by walls
var pathBlocked = false;
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower.towerType === 'wall') {
var wallDistance = Math.sqrt(Math.pow(tower.x - waypoint.x, 2) + Math.pow(tower.y - waypoint.y, 2));
if (wallDistance <= tower.blockRadius) {
pathBlocked = true;
break;
}
}
}
if (pathBlocked) {
// Skip this waypoint if blocked
self.currentWaypoint++;
return;
}
var distance = Math.sqrt(Math.pow(waypoint.x - self.x, 2) + Math.pow(waypoint.y - self.y, 2));
var duration = distance / self.speed * 16.67;
self.isMovingToWaypoint = true;
tween(self, {
x: waypoint.x,
y: waypoint.y
}, {
duration: duration,
easing: tween.linear,
onFinish: function onFinish() {
self.currentWaypoint++;
self.isMovingToWaypoint = false;
}
});
};
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
if (!self.target || !self.target.parent) {
self.target = null;
var closestTarget = null;
var closestDistance = Infinity;
if (enemyBase && enemyBase.parent) {
var baseDistance = Math.sqrt(Math.pow(enemyBase.x - self.x, 2) + Math.pow(enemyBase.y - self.y, 2));
if (baseDistance <= self.range) {
closestTarget = enemyBase;
closestDistance = baseDistance;
}
}
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower && tower.parent) {
var towerDistance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (towerDistance <= self.range && towerDistance < closestDistance) {
closestDistance = towerDistance;
closestTarget = tower;
}
}
}
self.target = closestTarget;
}
if (self.target && self.shootCooldown <= 0) {
var bullet = new UnitBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = self.target;
bullet.damage = self.damage;
unitBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 75;
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
if (!self.target) {
self.followPath();
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
return true;
}
return false;
};
return self;
});
var EnemyBase = Container.expand(function () {
var self = Container.call(this);
var baseGraphics = self.attachAsset('enemyBase', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 800;
self.maxHealth = 800;
self.range = 300; // Base shooting range
self.damage = 60; // Base damage per shot
self.shootCooldown = 0;
self.target = null;
self.update = function () {
// Find target within range
if (!self.target || !self.target.parent) {
self.target = null;
var closestUnit = null;
var closestDistance = Infinity;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.range && distance < closestDistance) {
closestDistance = distance;
closestUnit = unit;
}
}
self.target = closestUnit;
}
// Shoot at target (if not frozen)
if (self.target && self.shootCooldown <= 0 && !self.frozenUntil) {
var bullet = new TowerBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = self.target;
bullet.damage = self.damage;
towerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 90; // 1.5 second cooldown
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 300);
if (self.health <= 0) {
return true; // Base destroyed - player wins!
}
return false;
};
return self;
});
// Game arrays to track entities
var EnemyTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('enemyTower', {
anchorX: 0.5,
anchorY: 0.5
});
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -50;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -50;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
// Upgrade indicator (initially hidden)
var upgradeIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
upgradeIndicator.x = 30;
upgradeIndicator.y = -30;
upgradeIndicator.visible = false;
self.level = 1;
self.maxLevel = 3;
self.health = 250;
self.maxHealth = 250;
self.baseRange = 220;
self.baseDamage = 45;
self.range = self.baseRange;
self.damage = self.baseDamage;
self.shootCooldown = 0;
self.target = null;
self.updateStats = function () {
var multiplier = 1 + (self.level - 1) * 0.5; // 50% increase per level
self.range = Math.floor(self.baseRange * multiplier);
self.damage = Math.floor(self.baseDamage * multiplier);
self.maxHealth = Math.floor(150 * multiplier);
self.health = Math.min(self.health, self.maxHealth);
// Update visual appearance based on level
if (self.level === 2) {
towerGraphics.tint = 0xc0392b; // Darker red
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xffd700; // Gold
} else if (self.level === 3) {
towerGraphics.tint = 0x8b0000; // Dark red
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xff4500; // Orange red
}
};
self.canUpgrade = function () {
return self.level < self.maxLevel;
};
self.getUpgradeCost = function () {
return 75 * self.level; // Cost increases with level
};
self.upgrade = function () {
if (self.canUpgrade()) {
self.level++;
self.updateStats();
LK.effects.flashObject(self, 0xffd700, 500);
return true;
}
return false;
};
self.update = function () {
// Increase range and damage if base is low on health
var baseHealthPercent = enemyBase ? enemyBase.health / enemyBase.maxHealth : 1;
var bonusRange = baseHealthPercent < 0.5 ? 100 : 0;
var bonusDamage = baseHealthPercent < 0.3 ? 20 : 0;
var currentRange = self.range + bonusRange;
var currentDamage = self.damage + bonusDamage;
// Find target
if (!self.target || !self.target.parent) {
self.target = null;
var closestUnit = null;
var closestDistance = Infinity;
// Prioritize units closer to base
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
var distanceToBase = Math.sqrt(Math.pow(unit.x - enemyBase.x, 2) + Math.pow(unit.y - enemyBase.y, 2));
if (distance <= currentRange) {
// Prioritize units closer to base
var priority = distance - distanceToBase * 0.3;
if (priority < closestDistance) {
closestDistance = priority;
closestUnit = unit;
}
}
}
self.target = closestUnit;
}
// Shoot at target
if (self.target && self.shootCooldown <= 0) {
var bullet = new TowerBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = self.target;
bullet.damage = currentDamage;
towerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
// Shoot faster when base is in danger
var shootCooldown = baseHealthPercent < 0.5 ? 60 : 90;
self.shootCooldown = shootCooldown;
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var FastTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('fastTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -40;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -40;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
self.towerType = 'fast';
self.health = 80;
self.maxHealth = 80;
self.range = 180;
self.damage = 15;
self.shootCooldown = 0;
self.target = null;
self.cost = 100;
// Upgrade indicator (initially hidden)
var upgradeIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
upgradeIndicator.x = 25;
upgradeIndicator.y = -25;
upgradeIndicator.visible = false;
self.level = 1;
self.maxLevel = 3;
self.baseRange = 180;
self.baseDamage = 15;
self.baseCooldown = 30;
self.multiShot = 1; // Number of bullets per shot
self.range = self.baseRange;
self.damage = self.baseDamage;
self.updateStats = function () {
var multiplier = 1 + (self.level - 1) * 0.4; // 40% increase per level
self.range = Math.floor(self.baseRange * multiplier);
self.damage = Math.floor(self.baseDamage * multiplier);
self.maxHealth = Math.floor(80 * multiplier);
self.health = Math.min(self.health, self.maxHealth);
self.multiShot = self.level; // Shoot multiple bullets at higher levels
// Special abilities for max level
if (self.level === 3) {
self.burstMode = false;
self.burstCooldown = 0;
self.burstDuration = 0;
self.burstTriggerDistance = 100; // Distance to activate burst mode
}
// Update visual appearance based on level
if (self.level === 2) {
towerGraphics.tint = 0x00FF7F; // Spring green
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xffd700; // Gold
} else if (self.level === 3) {
towerGraphics.tint = 0x00FF00; // Pure green
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xff4500; // Orange red
}
};
self.canUpgrade = function () {
return self.level < self.maxLevel;
};
self.getUpgradeCost = function () {
return 60 + (self.level - 1) * 30; // Cost increases with level
};
self.upgrade = function () {
if (self.canUpgrade()) {
self.level++;
self.updateStats();
LK.effects.flashObject(self, 0xffd700, 500);
return true;
}
return false;
};
self.update = function () {
// Burst mode ability for level 3
if (self.level === 3) {
// Check if any enemies are very close to activate burst mode
var hasCloseEnemies = false;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.burstTriggerDistance) {
hasCloseEnemies = true;
break;
}
}
// Activate burst mode when enemies are close and not on cooldown
if (hasCloseEnemies && !self.burstMode && self.burstCooldown <= 0) {
self.burstMode = true;
self.burstDuration = 180; // 3 seconds of burst fire
towerGraphics.tint = 0xFFFF00; // Yellow burst color
LK.effects.flashObject(self, 0xFFFF00, 300);
}
// Handle burst duration
if (self.burstMode) {
self.burstDuration--;
if (self.burstDuration <= 0) {
self.burstMode = false;
self.burstCooldown = 600; // 10 second cooldown
towerGraphics.tint = 0x00FF00; // Back to normal level 3 color
}
}
// Update cooldown
if (self.burstCooldown > 0) {
self.burstCooldown--;
}
}
// Find target
if (!self.target || !self.target.parent) {
self.target = null;
var closestUnit = null;
var closestDistance = Infinity;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.range && distance < closestDistance) {
closestDistance = distance;
closestUnit = unit;
}
}
self.target = closestUnit;
}
// Shoot at target with fast rate and multi-shot
if (self.target && self.shootCooldown <= 0) {
// Determine shot count - burst mode doubles the shots
var shotCount = self.multiShot;
if (self.level === 3 && self.burstMode) {
shotCount = self.multiShot * 2; // Double shots in burst mode
}
// Shoot multiple bullets based on level and burst mode
for (var shotIndex = 0; shotIndex < shotCount; shotIndex++) {
var bullet = new TowerBullet();
bullet.x = self.x;
bullet.y = self.y;
// For multi-shot, slightly spread the bullets
if (shotCount > 1) {
var angleOffset = (shotIndex - (shotCount - 1) / 2) * 0.2; // Smaller angle spread for more bullets
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var angle = Math.atan2(dy, dx) + angleOffset;
var distance = Math.sqrt(dx * dx + dy * dy);
// Create a virtual target with slight offset
bullet.target = {
x: self.x + Math.cos(angle) * distance,
y: self.y + Math.sin(angle) * distance,
parent: self.target.parent,
takeDamage: self.target.takeDamage.bind(self.target)
};
} else {
bullet.target = self.target;
}
bullet.damage = self.damage;
towerBullets.push(bullet);
game.addChild(bullet);
}
LK.getSound('shoot').play();
// Faster shooting in burst mode
var cooldownTime = self.level === 3 && self.burstMode ? 15 : 30;
self.shootCooldown = cooldownTime;
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var FastUnit = Container.expand(function () {
var self = Container.call(this);
var unitGraphics = self.attachAsset('fastUnitAsset', {
anchorX: 0.5,
anchorY: 0.5
});
unitGraphics.tint = 0x32CD32; // Lime green color for fast unit
unitGraphics.scaleX = 0.8;
unitGraphics.scaleY = 0.8;
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -32;
healthBarBg.scaleX = 0.7;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -32;
healthBar.scaleX = 0.7;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent * 0.7;
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00;
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00;
} else {
healthBar.tint = 0xff0000;
}
};
self.unitType = 'fast';
self.health = 45;
self.maxHealth = 45;
self.speed = 3.5;
self.damage = 12;
self.range = 120;
self.cost = 60;
self.shootCooldown = 0;
self.target = null;
self.lastX = 0;
self.lastY = 0;
self.currentWaypoint = 0;
self.isMovingToWaypoint = false;
// Create path through tower grid
var pathWaypoints = [];
pathWaypoints.push({
x: 400,
y: self.y
});
// Tower grid parameters
var gridStartX = 800;
var gridStartY = 600;
var gridSpacingX = 200;
var gridSpacingY = 200;
var gridRows = 8;
var gridCols = 4;
// Serpentine path through towers
for (var col = 0; col < gridCols; col++) {
if (col % 2 === 0) {
for (var row = 0; row < gridRows; row++) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
} else {
for (var row = gridRows - 1; row >= 0; row--) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
}
}
pathWaypoints.push({
x: 1600,
y: 1000
});
self.pathWaypoints = pathWaypoints;
self.followPath = function () {
if (self.isMovingToWaypoint) return;
if (self.currentWaypoint >= self.pathWaypoints.length) {
self.x += self.speed;
return;
}
var waypoint = self.pathWaypoints[self.currentWaypoint];
var distance = Math.sqrt(Math.pow(waypoint.x - self.x, 2) + Math.pow(waypoint.y - self.y, 2));
var duration = distance / self.speed * 16.67;
self.isMovingToWaypoint = true;
tween(self, {
x: waypoint.x,
y: waypoint.y
}, {
duration: duration,
easing: tween.linear,
onFinish: function onFinish() {
self.currentWaypoint++;
self.isMovingToWaypoint = false;
}
});
};
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
if (!self.target || !self.target.parent) {
self.target = null;
var closestTarget = null;
var closestDistance = Infinity;
if (enemyBase && enemyBase.parent) {
var baseDistance = Math.sqrt(Math.pow(enemyBase.x - self.x, 2) + Math.pow(enemyBase.y - self.y, 2));
if (baseDistance <= self.range) {
closestTarget = enemyBase;
closestDistance = baseDistance;
}
}
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower && tower.parent) {
var towerDistance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (towerDistance <= self.range && towerDistance < closestDistance) {
closestDistance = towerDistance;
closestTarget = tower;
}
}
}
self.target = closestTarget;
}
if (self.target && self.shootCooldown <= 0) {
var bullet = new UnitBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = self.target;
bullet.damage = self.damage;
unitBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 45; // Faster shooting
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
if (!self.target) {
self.followPath();
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
return true;
}
return false;
};
return self;
});
var HealTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('healTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -50;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -50;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
// Add a cross symbol effect
var crossIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
crossIndicator.x = 0;
crossIndicator.y = 0;
crossIndicator.tint = 0xffffff; // White cross
crossIndicator.scaleX = 1.5;
crossIndicator.scaleY = 1.5;
self.towerType = 'heal';
self.health = 120;
self.maxHealth = 120;
self.range = 200; // Healing range
self.healAmount = 15;
self.healCooldown = 0;
self.target = null;
self.cost = 110;
self.canUpgrade = function () {
return false; // Heal towers cannot be upgraded
};
self.getUpgradeCost = function () {
return 0; // Heal towers cannot be upgraded
};
self.update = function () {
// Find damaged towers to heal
if (self.healCooldown <= 0) {
self.target = null;
var bestTarget = null;
var lowestHealthPercent = 1;
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower !== self && tower.parent) {
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance <= self.range) {
var healthPercent = tower.health / tower.maxHealth;
if (healthPercent < 1 && healthPercent < lowestHealthPercent) {
lowestHealthPercent = healthPercent;
bestTarget = tower;
}
}
}
}
self.target = bestTarget;
}
// Heal target
if (self.target && self.healCooldown <= 0) {
var healingAmount = Math.min(self.healAmount, self.target.maxHealth - self.target.health);
if (healingAmount > 0) {
self.target.health += healingAmount;
// Update target's health bar after healing
if (self.target.updateHealthBar) {
self.target.updateHealthBar();
}
LK.effects.flashObject(self.target, 0x00ff00, 400);
// Create healing visual effect
tween(crossIndicator, {
scaleX: 2.0,
scaleY: 2.0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(crossIndicator, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeIn
});
}
});
self.healCooldown = 120; // 2 second cooldown
}
}
if (self.healCooldown > 0) {
self.healCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var HeavyTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('heavyTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -60;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -60;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
self.towerType = 'heavy';
self.health = 200;
self.maxHealth = 200;
self.range = 160;
self.damage = 50;
self.shootCooldown = 0;
self.target = null;
self.cost = 150;
// Upgrade indicator (initially hidden)
var upgradeIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
upgradeIndicator.x = 35;
upgradeIndicator.y = -35;
upgradeIndicator.visible = false;
self.level = 1;
self.maxLevel = 3;
self.baseRange = 160;
self.baseDamage = 50;
self.splashRadius = 0; // Splash damage radius
self.range = self.baseRange;
self.damage = self.baseDamage;
self.updateStats = function () {
var multiplier = 1 + (self.level - 1) * 0.5; // 50% increase per level
self.range = Math.floor(self.baseRange * multiplier);
self.damage = Math.floor(self.baseDamage * multiplier);
self.maxHealth = Math.floor(200 * multiplier);
self.health = Math.min(self.health, self.maxHealth);
self.splashRadius = (self.level - 1) * 60; // Splash damage at higher levels
// Special abilities for max level
if (self.level === 3) {
self.mineActive = false;
self.mineCooldown = 0;
self.mineRadius = 100;
self.mineDamage = 120;
}
// Update visual appearance based on level
if (self.level === 2) {
towerGraphics.tint = 0x8B4513; // Saddle brown
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xffd700; // Gold
} else if (self.level === 3) {
towerGraphics.tint = 0x654321; // Dark brown
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xff4500; // Orange red
}
};
self.canUpgrade = function () {
return self.level < self.maxLevel;
};
self.getUpgradeCost = function () {
return 75 + (self.level - 1) * 40; // Cost increases with level
};
self.upgrade = function () {
if (self.canUpgrade()) {
self.level++;
self.updateStats();
LK.effects.flashObject(self, 0xffd700, 500);
return true;
}
return false;
};
self.update = function () {
// Earthquake ability for level 3
if (self.level === 3 && self.earthquakeCooldown <= 0) {
// Count enemies within earthquake range
var enemiesInRange = 0;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.earthquakeRadius) {
enemiesInRange++;
}
}
// Trigger earthquake if 3+ enemies are nearby
if (enemiesInRange >= 3) {
// Damage all enemies in range
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.earthquakeRadius) {
unit.takeDamage(self.earthquakeDamage);
LK.effects.flashObject(unit, 0x8B4513, 400);
}
}
// Visual earthquake effect
towerGraphics.tint = 0xFFD700; // Golden earthquake color
LK.effects.flashObject(self, 0x8B4513, 600);
// Screen shake effect simulation
LK.effects.flashScreen(0x8B4513, 300);
self.earthquakeCooldown = 900; // 15 second cooldown
// Return to normal color after effect
var self_ref = self;
LK.setTimeout(function () {
self_ref.towerGraphics.tint = 0x654321; // Back to level 3 color
}, 600);
}
}
// Update earthquake cooldown
if (self.earthquakeCooldown > 0) {
self.earthquakeCooldown--;
}
// Find target
if (!self.target || !self.target.parent) {
self.target = null;
var closestUnit = null;
var closestDistance = Infinity;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.range && distance < closestDistance) {
closestDistance = distance;
closestUnit = unit;
}
}
self.target = closestUnit;
}
// Shoot at target with heavy damage and potential splash
if (self.target && self.shootCooldown <= 0) {
var bullet = new TowerBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = self.target;
bullet.damage = self.damage;
bullet.splashRadius = self.splashRadius; // Add splash capability
bullet.splashDamage = Math.floor(self.damage * 0.6); // 60% splash damage
towerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 120; // 2 second cooldown (slow)
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var HeavyUnit = Container.expand(function () {
var self = Container.call(this);
var unitGraphics = self.attachAsset('heavyUnitAsset', {
anchorX: 0.5,
anchorY: 0.5
});
unitGraphics.tint = 0x8B4513; // Brown color for heavy unit
unitGraphics.scaleX = 1.2;
unitGraphics.scaleY = 1.2;
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -45;
healthBarBg.scaleX = 1.0;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -45;
healthBar.scaleX = 1.0;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent * 1.0;
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00;
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00;
} else {
healthBar.tint = 0xff0000;
}
};
self.unitType = 'heavy';
self.health = 120;
self.maxHealth = 120;
self.speed = 0.8;
self.damage = 30;
self.range = 160;
self.cost = 90;
self.shootCooldown = 0;
self.target = null;
self.lastX = 0;
self.lastY = 0;
self.currentWaypoint = 0;
self.isMovingToWaypoint = false;
// Create path through tower grid
var pathWaypoints = [];
pathWaypoints.push({
x: 400,
y: self.y
});
// Tower grid parameters
var gridStartX = 800;
var gridStartY = 600;
var gridSpacingX = 200;
var gridSpacingY = 200;
var gridRows = 8;
var gridCols = 4;
// Serpentine path through towers
for (var col = 0; col < gridCols; col++) {
if (col % 2 === 0) {
for (var row = 0; row < gridRows; row++) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
} else {
for (var row = gridRows - 1; row >= 0; row--) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
}
}
pathWaypoints.push({
x: 1600,
y: 1000
});
self.pathWaypoints = pathWaypoints;
self.followPath = function () {
if (self.isMovingToWaypoint) return;
if (self.currentWaypoint >= self.pathWaypoints.length) {
self.x += self.speed;
return;
}
var waypoint = self.pathWaypoints[self.currentWaypoint];
var distance = Math.sqrt(Math.pow(waypoint.x - self.x, 2) + Math.pow(waypoint.y - self.y, 2));
var duration = distance / self.speed * 16.67;
self.isMovingToWaypoint = true;
tween(self, {
x: waypoint.x,
y: waypoint.y
}, {
duration: duration,
easing: tween.linear,
onFinish: function onFinish() {
self.currentWaypoint++;
self.isMovingToWaypoint = false;
}
});
};
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
if (!self.target || !self.target.parent) {
self.target = null;
var closestTarget = null;
var closestDistance = Infinity;
if (enemyBase && enemyBase.parent) {
var baseDistance = Math.sqrt(Math.pow(enemyBase.x - self.x, 2) + Math.pow(enemyBase.y - self.y, 2));
if (baseDistance <= self.range) {
closestTarget = enemyBase;
closestDistance = baseDistance;
}
}
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower && tower.parent) {
var towerDistance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (towerDistance <= self.range && towerDistance < closestDistance) {
closestDistance = towerDistance;
closestTarget = tower;
}
}
}
self.target = closestTarget;
}
if (self.target && self.shootCooldown <= 0) {
var bullet = new UnitBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = self.target;
bullet.damage = self.damage;
unitBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 100; // Slower shooting
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
if (!self.target) {
self.followPath();
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
return true;
}
return false;
};
return self;
});
var LaserTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('laserTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
towerGraphics.tint = 0xFF1493; // Deep pink for laser tower
towerGraphics.scaleX = 1.2;
towerGraphics.scaleY = 1.2;
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -45;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -45;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
// Add laser beam indicator
var laserIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
laserIndicator.x = 0;
laserIndicator.y = 0;
laserIndicator.tint = 0xFF1493; // Pink laser symbol
laserIndicator.scaleX = 1.3;
laserIndicator.scaleY = 1.3;
self.towerType = 'laser';
self.health = 100;
self.maxHealth = 100;
self.range = 220;
self.damage = 8; // Continuous damage per frame
self.chargingTime = 60; // 1 second to charge
self.maxBeamTime = 180; // 3 seconds max beam
self.cooldownTime = 120; // 2 second cooldown
self.beamState = 'idle'; // 'idle', 'charging', 'firing', 'cooling'
self.stateTimer = 0;
self.target = null;
self.cost = 140;
self.canUpgrade = function () {
return false; // Laser towers cannot be upgraded
};
self.getUpgradeCost = function () {
return 0; // Laser towers cannot be upgraded
};
self.update = function () {
// Find target when idle
if (self.beamState === 'idle') {
if (!self.target || !self.target.parent) {
self.target = null;
var closestUnit = null;
var closestDistance = Infinity;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.range && distance < closestDistance) {
closestDistance = distance;
closestUnit = unit;
}
}
self.target = closestUnit;
}
if (self.target) {
self.beamState = 'charging';
self.stateTimer = self.chargingTime;
// Start charging effect
towerGraphics.tint = 0xFF69B4; // Lighter pink while charging
}
}
// Handle beam states
if (self.beamState === 'charging') {
self.stateTimer--;
if (self.stateTimer <= 0) {
self.beamState = 'firing';
self.stateTimer = self.maxBeamTime;
towerGraphics.tint = 0xFF0000; // Red while firing
}
} else if (self.beamState === 'firing') {
// Continuous damage while firing
if (self.target && self.target.parent) {
var distance = Math.sqrt(Math.pow(self.target.x - self.x, 2) + Math.pow(self.target.y - self.y, 2));
if (distance <= self.range) {
self.target.takeDamage(self.damage);
// Visual beam effect
if (LK.ticks % 10 === 0) {
LK.effects.flashObject(self.target, 0xFF1493, 100);
}
} else {
self.target = null; // Lost target
}
}
// Laser beam visual effect
tween(laserIndicator, {
scaleX: 1.8,
scaleY: 1.8,
alpha: 0.7
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(laserIndicator, {
scaleX: 1.3,
scaleY: 1.3,
alpha: 1
}, {
duration: 100,
easing: tween.easeIn
});
}
});
self.stateTimer--;
if (self.stateTimer <= 0 || !self.target || !self.target.parent) {
self.beamState = 'cooling';
self.stateTimer = self.cooldownTime;
self.target = null;
towerGraphics.tint = 0x808080; // Gray while cooling
}
} else if (self.beamState === 'cooling') {
self.stateTimer--;
if (self.stateTimer <= 0) {
self.beamState = 'idle';
towerGraphics.tint = 0xFF1493; // Back to normal color
}
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var MeleeUnit = Container.expand(function () {
var self = Container.call(this);
var unitGraphics = self.attachAsset('meleeUnitAsset', {
anchorX: 0.5,
anchorY: 0.5
});
unitGraphics.tint = 0xFF6347; // Tomato red color for melee unit
unitGraphics.scaleX = 1.1;
unitGraphics.scaleY = 1.1;
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -40;
healthBarBg.scaleX = 0.9;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -40;
healthBar.scaleX = 0.9;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent * 0.9;
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00;
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00;
} else {
healthBar.tint = 0xff0000;
}
};
self.unitType = 'melee';
self.health = 90;
self.maxHealth = 90;
self.speed = 1.3;
self.damage = 25;
self.range = 50; // Very short range for melee
self.cost = 30;
self.attackCooldown = 0;
self.target = null;
self.lastX = 0;
self.lastY = 0;
self.currentWaypoint = 0;
self.isMovingToWaypoint = false;
// Create path through tower grid
var pathWaypoints = [];
pathWaypoints.push({
x: 400,
y: self.y
});
// Tower grid parameters
var gridStartX = 800;
var gridStartY = 600;
var gridSpacingX = 200;
var gridSpacingY = 200;
var gridRows = 8;
var gridCols = 4;
// Serpentine path through towers
for (var col = 0; col < gridCols; col++) {
if (col % 2 === 0) {
for (var row = 0; row < gridRows; row++) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
} else {
for (var row = gridRows - 1; row >= 0; row--) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
}
}
pathWaypoints.push({
x: 1600,
y: 1000
});
self.pathWaypoints = pathWaypoints;
self.followPath = function () {
if (self.isMovingToWaypoint) return;
if (self.currentWaypoint >= self.pathWaypoints.length) {
self.x += self.speed;
return;
}
var waypoint = self.pathWaypoints[self.currentWaypoint];
var distance = Math.sqrt(Math.pow(waypoint.x - self.x, 2) + Math.pow(waypoint.y - self.y, 2));
var duration = distance / self.speed * 16.67;
self.isMovingToWaypoint = true;
tween(self, {
x: waypoint.x,
y: waypoint.y
}, {
duration: duration,
easing: tween.linear,
onFinish: function onFinish() {
self.currentWaypoint++;
self.isMovingToWaypoint = false;
}
});
};
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
// Find closest target (prioritize base, then towers)
if (!self.target || !self.target.parent) {
self.target = null;
var closestTarget = null;
var closestDistance = Infinity;
// Check distance to enemy base first
if (enemyBase && enemyBase.parent) {
var baseDistance = Math.sqrt(Math.pow(enemyBase.x - self.x, 2) + Math.pow(enemyBase.y - self.y, 2));
if (baseDistance <= self.range) {
closestTarget = enemyBase;
closestDistance = baseDistance;
}
}
// Check towers if no base target or if tower is closer
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower && tower.parent) {
var towerDistance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (towerDistance <= self.range && towerDistance < closestDistance) {
closestDistance = towerDistance;
closestTarget = tower;
}
}
}
self.target = closestTarget;
}
// Melee attack - no bullets, direct damage
if (self.target && self.attackCooldown <= 0) {
// Flash effect for melee attack
LK.effects.flashObject(self, 0xFF6347, 200);
LK.effects.flashObject(self.target, 0xFF0000, 200);
// Direct damage to target
self.target.takeDamage(self.damage);
LK.getSound('hit').play();
self.attackCooldown = 90; // 1.5 second cooldown
}
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
// Move towards target or follow path
if (self.target) {
// Move directly towards target if one is found and not in attack range
if (!self.isMovingToWaypoint) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > self.range) {
// Move towards target only if outside attack range
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
}
} else {
// Follow path if no target
self.followPath();
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
return true;
}
return false;
};
return self;
});
var PoisonTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('poisonTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
towerGraphics.tint = 0x228B22; // Forest green for poison tower
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -50;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -50;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
// Add poison cloud indicator
var poisonIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
poisonIndicator.x = 0;
poisonIndicator.y = 0;
poisonIndicator.tint = 0x228B22; // Green poison symbol
poisonIndicator.scaleX = 1.4;
poisonIndicator.scaleY = 1.4;
self.towerType = 'poison';
self.health = 110;
self.maxHealth = 110;
self.range = 160;
self.damage = 15; // Initial damage
self.poisonDamage = 5; // Damage per tick
self.poisonDuration = 480; // 8 seconds at 60fps
self.shootCooldown = 0;
self.target = null;
self.cost = 95;
self.canUpgrade = function () {
return false; // Poison towers cannot be upgraded
};
self.getUpgradeCost = function () {
return 0; // Poison towers cannot be upgraded
};
self.update = function () {
// Find target
if (!self.target || !self.target.parent) {
self.target = null;
var closestUnit = null;
var closestDistance = Infinity;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.range && distance < closestDistance) {
closestDistance = distance;
closestUnit = unit;
}
}
self.target = closestUnit;
}
// Apply poison effect
if (self.target && self.shootCooldown <= 0) {
// Initial damage
self.target.takeDamage(self.damage);
// Apply poison effect
self.target.poisonDamage = self.poisonDamage;
self.target.poisonEndTime = LK.ticks + self.poisonDuration;
self.target.lastPoisonTick = LK.ticks;
// Visual effects
LK.effects.flashObject(self.target, 0x228B22, 400);
tween(poisonIndicator, {
scaleX: 2.2,
scaleY: 2.2,
alpha: 0.6
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(poisonIndicator, {
scaleX: 1.4,
scaleY: 1.4,
alpha: 1
}, {
duration: 200,
easing: tween.easeIn
});
}
});
LK.getSound('shoot').play();
self.shootCooldown = 120; // 2 second cooldown
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var ResurrectionTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('resurrectionTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
towerGraphics.tint = 0xFFD700; // Gold color for resurrection tower
towerGraphics.scaleX = 1.3;
towerGraphics.scaleY = 1.3;
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -60;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -60;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
// Add resurrection symbol effect
var resIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
resIndicator.x = 0;
resIndicator.y = 0;
resIndicator.tint = 0xFFFFFF; // White resurrection symbol
resIndicator.scaleX = 2.0;
resIndicator.scaleY = 2.0;
self.towerType = 'resurrection';
self.health = 150;
self.maxHealth = 150;
self.range = 250; // Resurrection range
self.resurrectionCooldown = 0;
self.cost = 200;
self.canUpgrade = function () {
return false; // Resurrection towers cannot be upgraded
};
self.getUpgradeCost = function () {
return 0; // Resurrection towers cannot be upgraded
};
self.update = function () {
// Only attempt resurrection when not on cooldown
if (self.resurrectionCooldown <= 0) {
// Look for empty slots within range that used to have towers
var bestSlot = null;
var bestPriority = 0;
for (var i = 0; i < towerSlots.length; i++) {
var slot = towerSlots[i];
if (!slot.occupied && slot.lastTowerType) {
// Check if slot is within resurrection range
var distance = Math.sqrt(Math.pow(slot.x - self.x, 2) + Math.pow(slot.y - self.y, 2));
if (distance <= self.range) {
// Calculate priority based on tower type and nearby units
var priority = 1;
var nearbyUnits = 0;
for (var j = 0; j < playerUnits.length; j++) {
var unit = playerUnits[j];
var unitDistance = Math.sqrt(Math.pow(unit.x - slot.x, 2) + Math.pow(unit.y - slot.y, 2));
if (unitDistance <= 300) {
nearbyUnits++;
}
}
// Higher priority for slots with nearby enemies
priority += nearbyUnits * 2;
// Higher priority for certain tower types
if (slot.lastTowerType === 'basic' || slot.lastTowerType === 'fast') {
priority += 3;
} else if (slot.lastTowerType === 'heavy' || slot.lastTowerType === 'sniper') {
priority += 5;
}
if (priority > bestPriority) {
bestPriority = priority;
bestSlot = slot;
}
}
}
}
// Resurrect tower at best slot
if (bestSlot) {
var newTower = null;
// Recreate the tower based on its last type
switch (bestSlot.lastTowerType) {
case 'basic':
newTower = new BasicTower();
break;
case 'fast':
newTower = new FastTower();
break;
case 'heavy':
newTower = new HeavyTower();
break;
case 'sniper':
newTower = new SniperTower();
break;
case 'slow':
newTower = new SlowTower();
break;
case 'poison':
newTower = new PoisonTower();
break;
case 'laser':
newTower = new LaserTower();
break;
case 'area':
newTower = new AreaTower();
break;
case 'heal':
newTower = new HealTower();
break;
case 'wall':
newTower = new Wall();
break;
case 'trap':
newTower = new Trap();
break;
}
if (newTower) {
// Place the resurrected tower
newTower.x = bestSlot.x;
newTower.y = bestSlot.y;
newTower.slot = bestSlot;
// Resurrect at 50% health
newTower.health = Math.floor(newTower.maxHealth * 0.5);
enemyTowers.push(newTower);
game.addChild(newTower);
bestSlot.occupied = true;
bestSlot.tower = newTower;
bestSlot.attachAsset('towerSlot', {}).alpha = 0.1;
// Clear the resurrection memory
bestSlot.lastTowerType = null;
// Initialize health bar
if (newTower.updateHealthBar) {
newTower.updateHealthBar();
}
// Visual resurrection effect
tween(resIndicator, {
scaleX: 3.5,
scaleY: 3.5,
alpha: 0.3
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(resIndicator, {
scaleX: 2.0,
scaleY: 2.0,
alpha: 1
}, {
duration: 300,
easing: tween.easeIn
});
}
});
LK.effects.flashObject(newTower, 0xFFD700, 800);
LK.effects.flashObject(self, 0xFFD700, 400);
self.resurrectionCooldown = 1200; // 20 second cooldown
}
}
}
if (self.resurrectionCooldown > 0) {
self.resurrectionCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var SlowTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('slowTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
towerGraphics.tint = 0x9932CC; // Purple color for slow tower
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -50;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -50;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
// Add slow effect indicator
var slowIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
slowIndicator.x = 0;
slowIndicator.y = 0;
slowIndicator.tint = 0x9932CC; // Purple indicator
slowIndicator.scaleX = 1.2;
slowIndicator.scaleY = 1.2;
self.towerType = 'slow';
self.health = 90;
self.maxHealth = 90;
self.range = 200; // Large range for slowing
self.damage = 10; // Low damage
self.slowEffect = 0.5; // Reduces speed by 50%
self.slowDuration = 300; // 5 seconds at 60fps
self.shootCooldown = 0;
self.target = null;
self.cost = 85;
self.canUpgrade = function () {
return false; // Slow towers cannot be upgraded
};
self.getUpgradeCost = function () {
return 0; // Slow towers cannot be upgraded
};
self.update = function () {
// Find target
if (!self.target || !self.target.parent) {
self.target = null;
var closestUnit = null;
var closestDistance = Infinity;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.range && distance < closestDistance) {
closestDistance = distance;
closestUnit = unit;
}
}
self.target = closestUnit;
}
// Apply slow effect and light damage
if (self.target && self.shootCooldown <= 0) {
// Apply slow effect
if (!self.target.originalSpeed) {
self.target.originalSpeed = self.target.speed;
}
self.target.speed = self.target.originalSpeed * self.slowEffect;
self.target.slowEndTime = LK.ticks + self.slowDuration;
// Light damage
self.target.takeDamage(self.damage);
// Visual effects
LK.effects.flashObject(self.target, 0x9932CC, 300);
tween(slowIndicator, {
scaleX: 1.8,
scaleY: 1.8
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(slowIndicator, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeIn
});
}
});
LK.getSound('shoot').play();
self.shootCooldown = 60; // 1 second cooldown
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var SniperTower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('sniperTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -55;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -55;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
self.towerType = 'sniper';
self.health = 120;
self.maxHealth = 120;
self.range = 300; // Very long range
self.damage = 40;
self.shootCooldown = 0;
self.target = null;
self.cost = 125;
// Upgrade indicator (initially hidden)
var upgradeIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
upgradeIndicator.x = 30;
upgradeIndicator.y = -35;
upgradeIndicator.visible = false;
self.level = 1;
self.maxLevel = 3;
self.baseRange = 300;
self.baseDamage = 40;
self.criticalChance = 0; // Chance for critical hits
self.pierceCount = 0; // Number of units bullet can pierce through
self.range = self.baseRange;
self.damage = self.baseDamage;
self.updateStats = function () {
var multiplier = 1 + (self.level - 1) * 0.7; // 70% increase per level
self.range = Math.floor(self.baseRange * multiplier);
self.damage = Math.floor(self.baseDamage * multiplier);
self.maxHealth = Math.floor(120 * multiplier);
self.health = Math.min(self.health, self.maxHealth);
self.criticalChance = (self.level - 1) * 0.25; // 25% crit chance per level above 1
self.pierceCount = self.level - 1; // Pierce through multiple enemies
// Special abilities for max level
if (self.level === 3) {
self.executeThreshold = 0.25; // Execute enemies below 25% health
}
// Update visual appearance based on level
if (self.level === 2) {
towerGraphics.tint = 0x708090; // Slate gray
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xffd700; // Gold
} else if (self.level === 3) {
towerGraphics.tint = 0x2F4F4F; // Dark slate gray
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xff4500; // Orange red
}
};
self.canUpgrade = function () {
return self.level < self.maxLevel;
};
self.getUpgradeCost = function () {
return 80 + (self.level - 1) * 50; // Cost increases with level
};
self.upgrade = function () {
if (self.canUpgrade()) {
self.level++;
self.updateStats();
LK.effects.flashObject(self, 0xffd700, 500);
return true;
}
return false;
};
self.update = function () {
// Find target (prioritize units furthest away)
if (!self.target || !self.target.parent) {
self.target = null;
var bestUnit = null;
var longestDistance = 0;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.range && distance > longestDistance) {
longestDistance = distance;
bestUnit = unit;
}
}
self.target = bestUnit;
}
// Shoot at target with precision, critical hits, and piercing
if (self.target && self.shootCooldown <= 0) {
var bullet = new TowerBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = self.target;
// Check for critical hit
var isCritical = Math.random() < self.criticalChance;
bullet.damage = isCritical ? self.damage * 2 : self.damage;
// Execute ability for level 3 - instantly kill low health enemies
if (self.level === 3) {
var targetHealthPercent = self.target.health / self.target.maxHealth;
if (targetHealthPercent <= self.executeThreshold) {
bullet.execute = true; // Mark bullet for execution
bullet.damage = self.target.health + 1000; // Ensure instant kill
LK.effects.flashObject(self, 0xFF0000, 500); // Red flash for execute
}
}
bullet.speed = 10; // Faster bullet
bullet.pierceCount = self.pierceCount; // Add pierce capability
bullet.pierceRemaining = self.pierceCount;
if (isCritical) {
// Visual effect for critical hit
LK.effects.flashObject(self, 0xFFD700, 300);
}
towerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 150; // 2.5 second cooldown
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Tower is destroyed
}
return false;
};
return self;
});
var SniperUnit = Container.expand(function () {
var self = Container.call(this);
var unitGraphics = self.attachAsset('sniperUnitAsset', {
anchorX: 0.5,
anchorY: 0.5
});
unitGraphics.tint = 0x4B0082; // Indigo color for sniper unit
unitGraphics.scaleX = 1.0;
unitGraphics.scaleY = 1.3; // Taller appearance
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -42;
healthBarBg.scaleX = 0.9;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -42;
healthBar.scaleX = 0.9;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent * 0.9;
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00;
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00;
} else {
healthBar.tint = 0xff0000;
}
};
self.unitType = 'sniper';
self.health = 50;
self.maxHealth = 50;
self.speed = 1.2;
self.damage = 35;
self.range = 250; // Very long range
self.cost = 80;
self.shootCooldown = 0;
self.target = null;
self.lastX = 0;
self.lastY = 0;
self.currentWaypoint = 0;
self.isMovingToWaypoint = false;
// Create path through tower grid
var pathWaypoints = [];
pathWaypoints.push({
x: 400,
y: self.y
});
// Tower grid parameters
var gridStartX = 800;
var gridStartY = 600;
var gridSpacingX = 200;
var gridSpacingY = 200;
var gridRows = 8;
var gridCols = 4;
// Serpentine path through towers
for (var col = 0; col < gridCols; col++) {
if (col % 2 === 0) {
for (var row = 0; row < gridRows; row++) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
} else {
for (var row = gridRows - 1; row >= 0; row--) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
}
}
pathWaypoints.push({
x: 1600,
y: 1000
});
self.pathWaypoints = pathWaypoints;
self.followPath = function () {
if (self.isMovingToWaypoint) return;
if (self.currentWaypoint >= self.pathWaypoints.length) {
self.x += self.speed;
return;
}
var waypoint = self.pathWaypoints[self.currentWaypoint];
var distance = Math.sqrt(Math.pow(waypoint.x - self.x, 2) + Math.pow(waypoint.y - self.y, 2));
var duration = distance / self.speed * 16.67;
self.isMovingToWaypoint = true;
tween(self, {
x: waypoint.x,
y: waypoint.y
}, {
duration: duration,
easing: tween.linear,
onFinish: function onFinish() {
self.currentWaypoint++;
self.isMovingToWaypoint = false;
}
});
};
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
if (!self.target || !self.target.parent) {
self.target = null;
var closestTarget = null;
var closestDistance = Infinity;
// Prioritize towers over base due to long range
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower && tower.parent) {
var towerDistance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (towerDistance <= self.range && towerDistance < closestDistance) {
closestDistance = towerDistance;
closestTarget = tower;
}
}
}
// Check base if no towers in range
if (!closestTarget && enemyBase && enemyBase.parent) {
var baseDistance = Math.sqrt(Math.pow(enemyBase.x - self.x, 2) + Math.pow(enemyBase.y - self.y, 2));
if (baseDistance <= self.range) {
closestTarget = enemyBase;
closestDistance = baseDistance;
}
}
self.target = closestTarget;
}
if (self.target && self.shootCooldown <= 0) {
var bullet = new UnitBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = self.target;
bullet.damage = self.damage;
bullet.speed = 12; // Faster bullet
unitBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = 120; // Slow shooting rate
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
if (!self.target) {
self.followPath();
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
return true;
}
return false;
};
return self;
});
var TowerBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('towerBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.target = null;
self.damage = 30;
self.update = function () {
if (!self.target || !self.target.parent) {
// Target destroyed, remove bullet
return;
}
// 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 < 10) {
// Hit target
if (self.target.takeDamage) {
self.target.takeDamage(self.damage);
LK.getSound('hit').play();
// Handle splash damage
if (self.splashRadius && self.splashRadius > 0) {
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
if (unit !== self.target && unit.parent) {
var splashDistance = Math.sqrt(Math.pow(unit.x - self.target.x, 2) + Math.pow(unit.y - self.target.y, 2));
if (splashDistance <= self.splashRadius) {
unit.takeDamage(self.splashDamage || Math.floor(self.damage * 0.6));
LK.effects.flashObject(unit, 0xFF6600, 200);
}
}
}
// Visual splash effect
LK.effects.flashObject(self.target, 0xFF6600, 400);
}
}
// Handle piercing
if (self.pierceRemaining && self.pierceRemaining > 0) {
self.pierceRemaining--;
// Find next target in line
var nextTarget = null;
var minDistance = Infinity;
var bulletDirection = {
x: (self.target.x - self.x) / distance,
y: (self.target.y - self.y) / distance
};
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
if (unit !== self.target && unit.parent) {
var unitDistance = Math.sqrt(Math.pow(unit.x - self.target.x, 2) + Math.pow(unit.y - self.target.y, 2));
// Check if unit is roughly in the same direction
var unitDirection = {
x: (unit.x - self.target.x) / unitDistance,
y: (unit.y - self.target.y) / unitDistance
};
var dotProduct = bulletDirection.x * unitDirection.x + bulletDirection.y * unitDirection.y;
if (dotProduct > 0.7 && unitDistance < minDistance && unitDistance < 150) {
// Within piercing range
minDistance = unitDistance;
nextTarget = unit;
}
}
}
if (nextTarget) {
self.target = nextTarget;
// Continue moving towards new target
return;
}
}
return; // Bullet will be removed
}
// Normalize and move
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
};
return self;
});
var TowerSlot = Container.expand(function () {
var self = Container.call(this);
var slotGraphics = self.attachAsset('towerSlot', {
anchorX: 0.5,
anchorY: 0.5
});
slotGraphics.alpha = 0.3;
self.occupied = false;
self.tower = null;
self.placeTower = function () {
if (!self.occupied) {
var tower = new EnemyTower();
tower.x = self.x;
tower.y = self.y;
tower.slot = self;
tower.updateStats(); // Initialize stats properly
enemyTowers.push(tower);
game.addChild(tower);
self.occupied = true;
self.tower = tower;
slotGraphics.alpha = 0.1;
return tower;
}
return null;
};
self.removeTower = function () {
if (self.occupied && self.tower) {
self.occupied = false;
self.tower = null;
slotGraphics.alpha = 0.3;
}
};
return self;
});
var Trap = Container.expand(function () {
var self = Container.call(this);
var trapGraphics = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
trapGraphics.tint = 0x8B4513; // Brown color for trap
trapGraphics.scaleX = 0.8;
trapGraphics.scaleY = 0.8;
trapGraphics.alpha = 0.6; // Semi-transparent to show it's hidden
// Upgrade indicator (initially hidden)
var upgradeIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
upgradeIndicator.x = 15;
upgradeIndicator.y = -15;
upgradeIndicator.visible = false;
self.towerType = 'trap';
self.level = 1;
self.maxLevel = 3;
self.health = 1; // Traps are destroyed after one use
self.maxHealth = 1;
self.baseDamage = 60;
self.baseRange = 40;
self.damage = self.baseDamage; // High damage when triggered
self.range = self.baseRange; // Detection range for triggering
self.triggered = false;
self.cost = 50;
self.updateStats = function () {
var multiplier = 1 + (self.level - 1) * 0.8; // 80% increase per level
self.damage = Math.floor(self.baseDamage * multiplier);
self.range = Math.floor(self.baseRange * multiplier);
// Update visual appearance based on level
if (self.level === 2) {
trapGraphics.tint = 0xA0522D; // Darker brown
trapGraphics.scaleX = 1.0;
trapGraphics.scaleY = 1.0;
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xffd700; // Gold
} else if (self.level === 3) {
trapGraphics.tint = 0x8B0000; // Dark red
trapGraphics.scaleX = 1.2;
trapGraphics.scaleY = 1.2;
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xff4500; // Orange red
}
};
self.canUpgrade = function () {
return self.level < self.maxLevel && !self.triggered;
};
self.getUpgradeCost = function () {
return 30 + (self.level - 1) * 20; // Cost increases with level
};
self.upgrade = function () {
if (self.canUpgrade()) {
self.level++;
self.updateStats();
LK.effects.flashObject(self, 0xffd700, 500);
return true;
}
return false;
};
self.update = function () {
if (self.triggered) return; // Already triggered
// Check for player units in range
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.range) {
// Trigger trap
self.triggered = true;
unit.takeDamage(self.damage);
// Visual explosion effect
LK.effects.flashObject(self, 0xFF6600, 400);
LK.effects.flashObject(unit, 0xFF0000, 300);
// Create explosion visual
trapGraphics.tint = 0xFF6600;
trapGraphics.alpha = 1.0;
tween(trapGraphics, {
scaleX: 2.0,
scaleY: 2.0,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut
});
LK.getSound('hit').play();
return true; // Trap is destroyed
}
}
};
self.takeDamage = function (damage) {
// Traps take minimal damage from ranged attacks
self.health -= Math.max(1, Math.floor(damage * 0.1));
if (self.health <= 0) {
return true; // Trap is destroyed
}
return false;
};
return self;
});
var UnitBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('unitBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.target = null;
self.damage = 25;
self.update = function () {
if (!self.target || !self.target.parent) {
// Target destroyed, remove bullet
return;
}
// 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 < 10) {
// Hit target
if (self.target.takeDamage) {
self.target.takeDamage(self.damage);
LK.getSound('hit').play();
}
return; // Bullet will be removed
}
// Normalize and move
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
};
return self;
});
var Wall = Container.expand(function () {
var self = Container.call(this);
var wallGraphics = self.attachAsset('basicTowerAsset', {
anchorX: 0.5,
anchorY: 0.5
});
wallGraphics.tint = 0x696969; // Gray color for wall
wallGraphics.scaleX = 1.2;
wallGraphics.scaleY = 0.8; // Make it look more like a wall
// Health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -40;
// Health bar foreground
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
});
healthBar.x = 0;
healthBar.y = -40;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
if (healthPercent > 0.6) {
healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xffff00; // Yellow
} else {
healthBar.tint = 0xff0000; // Red
}
};
// Upgrade indicator (initially hidden)
var upgradeIndicator = self.attachAsset('upgradeIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
upgradeIndicator.x = 25;
upgradeIndicator.y = -25;
upgradeIndicator.visible = false;
self.towerType = 'wall';
self.level = 1;
self.maxLevel = 3;
self.baseHealth = 300;
self.baseBlockRadius = 60;
self.health = self.baseHealth; // High health to absorb damage
self.maxHealth = self.baseHealth;
self.blockRadius = self.baseBlockRadius; // Radius that blocks unit movement
self.cost = 40;
self.updateStats = function () {
var multiplier = 1 + (self.level - 1) * 0.6; // 60% increase per level
self.maxHealth = Math.floor(self.baseHealth * multiplier);
self.health = Math.min(self.health, self.maxHealth);
self.blockRadius = Math.floor(self.baseBlockRadius * multiplier);
// Update visual appearance based on level
if (self.level === 2) {
wallGraphics.tint = 0x808080; // Darker gray
wallGraphics.scaleX = 1.4;
wallGraphics.scaleY = 1.0;
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xffd700; // Gold
} else if (self.level === 3) {
wallGraphics.tint = 0x2F4F4F; // Dark slate gray
wallGraphics.scaleX = 1.6;
wallGraphics.scaleY = 1.2;
upgradeIndicator.visible = true;
upgradeIndicator.tint = 0xff4500; // Orange red
}
};
self.canUpgrade = function () {
return self.level < self.maxLevel;
};
self.getUpgradeCost = function () {
return 25 + (self.level - 1) * 15; // Cost increases with level
};
self.upgrade = function () {
if (self.canUpgrade()) {
self.level++;
self.updateStats();
LK.effects.flashObject(self, 0xffd700, 500);
return true;
}
return false;
};
self.update = function () {
// Mine ability for level 3
if (self.level === 3 && self.mineCooldown <= 0) {
// Check if any enemies are touching the wall
var enemiesInContact = 0;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.blockRadius + 20) {
enemiesInContact++;
}
}
// Trigger mine explosion if enemies are touching the wall
if (enemiesInContact > 0) {
// Damage all enemies within mine radius
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var distance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distance <= self.mineRadius) {
unit.takeDamage(self.mineDamage);
LK.effects.flashObject(unit, 0xFF6600, 400);
}
}
// Visual mine explosion effect
wallGraphics.tint = 0xFF6600; // Orange explosion color
LK.effects.flashObject(self, 0xFF6600, 600);
// Screen flash effect for mine explosion
LK.effects.flashScreen(0xFF6600, 200);
self.mineCooldown = 600; // 10 second cooldown
// Return to normal color after effect
var self_ref = self;
LK.setTimeout(function () {
self_ref.wallGraphics.tint = 0x2F4F4F; // Back to level 3 color
}, 600);
}
}
// Update mine cooldown
if (self.mineCooldown > 0) {
self.mineCooldown--;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
return true; // Wall is destroyed
}
return false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2d5016
});
/****
* Game Code
****/
// Game arrays to track entities
// Initialize game assets
var playerUnits = [];
var enemyTowers = [];
var unitBullets = [];
var towerBullets = [];
var towerSlots = [];
var enemyBase;
// Game resources
var coins = 100;
var unitCost = 250;
// Ability system
var abilities = {
lightning: {
name: 'Lightning Strike',
cooldown: 900,
// 15 seconds
currentCooldown: 0,
description: 'Strikes all towers with lightning'
},
heal: {
name: 'Mass Heal',
cooldown: 600,
// 10 seconds
currentCooldown: 0,
description: 'Heals all your units to full health'
},
freeze: {
name: 'Time Freeze',
cooldown: 1200,
// 20 seconds
currentCooldown: 0,
description: 'Freezes all towers for 5 seconds'
},
boost: {
name: 'Unit Boost',
cooldown: 480,
// 8 seconds
currentCooldown: 0,
description: 'Doubles unit damage for 8 seconds'
}
};
// UI Elements
var coinsText = new Text2('Coins: ' + coins, {
size: 80,
fill: 0xFFD700
});
coinsText.anchor.set(1, 0);
coinsText.x = -20; // Small margin from right edge
coinsText.y = 20; // Small margin from top
LK.gui.topRight.addChild(coinsText);
var coinIcon = LK.getAsset('coinIcon', {
anchorX: 0.5,
anchorY: 0.5
});
coinIcon.x = -coinsText.width - 60; // Position to left of text
coinIcon.y = 45; // Align with text
LK.gui.topRight.addChild(coinIcon);
var healthText = new Text2('Base Health: 500', {
size: 50,
fill: 0xFFFFFF
});
healthText.anchor.set(0.5, 0);
LK.gui.top.addChild(healthText);
// Unit spawn buttons
var basicButton = new Text2('Basic (300)', {
size: 40,
fill: 0x87CEEB
});
basicButton.anchor.set(0.5, 1);
basicButton.x = -300;
basicButton.interactive = true;
basicButton.down = function () {
if (coins >= 300) {
spawnUnit('basic');
}
};
LK.gui.bottom.addChild(basicButton);
var fastButton = new Text2('Fast (450)', {
size: 40,
fill: 0x32CD32
});
fastButton.anchor.set(0.5, 1);
fastButton.x = -100;
fastButton.interactive = true;
fastButton.down = function () {
if (coins >= 450) {
spawnUnit('fast');
}
};
LK.gui.bottom.addChild(fastButton);
var heavyButton = new Text2('Heavy (650)', {
size: 40,
fill: 0x8B4513
});
heavyButton.anchor.set(0.5, 1);
heavyButton.x = 100;
heavyButton.interactive = true;
heavyButton.down = function () {
if (coins >= 650) {
spawnUnit('heavy');
}
};
LK.gui.bottom.addChild(heavyButton);
var sniperButton = new Text2('Sniper (600)', {
size: 40,
fill: 0x4B0082
});
sniperButton.anchor.set(0.5, 1);
sniperButton.x = 200;
sniperButton.interactive = true;
sniperButton.down = function () {
if (coins >= 600) {
spawnUnit('sniper');
}
};
LK.gui.bottom.addChild(sniperButton);
var meleeButton = new Text2('Melee (250)', {
size: 40,
fill: 0xFF6347
});
meleeButton.anchor.set(0.5, 1);
meleeButton.x = 400;
meleeButton.interactive = true;
meleeButton.down = function () {
if (coins >= 250) {
spawnUnit('melee');
}
};
LK.gui.bottom.addChild(meleeButton);
// Ability buttons
var lightningButton = new Text2('Lightning', {
size: 35,
fill: 0xFFFF00
});
lightningButton.anchor.set(0, 0.5);
lightningButton.x = 20;
lightningButton.y = -200;
lightningButton.interactive = true;
lightningButton.down = function () {
useAbility('lightning');
};
LK.gui.left.addChild(lightningButton);
var healButton = new Text2('Heal All', {
size: 35,
fill: 0x00FF00
});
healButton.anchor.set(0, 0.5);
healButton.x = 20;
healButton.y = -100;
healButton.interactive = true;
healButton.down = function () {
useAbility('heal');
};
LK.gui.left.addChild(healButton);
var freezeButton = new Text2('Freeze', {
size: 35,
fill: 0x00FFFF
});
freezeButton.anchor.set(0, 0.5);
freezeButton.x = 20;
freezeButton.y = 0;
freezeButton.interactive = true;
freezeButton.down = function () {
useAbility('freeze');
};
LK.gui.left.addChild(freezeButton);
var boostButton = new Text2('Boost', {
size: 35,
fill: 0xFF8C00
});
boostButton.anchor.set(0, 0.5);
boostButton.x = 20;
boostButton.y = 100;
boostButton.interactive = true;
boostButton.down = function () {
useAbility('boost');
};
LK.gui.left.addChild(boostButton);
// Create enemy base
enemyBase = game.addChild(new EnemyBase());
enemyBase.x = 1800;
enemyBase.y = 2732 / 2;
// Create tower slots in a grid
var gridStartX = 800;
var gridStartY = 600;
var gridSpacingX = 200;
var gridSpacingY = 200;
var gridRows = 8;
var gridCols = 4;
for (var row = 0; row < gridRows; row++) {
for (var col = 0; col < gridCols; col++) {
var slot = new TowerSlot();
slot.x = gridStartX + col * gridSpacingX;
slot.y = gridStartY + row * gridSpacingY;
towerSlots.push(slot);
game.addChild(slot);
}
}
// Initially place some towers in random slots
var initialTowerCount = 8;
var placedTowers = 0;
while (placedTowers < initialTowerCount && placedTowers < towerSlots.length) {
var randomSlotIndex = Math.floor(Math.random() * towerSlots.length);
var slot = towerSlots[randomSlotIndex];
if (slot.placeTower()) {
// Initialize health bar for the newly placed tower
if (slot.tower && slot.tower.updateHealthBar) {
slot.tower.updateHealthBar();
}
placedTowers++;
}
}
function useAbility(abilityType) {
var ability = abilities[abilityType];
if (!ability || ability.currentCooldown > 0) {
return false;
}
ability.currentCooldown = ability.cooldown;
// Enhanced visual feedback - pulse effect on ability button and coins
switch (abilityType) {
case 'lightning':
// Enhanced lightning visual effects
tween(lightningButton, {
scaleX: 1.3,
scaleY: 1.3,
alpha: 0.7
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(lightningButton, {
scaleX: 1.0,
scaleY: 1.0,
alpha: 1.0
}, {
duration: 300,
easing: tween.bounceOut
});
}
});
// Strike all towers with lightning
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
tower.takeDamage(80);
LK.effects.flashObject(tower, 0xFFFF00, 500);
// Add scaling effect to towers when hit by lightning
tween(tower, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(tower, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.elasticOut
});
}
});
}
// Strike the base too
if (enemyBase) {
enemyBase.takeDamage(120);
LK.effects.flashObject(enemyBase, 0xFFFF00, 800);
tween(enemyBase, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(enemyBase, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.elasticOut
});
}
});
}
LK.effects.flashScreen(0xFFFF00, 300);
break;
case 'heal':
// Enhanced heal visual effects
tween(healButton, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0x00FFFF
}, {
duration: 250,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(healButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0x00FF00
}, {
duration: 400,
easing: tween.bounceOut
});
}
});
// Heal all units to full health
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
unit.health = unit.maxHealth;
unit.updateHealthBar();
LK.effects.flashObject(unit, 0x00FF00, 400);
// Add pulsing effect to healed units
tween(unit, {
scaleX: 1.15,
scaleY: 1.15,
alpha: 0.8
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(unit, {
scaleX: 1.0,
scaleY: 1.0,
alpha: 1.0
}, {
duration: 300,
easing: tween.bounceOut
});
}
});
}
LK.effects.flashScreen(0x00FF00, 200);
break;
case 'freeze':
// Enhanced freeze visual effects
tween(freezeButton, {
scaleX: 1.4,
scaleY: 1.4,
rotation: 0.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(freezeButton, {
scaleX: 1.0,
scaleY: 1.0,
rotation: 0
}, {
duration: 500,
easing: tween.elasticOut
});
}
});
// Freeze all towers for 5 seconds
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
tower.frozenUntil = LK.ticks + 300; // 5 seconds
tower.originalTint = tower.tint || 0xFFFFFF;
LK.effects.flashObject(tower, 0x00FFFF, 600);
// Add shaking effect to frozen towers
tween(tower, {
x: tower.x + 5,
y: tower.y + 5
}, {
duration: 100,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(tower, {
x: tower.x - 5,
y: tower.y - 5
}, {
duration: 100,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(tower, {
x: tower.x,
y: tower.y
}, {
duration: 100,
easing: tween.easeOut
});
}
});
}
});
}
// Freeze the base too
if (enemyBase) {
enemyBase.frozenUntil = LK.ticks + 300;
LK.effects.flashObject(enemyBase, 0x00FFFF, 600);
}
LK.effects.flashScreen(0x00FFFF, 400);
break;
case 'boost':
// Enhanced boost visual effects
tween(boostButton, {
scaleX: 1.5,
scaleY: 1.5,
tint: 0xFFD700
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(boostButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFF8C00
}, {
duration: 400,
easing: tween.bounceOut
});
}
});
// Double unit damage for 8 seconds
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
if (!unit.boostedUntil) {
unit.originalDamage = unit.damage;
}
unit.damage = unit.originalDamage * 2;
unit.boostedUntil = LK.ticks + 480; // 8 seconds
LK.effects.flashObject(unit, 0xFF8C00, 500);
// Add growing effect to boosted units
tween(unit, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFFD700
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(unit, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 200,
easing: tween.easeInOut
});
}
});
}
LK.effects.flashScreen(0xFF8C00, 300);
break;
}
return true;
}
function spawnUnit(unitType) {
if (!unitType) unitType = 'basic';
var unitClass = null;
var cost = 40;
switch (unitType) {
case 'basic':
unitClass = BasicUnit;
cost = 300;
break;
case 'fast':
unitClass = FastUnit;
cost = 450;
break;
case 'heavy':
unitClass = HeavyUnit;
cost = 650;
break;
case 'sniper':
unitClass = SniperUnit;
cost = 600;
break;
case 'melee':
unitClass = MeleeUnit;
cost = 250;
break;
default:
unitClass = BasicUnit;
cost = 200;
break;
}
if (coins >= cost && unitClass) {
coins -= cost;
coinsText.setText('Coins: ' + coins);
coinIcon.x = -coinsText.width - 60;
// Spawn 5 units
for (var unitIndex = 0; unitIndex < 5; unitIndex++) {
var unit = new unitClass();
unit.x = 100;
unit.y = 500 + (playerUnits.length + unitIndex) % 8 * 100;
// Create path that goes through all tower positions
var pathWaypoints = [];
// Start from spawn position
pathWaypoints.push({
x: 400,
y: unit.y
});
// Add waypoints to pass through tower grid
var gridStartX = 800;
var gridStartY = 600;
var gridSpacingX = 200;
var gridSpacingY = 200;
var gridRows = 8;
var gridCols = 4;
// Create serpentine path through tower grid
for (var col = 0; col < gridCols; col++) {
if (col % 2 === 0) {
// Go down for even columns
for (var row = 0; row < gridRows; row++) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
} else {
// Go up for odd columns
for (var row = gridRows - 1; row >= 0; row--) {
pathWaypoints.push({
x: gridStartX + col * gridSpacingX,
y: gridStartY + row * gridSpacingY
});
}
}
}
// Final waypoint towards the base
pathWaypoints.push({
x: 1600,
y: 1366
});
unit.pathWaypoints = pathWaypoints;
playerUnits.push(unit);
game.addChild(unit);
unit.updateHealthBar();
}
LK.getSound('unitSpawn').play();
}
}
// Coin generation timer
var coinTimer = 0;
// Enemy AI system
var enemyAI = {
coins: 400,
towerCost: 75,
lastTowerPlacement: 0,
placementCooldown: 180,
// 3 seconds at 60fps
update: function update() {
// Generate coins for enemy AI
if (LK.ticks % 120 === 0) {
// Every 2 seconds
this.coins += 75;
}
// Force spending if AI has too many coins
var forceSpend = this.coins >= 1000;
// Check if should place new towers
if (this.coins >= this.towerCost && LK.ticks - this.lastTowerPlacement > this.placementCooldown || forceSpend) {
this.considerTowerPlacement();
}
// Upgrade existing towers (with forced spending)
this.considerTowerUpgrades(forceSpend);
// Repair damaged towers
this.repairTowers();
},
considerTowerPlacement: function considerTowerPlacement() {
// First priority: Check if we need heal towers near damaged towers
var needsHealTower = false;
var healSlot = null;
var damagedTowerNeedingHeal = null;
// Find damaged towers that need healing
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower.health < tower.maxHealth * 0.6) {
// If tower is below 60% health
// Check if there's already a heal tower nearby
var hasHealerNearby = false;
for (var j = 0; j < enemyTowers.length; j++) {
var otherTower = enemyTowers[j];
if (otherTower.towerType === 'heal') {
var distance = Math.sqrt(Math.pow(tower.x - otherTower.x, 2) + Math.pow(tower.y - otherTower.y, 2));
if (distance <= 200) {
// Within healing range
hasHealerNearby = true;
break;
}
}
}
if (!hasHealerNearby) {
damagedTowerNeedingHeal = tower;
needsHealTower = true;
break;
}
}
}
// If we need a heal tower and can afford it, find best slot near damaged tower
if (needsHealTower && damagedTowerNeedingHeal && this.coins >= 110) {
var bestDistance = Infinity;
var forceSpendHeal = this.coins >= 1000;
for (var i = 0; i < towerSlots.length; i++) {
var slot = towerSlots[i];
if (!slot.occupied) {
// Check if there are any units too close to this heal slot (within 120 pixels)
var hasUnitsNearbyHeal = false;
for (var k = 0; k < playerUnits.length; k++) {
var unit = playerUnits[k];
var unitDistance = Math.sqrt(Math.pow(unit.x - slot.x, 2) + Math.pow(unit.y - slot.y, 2));
if (unitDistance < 120) {
// Don't build heal tower if units are too close
hasUnitsNearbyHeal = true;
break;
}
}
// Skip this slot if units are too close (unless forced to spend)
if (hasUnitsNearbyHeal && !forceSpendHeal) {
continue;
}
var distance = Math.sqrt(Math.pow(damagedTowerNeedingHeal.x - slot.x, 2) + Math.pow(damagedTowerNeedingHeal.y - slot.y, 2));
if (distance < bestDistance && distance <= 200) {
// Within heal range
bestDistance = distance;
healSlot = slot;
}
}
}
// Place heal tower if we found a good slot
if (healSlot) {
var tower = new HealTower();
tower.x = healSlot.x;
tower.y = healSlot.y;
tower.slot = healSlot;
enemyTowers.push(tower);
game.addChild(tower);
healSlot.occupied = true;
healSlot.tower = tower;
healSlot.attachAsset('towerSlot', {}).alpha = 0.1;
this.coins -= 110;
this.lastTowerPlacement = LK.ticks;
// Initialize health bar
if (tower.updateHealthBar) {
tower.updateHealthBar();
}
// Visual feedback
LK.effects.flashObject(healSlot, 0x00ff00, 500);
return; // Exit early after placing heal tower
}
}
// Second priority: Find empty slots closest to player units for combat towers
var bestSlot = null;
var minDistance = Infinity;
var forceSpend = this.coins >= 1000;
for (var i = 0; i < towerSlots.length; i++) {
var slot = towerSlots[i];
if (!slot.occupied) {
// Check if there are any units too close to this slot (within 120 pixels)
var hasUnitsNearby = false;
for (var k = 0; k < playerUnits.length; k++) {
var unit = playerUnits[k];
var unitDistance = Math.sqrt(Math.pow(unit.x - slot.x, 2) + Math.pow(unit.y - slot.y, 2));
if (unitDistance < 120) {
// Don't build if units are too close
hasUnitsNearby = true;
break;
}
}
// Skip this slot if units are too close (unless forced to spend)
if (hasUnitsNearby && !forceSpend) {
continue;
}
// Calculate average distance to all player units
var totalDistance = 0;
var unitCount = 0;
for (var j = 0; j < playerUnits.length; j++) {
var unit = playerUnits[j];
var distance = Math.sqrt(Math.pow(unit.x - slot.x, 2) + Math.pow(unit.y - slot.y, 2));
totalDistance += distance;
unitCount++;
}
if (unitCount > 0) {
var avgDistance = totalDistance / unitCount;
// Force placement if we have too many coins, otherwise use distance restriction
if (avgDistance < minDistance && (avgDistance < 400 || forceSpend)) {
minDistance = avgDistance;
bestSlot = slot;
}
} else if (forceSpend && !bestSlot) {
// If forced to spend and no players around, pick any empty slot
bestSlot = slot;
}
}
}
// Place tower at best location
if (bestSlot) {
// Strategic placement: Check if we should place walls or traps based on unit density
var nearbyUnits = 0;
var averageUnitDistance = 0;
for (var k = 0; k < playerUnits.length; k++) {
var unit = playerUnits[k];
var unitDist = Math.sqrt(Math.pow(unit.x - bestSlot.x, 2) + Math.pow(unit.y - bestSlot.y, 2));
if (unitDist <= 300) {
nearbyUnits++;
averageUnitDistance += unitDist;
}
}
if (nearbyUnits > 0) {
averageUnitDistance /= nearbyUnits;
}
// Choose tower type based on strategy and available coins
var towerTypes = [];
// Add defensive structures first if units are close
if (nearbyUnits >= 3 && this.coins >= 40) towerTypes.push('wall');
if (nearbyUnits >= 2 && averageUnitDistance < 150 && this.coins >= 50) towerTypes.push('trap');
// Add combat towers
if (this.coins >= 75) towerTypes.push('basic');
if (this.coins >= 85) towerTypes.push('slow');
if (this.coins >= 95) towerTypes.push('poison');
if (this.coins >= 100) towerTypes.push('fast');
if (this.coins >= 125) towerTypes.push('sniper');
if (this.coins >= 130) towerTypes.push('area');
if (this.coins >= 140) towerTypes.push('laser');
if (this.coins >= 150) towerTypes.push('heavy');
if (this.coins >= 200) towerTypes.push('resurrection');
if (towerTypes.length > 0) {
var randomType = towerTypes[Math.floor(Math.random() * towerTypes.length)];
var tower = null;
var cost = 75;
switch (randomType) {
case 'wall':
tower = new Wall();
cost = 40;
break;
case 'trap':
tower = new Trap();
cost = 50;
break;
case 'basic':
tower = new BasicTower();
cost = 75;
break;
case 'slow':
tower = new SlowTower();
cost = 85;
break;
case 'poison':
tower = new PoisonTower();
cost = 95;
break;
case 'fast':
tower = new FastTower();
cost = 100;
break;
case 'sniper':
tower = new SniperTower();
cost = 125;
break;
case 'area':
tower = new AreaTower();
cost = 130;
break;
case 'laser':
tower = new LaserTower();
cost = 140;
break;
case 'heavy':
tower = new HeavyTower();
cost = 150;
break;
case 'resurrection':
tower = new ResurrectionTower();
cost = 200;
break;
}
if (tower) {
tower.x = bestSlot.x;
tower.y = bestSlot.y;
tower.slot = bestSlot;
enemyTowers.push(tower);
game.addChild(tower);
bestSlot.occupied = true;
bestSlot.tower = tower;
bestSlot.attachAsset('towerSlot', {}).alpha = 0.1;
this.coins -= cost;
this.lastTowerPlacement = LK.ticks;
// Initialize health bar for towers that have them
if (tower.updateHealthBar) {
tower.updateHealthBar();
}
// Visual feedback
var feedbackColor = 0xff0000; // Red for combat towers
if (randomType === 'wall') feedbackColor = 0x696969; // Gray for walls
if (randomType === 'trap') feedbackColor = 0x8B4513; // Brown for traps
LK.effects.flashObject(bestSlot, feedbackColor, 500);
}
}
}
},
considerTowerUpgrades: function considerTowerUpgrades(forceSpend) {
// Find towers that can be upgraded and are in combat zones
var upgradeTargets = [];
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower.canUpgrade && tower.canUpgrade()) {
var upgradeCost = tower.getUpgradeCost();
if (this.coins >= upgradeCost) {
// Check if tower is in an active combat zone (has nearby player units)
var nearbyUnits = 0;
var priority = 0;
for (var j = 0; j < playerUnits.length; j++) {
var unit = playerUnits[j];
var distance = Math.sqrt(Math.pow(unit.x - tower.x, 2) + Math.pow(unit.y - tower.y, 2));
// Special range consideration for traps and walls
var checkRange = tower.range + 100;
if (tower.towerType === 'trap') {
checkRange = tower.range + 50; // Shorter range check for traps
} else if (tower.towerType === 'wall') {
checkRange = tower.blockRadius + 80; // Check block radius for walls
}
if (distance <= checkRange) {
nearbyUnits++;
}
}
// Calculate upgrade priority with special consideration for traps and walls
if (nearbyUnits > 0) {
priority = nearbyUnits * 10 + (tower.maxLevel - tower.level + 1) * 5;
// Higher priority for traps and walls in active zones
if (tower.towerType === 'trap' || tower.towerType === 'wall') {
priority += nearbyUnits * 5; // Extra priority for defensive structures
}
} else if (this.coins > upgradeCost * 3 || forceSpend) {
priority = 1; // Low priority for non-combat upgrades
}
if (priority > 0) {
upgradeTargets.push({
tower: tower,
cost: upgradeCost,
priority: priority
});
}
}
}
}
// Sort by priority and upgrade the best candidate
if (upgradeTargets.length > 0) {
upgradeTargets.sort(function (a, b) {
return b.priority - a.priority;
});
var bestTarget = upgradeTargets[0];
if (bestTarget.tower.upgrade && bestTarget.tower.upgrade()) {
this.coins -= bestTarget.cost;
// Visual feedback
LK.effects.flashObject(bestTarget.tower, 0xFFD700, 600);
}
}
},
repairTowers: function repairTowers() {
// Find damaged towers and repair them
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower.health < tower.maxHealth * 0.5 && this.coins >= 25) {
tower.health = Math.min(tower.health + 25, tower.maxHealth);
this.coins -= 25;
LK.effects.flashObject(tower, 0x00ff00, 300);
break; // Only repair one tower per update
}
}
}
};
// Touch controls for spawning units
game.down = function (x, y, obj) {
if (coins >= 300) {
spawnUnit('basic');
}
};
game.update = function () {
// Update enemy AI
enemyAI.update();
// Generate coins over time
coinTimer++;
if (coinTimer >= 180) {
// Every 3 seconds
coins += 50;
coinsText.setText('Coins: ' + coins);
coinIcon.x = -coinsText.width - 60; // Update icon position
coinTimer = 0;
}
// Update unit bullets
for (var i = unitBullets.length - 1; i >= 0; i--) {
var bullet = unitBullets[i];
if (!bullet.target || !bullet.target.parent) {
bullet.destroy();
unitBullets.splice(i, 1);
continue;
}
var dx = bullet.target.x - bullet.x;
var dy = bullet.target.y - bullet.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 15) {
// Hit target
var isDead = bullet.target.takeDamage(bullet.damage);
if (isDead) {
// Remove from appropriate array
if (bullet.target === enemyBase) {
// Player wins!
LK.effects.flashScreen(0x00ff00, 2000);
LK.showYouWin();
return;
} else {
// Remove tower
for (var j = 0; j < enemyTowers.length; j++) {
if (enemyTowers[j] === bullet.target) {
// Calculate money reward based on tower type and level
var moneyReward = 225; // Base reward (75 * 3)
if (enemyTowers[j].towerType) {
switch (enemyTowers[j].towerType) {
case 'wall':
moneyReward = 60 + (enemyTowers[j].level ? (enemyTowers[j].level - 1) * 45 : 0);
break;
case 'trap':
moneyReward = 75 + (enemyTowers[j].level ? (enemyTowers[j].level - 1) * 60 : 0);
break;
case 'slow':
moneyReward = 255;
break;
case 'poison':
moneyReward = 285;
break;
case 'fast':
moneyReward = 300;
break;
case 'heal':
moneyReward = 330;
break;
case 'sniper':
moneyReward = 375;
break;
case 'area':
moneyReward = 390;
break;
case 'laser':
moneyReward = 420;
break;
case 'heavy':
moneyReward = 450;
break;
case 'resurrection':
moneyReward = 600;
break;
default:
moneyReward = 225;
break;
}
} else if (enemyTowers[j].level) {
// For upgradeable towers, reward based on level
moneyReward = 225 + (enemyTowers[j].level - 1) * 150;
}
// Clean up slot if tower has one
if (enemyTowers[j].slot) {
// Remember tower type for potential resurrection
enemyTowers[j].slot.lastTowerType = enemyTowers[j].towerType;
enemyTowers[j].slot.removeTower();
}
enemyTowers[j].destroy();
enemyTowers.splice(j, 1);
coins += moneyReward; // Money reward for destroying tower
coinsText.setText('Coins: ' + coins);
coinIcon.x = -coinsText.width - 60; // Update icon position
break;
}
}
}
}
bullet.destroy();
unitBullets.splice(i, 1);
}
}
// Handle unit status effects (slow, poison)
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
// Handle slow effect restoration
if (unit.slowEndTime && LK.ticks >= unit.slowEndTime) {
if (unit.originalSpeed) {
unit.speed = unit.originalSpeed;
unit.originalSpeed = null;
}
unit.slowEndTime = null;
}
// Handle poison damage over time
if (unit.poisonEndTime && LK.ticks >= unit.poisonEndTime) {
unit.poisonDamage = null;
unit.poisonEndTime = null;
unit.lastPoisonTick = null;
} else if (unit.poisonDamage && unit.lastPoisonTick && LK.ticks - unit.lastPoisonTick >= 60) {
// Apply poison damage every second
unit.takeDamage(unit.poisonDamage);
unit.lastPoisonTick = LK.ticks;
LK.effects.flashObject(unit, 0x228B22, 200);
}
}
// Update tower bullets
for (var i = towerBullets.length - 1; i >= 0; i--) {
var bullet = towerBullets[i];
if (!bullet.target || !bullet.target.parent) {
bullet.destroy();
towerBullets.splice(i, 1);
continue;
}
var dx = bullet.target.x - bullet.x;
var dy = bullet.target.y - bullet.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 15) {
// Hit target
var isDead = bullet.target.takeDamage(bullet.damage);
if (isDead) {
// Remove unit
for (var j = 0; j < playerUnits.length; j++) {
if (playerUnits[j] === bullet.target) {
playerUnits[j].destroy();
playerUnits.splice(j, 1);
break;
}
}
}
bullet.destroy();
towerBullets.splice(i, 1);
}
}
// Lose condition removed - player can no longer lose
// Process trap damage and cleanup
for (var i = enemyTowers.length - 1; i >= 0; i--) {
var tower = enemyTowers[i];
if (tower.towerType === 'trap' && tower.triggered) {
// Clean up slot if tower has one
if (tower.slot) {
tower.slot.removeTower();
}
tower.destroy();
enemyTowers.splice(i, 1);
}
}
// Update base health display
if (enemyBase) {
healthText.setText('Base Health: ' + enemyBase.health);
}
// Update ability cooldowns and button display
for (var abilityName in abilities) {
var ability = abilities[abilityName];
if (ability.currentCooldown > 0) {
ability.currentCooldown--;
}
}
// Update ability button texts and colors
var canUseLightning = abilities.lightning.currentCooldown <= 0;
lightningButton.fill = canUseLightning ? 0xFFFF00 : 0x666666;
lightningButton.setText(abilities.lightning.currentCooldown > 0 ? 'Lightning (' + Math.ceil(abilities.lightning.currentCooldown / 60) + 's)' : 'Lightning');
var canUseHeal = abilities.heal.currentCooldown <= 0;
healButton.fill = canUseHeal ? 0x00FF00 : 0x666666;
healButton.setText(abilities.heal.currentCooldown > 0 ? 'Heal (' + Math.ceil(abilities.heal.currentCooldown / 60) + 's)' : 'Heal All');
var canUseFreeze = abilities.freeze.currentCooldown <= 0;
freezeButton.fill = canUseFreeze ? 0x00FFFF : 0x666666;
freezeButton.setText(abilities.freeze.currentCooldown > 0 ? 'Freeze (' + Math.ceil(abilities.freeze.currentCooldown / 60) + 's)' : 'Freeze');
var canUseBoost = abilities.boost.currentCooldown <= 0;
boostButton.fill = canUseBoost ? 0xFF8C00 : 0x666666;
boostButton.setText(abilities.boost.currentCooldown > 0 ? 'Boost (' + Math.ceil(abilities.boost.currentCooldown / 60) + 's)' : 'Boost');
// Handle frozen towers (disable shooting)
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (tower.frozenUntil && LK.ticks >= tower.frozenUntil) {
tower.frozenUntil = null;
}
}
// Handle frozen base
if (enemyBase && enemyBase.frozenUntil && LK.ticks >= enemyBase.frozenUntil) {
enemyBase.frozenUntil = null;
}
// Handle unit boost expiration
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
if (unit.boostedUntil && LK.ticks >= unit.boostedUntil) {
unit.damage = unit.originalDamage || unit.damage / 2;
unit.boostedUntil = null;
}
}
};
coinIcon. In-Game asset. 2d. High contrast. No shadows
enemyBase. In-Game asset. 2d. High contrast. No shadows
enemyTower. In-Game asset. 2d. High contrast. No shadows
towerBullet. In-Game asset. 2d. High contrast. No shadows
towerSlot. In-Game asset. 2d. High contrast. No shadows
upgradeIndicator. In-Game asset. 2d. High contrast. No shadows
heavyTowerAsset. In-Game asset. 2d. High contrast. No shadows
fastTowerAsset. In-Game asset. 2d. High contrast. No shadows
healTowerAsset. In-Game asset. 2d. High contrast. No shadows
sniperTowerAsset. In-Game asset. 2d. High contrast. No shadows
areaTowerAsset. In-Game asset. 2d. High contrast. No shadows
meleeUnitAsset. In-Game asset. 2d. High contrast. No shadows
laserTowerAsset. In-Game asset. 2d. High contrast. No shadows
poisonTowerAsset. In-Game asset. 2d. High contrast. No shadows
slowTowerAsset. In-Game asset. 2d. High contrast. No shadows
resurrectionTowerAsset. In-Game asset. 2d. High contrast. No shadows