User prompt
fes que si la ai arriba a 1000 gasti per forza coloci una torra o millori
User prompt
fes que si te torras danyadas posi una torra de curacio aprop
User prompt
posa una barra de vida a las torras
User prompt
posa a cada torra un assets difarent
User prompt
crea torras que curan a altras torras i torras que fan dany daria
User prompt
fes que cada cop que mato una torra emb donan diners
User prompt
Please fix the bug: 'TypeError: tower.canUpgrade is not a function' in or related to this line: 'if (tower.canUpgrade()) {' Line Number: 1623
User prompt
fes que el cami que seguiexen las units pasi per totas las torras
User prompt
crea varias clases de units
User prompt
crea varias classes de torres
User prompt
fes que las units ataquin a la base
User prompt
fes visibla las monedas que tinc
User prompt
fes que las torras es puguin millorar
User prompt
posa una barra de vida a las units
User prompt
fes que els meus units no ataquin
User prompt
treu la vida a las torres
User prompt
fes que las defensas no es puguin atacar
User prompt
fes que el enemic defensi la base
User prompt
fes casellas per posar torres que posara el enemic
User prompt
fes un cami per las units ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
crea un joc de dafensa la base pero tu ets el que ataca
User prompt
Base Assault
Initial prompt
crea un joc de dafensa la base pero tu ets el que ataca
/**** * 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