User prompt
las habilitats no tenan preu
User prompt
fes visual al gastar una habilitat ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
crea un asset per la torra que reanima las torras
User prompt
crea habilitats per mi
User prompt
fes que els diners emb donguin mes
User prompt
borra que jo pugui perdre
User prompt
fes el joc mes dificil
User prompt
fes que quan la vida baixa de 0 mort la torra o la unit
User prompt
al comprar una unit fes que apareixin 5 pero fes que el preu sigui per 5
User prompt
al comprar una unit fes que apareixin 5
User prompt
crea una torra que torna a la vida a altras torras
User prompt
posa habilitats a els murs hi ha las minas
User prompt
fes que las trampas i els murs es puguin millorar
User prompt
crea trampas i murs per la ai
User prompt
fes que la base tambe dispari
User prompt
la ultima millora dona una habilitat a la torra
User prompt
crea novas milloras per las torras
User prompt
fes un asset per cada torra
User prompt
fes mes classes de torras
User prompt
fes que la ai no contrueixi si hi ha units a prop
User prompt
fes la unit de mele mes lenta pero mes barata
User prompt
fes que las units si tenan rang per atacar es deixin de moura
User prompt
fes que las torras de curacio curin la vida de las torras que estiguin al costat ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
fes que cada unit tingui el seu asset
User prompt
crea units a mele
/**** * 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); // 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 () { // 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 (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; 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; 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 = 80; self.maxHealth = 80; self.speed = 2; self.damage = 20; 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) { 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 = 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 = 500; self.maxHealth = 500; 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 = 150; self.maxHealth = 150; self.baseRange = 200; self.baseDamage = 30; 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 // 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 () { // 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) { // Shoot multiple bullets based on level for (var shotIndex = 0; shotIndex < self.multiShot; shotIndex++) { var bullet = new TowerBullet(); bullet.x = self.x; bullet.y = self.y; // For multi-shot, slightly spread the bullets if (self.multiShot > 1) { var angleOffset = (shotIndex - (self.multiShot - 1) / 2) * 0.3; // Small angle spread 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(); self.shootCooldown = 30; // 0.5 second cooldown (very fast) } 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 = 60; self.maxHealth = 60; self.speed = 4; self.damage = 15; 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 // 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 () { // 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 = 150; self.maxHealth = 150; self.speed = 1; self.damage = 40; 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 = 120; self.maxHealth = 120; self.speed = 1.5; self.damage = 35; 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 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 // 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; 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 = 70; self.maxHealth = 70; self.speed = 1.5; self.damage = 50; 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 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; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2d5016 }); /**** * Game Code ****/ // Initialize game assets // Game arrays to track entities var playerUnits = []; var enemyTowers = []; var unitBullets = []; var towerBullets = []; var towerSlots = []; var enemyBase; // Game resources var coins = 150; var unitCost = 50; // 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 (40)', { size: 40, fill: 0x87CEEB }); basicButton.anchor.set(0.5, 1); basicButton.x = -300; basicButton.interactive = true; basicButton.down = function () { if (coins >= 40) { spawnUnit('basic'); } }; LK.gui.bottom.addChild(basicButton); var fastButton = new Text2('Fast (60)', { size: 40, fill: 0x32CD32 }); fastButton.anchor.set(0.5, 1); fastButton.x = -100; fastButton.interactive = true; fastButton.down = function () { if (coins >= 60) { spawnUnit('fast'); } }; LK.gui.bottom.addChild(fastButton); var heavyButton = new Text2('Heavy (90)', { size: 40, fill: 0x8B4513 }); heavyButton.anchor.set(0.5, 1); heavyButton.x = 100; heavyButton.interactive = true; heavyButton.down = function () { if (coins >= 90) { spawnUnit('heavy'); } }; LK.gui.bottom.addChild(heavyButton); var sniperButton = new Text2('Sniper (80)', { size: 40, fill: 0x4B0082 }); sniperButton.anchor.set(0.5, 1); sniperButton.x = 200; sniperButton.interactive = true; sniperButton.down = function () { if (coins >= 80) { spawnUnit('sniper'); } }; LK.gui.bottom.addChild(sniperButton); var meleeButton = new Text2('Melee (30)', { size: 40, fill: 0xFF6347 }); meleeButton.anchor.set(0.5, 1); meleeButton.x = 400; meleeButton.interactive = true; meleeButton.down = function () { if (coins >= 30) { spawnUnit('melee'); } }; LK.gui.bottom.addChild(meleeButton); // 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 = 4; 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 spawnUnit(unitType) { if (!unitType) unitType = 'basic'; var unit = null; var cost = 40; switch (unitType) { case 'basic': unit = new BasicUnit(); cost = 40; break; case 'fast': unit = new FastUnit(); cost = 60; break; case 'heavy': unit = new HeavyUnit(); cost = 90; break; case 'sniper': unit = new SniperUnit(); cost = 80; break; case 'melee': unit = new MeleeUnit(); cost = 30; break; default: unit = new BasicUnit(); cost = 40; break; } if (coins >= cost && unit) { coins -= cost; coinsText.setText('Coins: ' + coins); coinIcon.x = -coinsText.width - 60; unit.x = 100; unit.y = 500 + playerUnits.length % 5 * 150; // 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: 200, towerCost: 100, lastTowerPlacement: 0, placementCooldown: 300, // 5 seconds at 60fps update: function update() { // Generate coins for enemy AI if (LK.ticks % 180 === 0) { // Every 3 seconds this.coins += 50; } // 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) { // Choose random tower type based on available coins (excluding heal since we handle that above) var towerTypes = []; 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 (towerTypes.length > 0) { var randomType = towerTypes[Math.floor(Math.random() * towerTypes.length)]; var tower = null; var cost = 75; switch (randomType) { 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; } 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 if (tower.updateHealthBar) { tower.updateHealthBar(); } // Visual feedback LK.effects.flashObject(bestSlot, 0xff0000, 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)); if (distance <= tower.range + 100) { nearbyUnits++; } } // Calculate upgrade priority if (nearbyUnits > 0) { priority = nearbyUnits * 10 + (3 - tower.level) * 5; // Prioritize combat zones and lower levels } 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 >= 40) { spawnUnit('basic'); } }; game.update = function () { // Update enemy AI enemyAI.update(); // Generate coins over time coinTimer++; if (coinTimer >= 120) { // Every 2 seconds coins += 25; 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 = 75; // Base reward if (enemyTowers[j].towerType) { switch (enemyTowers[j].towerType) { case 'slow': moneyReward = 85; break; case 'poison': moneyReward = 95; break; case 'fast': moneyReward = 100; break; case 'heal': moneyReward = 110; break; case 'sniper': moneyReward = 125; break; case 'area': moneyReward = 130; break; case 'laser': moneyReward = 140; break; case 'heavy': moneyReward = 150; break; default: moneyReward = 75; break; } } else if (enemyTowers[j].level) { // For upgradeable towers, reward based on level moneyReward = 75 + (enemyTowers[j].level - 1) * 50; } // Clean up slot if tower has one if (enemyTowers[j].slot) { 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); } } // Check if player has no units and insufficient coins (lose condition) if (playerUnits.length === 0 && coins < unitCost) { // Check if there are still towers or base with health if (enemyTowers.length > 0 || enemyBase && enemyBase.health > 0) { LK.effects.flashScreen(0xff0000, 2000); LK.showGameOver(); return; } } // Update base health display if (enemyBase) { healthText.setText('Base Health: ' + enemyBase.health); } };
===================================================================
--- original.js
+++ change.js
@@ -345,14 +345,54 @@
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);
+ // 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 false; // Basic towers cannot be upgraded
+ return self.level < self.maxLevel;
};
self.getUpgradeCost = function () {
- return 0; // Basic towers cannot be upgraded
+ 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 () {
// Find target
if (!self.target || !self.target.parent) {
self.target = null;
@@ -753,14 +793,57 @@
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
+ // 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 false; // Fast towers cannot be upgraded
+ return self.level < self.maxLevel;
};
self.getUpgradeCost = function () {
- return 0; // Fast towers cannot be upgraded
+ 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 () {
// Find target
if (!self.target || !self.target.parent) {
self.target = null;
@@ -775,17 +858,36 @@
}
}
self.target = closestUnit;
}
- // Shoot at target with fast rate
+ // Shoot at target with fast rate and multi-shot
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;
- towerBullets.push(bullet);
- game.addChild(bullet);
+ // Shoot multiple bullets based on level
+ for (var shotIndex = 0; shotIndex < self.multiShot; shotIndex++) {
+ var bullet = new TowerBullet();
+ bullet.x = self.x;
+ bullet.y = self.y;
+ // For multi-shot, slightly spread the bullets
+ if (self.multiShot > 1) {
+ var angleOffset = (shotIndex - (self.multiShot - 1) / 2) * 0.3; // Small angle spread
+ 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();
self.shootCooldown = 30; // 0.5 second cooldown (very fast)
}
if (self.shootCooldown > 0) {
@@ -1125,14 +1227,56 @@
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
+ // 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 false; // Heavy towers cannot be upgraded
+ return self.level < self.maxLevel;
};
self.getUpgradeCost = function () {
- return 0; // Heavy towers cannot be upgraded
+ 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 () {
// Find target
if (!self.target || !self.target.parent) {
self.target = null;
@@ -1147,15 +1291,17 @@
}
}
self.target = closestUnit;
}
- // Shoot at target with heavy damage
+ // 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)
@@ -1945,14 +2091,58 @@
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
+ // 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 false; // Sniper towers cannot be upgraded
+ return self.level < self.maxLevel;
};
self.getUpgradeCost = function () {
- return 0; // Sniper towers cannot be upgraded
+ 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;
@@ -1967,16 +2157,24 @@
}
}
self.target = bestUnit;
}
- // Shoot at target with precision
+ // 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;
- bullet.damage = self.damage;
+ // Check for critical hit
+ var isCritical = Math.random() < self.criticalChance;
+ bullet.damage = isCritical ? self.damage * 2 : self.damage;
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
@@ -2182,9 +2380,57 @@
// 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;
@@ -2268,10 +2514,10 @@
/****
* Game Code
****/
-// Game arrays to track entities
// Initialize game assets
+// Game arrays to track entities
var playerUnits = [];
var enemyTowers = [];
var unitBullets = [];
var towerBullets = [];
@@ -2708,32 +2954,52 @@
}
},
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()) {
+ 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));
if (distance <= tower.range + 100) {
nearbyUnits++;
}
}
- // Upgrade towers that are actively engaged or if we have excess coins or forced to spend
- if (nearbyUnits > 0 || this.coins > upgradeCost * 2 || forceSpend) {
- if (tower.upgrade()) {
- this.coins -= upgradeCost;
- break; // Only upgrade one tower per update
- }
+ // Calculate upgrade priority
+ if (nearbyUnits > 0) {
+ priority = nearbyUnits * 10 + (3 - tower.level) * 5; // Prioritize combat zones and lower levels
+ } 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++) {
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