/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Bullet = Container.expand(function () { var self = Container.call(this); self.speed = 8; self.damage = 1; self.targetUnit = null; self.owner = 1; var bulletGraphics = self.attachAsset('particle', { anchorX: 0.5, anchorY: 0.5 }); self.setTarget = function (target) { self.targetUnit = target; }; self.update = function () { if (!self.targetUnit || !self.targetUnit.parent) { // Target destroyed, remove bullet if (self.parent) { self.destroy(); var index = bullets.indexOf(self); if (index !== -1) { bullets.splice(index, 1); } } return; } // Move towards target var dx = self.targetUnit.x - self.x; var dy = self.targetUnit.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < self.speed) { // Hit target if (self.targetUnit.parent) { // Damage target self.targetUnit.health -= self.damage; if (self.targetUnit.health <= 0) { // Award coins when tower kills enemy unit if (self.owner === 1 && self.targetUnit.owner >= 2) { coins += 1; storage.coins = coins; coinsText.setText('Coins: ' + coins); } // Target destroyed memoryManager.recycleUnit(self.targetUnit); var unitIndex = units.indexOf(self.targetUnit); if (unitIndex !== -1) { units.splice(unitIndex, 1); } } } // Remove bullet if (self.parent) { self.destroy(); var index = bullets.indexOf(self); if (index !== -1) { bullets.splice(index, 1); } } } else { // Move towards target self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } }; return self; }); var MovingUnitGroup = Container.expand(function () { var self = Container.call(this); self.owner = 1; self.units = []; self.lastUpdateTime = 0; // Create count text with same size as tower unit count self.countText = new Text2('0', { size: 30, fill: 0xFFFFFF }); self.countText.anchor.set(0.5, 0.5); self.addChild(self.countText); self.updateGroup = function (nearbyUnits) { self.units = nearbyUnits; self.countText.setText(nearbyUnits.length); // Calculate center position of the group if (nearbyUnits.length > 0) { var totalX = 0; var totalY = 0; for (var i = 0; i < nearbyUnits.length; i++) { totalX += nearbyUnits[i].x; totalY += nearbyUnits[i].y; } self.x = totalX / nearbyUnits.length; self.y = totalY / nearbyUnits.length - 25; // Position above units self.visible = true; } else { self.visible = false; } self.lastUpdateTime = LK.ticks; }; // Add continuous update method to track unit movement self.update = function () { if (self.units && self.units.length > 0) { var totalX = 0; var totalY = 0; var validUnits = 0; // Calculate new center position based on current unit positions for (var i = 0; i < self.units.length; i++) { if (self.units[i] && self.units[i].parent) { totalX += self.units[i].x; totalY += self.units[i].y; validUnits++; } } // Update position if we have valid units if (validUnits > 0) { self.x = totalX / validUnits; self.y = totalY / validUnits - 25; // Position above units self.visible = true; } else { self.visible = false; } } }; self.isStale = function () { return LK.ticks - self.lastUpdateTime > 60; // 1 second at 60fps }; return self; }); var PathLine = Container.expand(function () { var self = Container.call(this); self.points = []; self.graphics = []; self.setPath = function (points) { // Clear old graphics for (var i = 0; i < self.graphics.length; i++) { self.graphics[i].destroy(); } self.graphics = []; self.points = points; // Draw new path for (var i = 0; i < points.length - 1; i++) { var dist = Math.sqrt(Math.pow(points[i + 1].x - points[i].x, 2) + Math.pow(points[i + 1].y - points[i].y, 2)); var segments = Math.floor(dist / 20); for (var j = 0; j < segments; j++) { var t = j / segments; var dot; if (memoryManager.pathDotPool.length > 0) { dot = memoryManager.pathDotPool.pop(); dot.visible = true; dot.alpha = 0.5; self.addChild(dot); } else { dot = self.attachAsset('path', { anchorX: 0.5, anchorY: 0.5 }); } dot.x = points[i].x + (points[i + 1].x - points[i].x) * t; dot.y = points[i].y + (points[i + 1].y - points[i].y) * t; dot.alpha = 0.5; self.graphics.push(dot); } } }; self.clear = function () { for (var i = 0; i < self.graphics.length; i++) { var dot = self.graphics[i]; if (dot && dot.parent) { dot.parent.removeChild(dot); dot.visible = false; memoryManager.pathDotPool.push(dot); } } self.graphics = []; self.points = []; }; return self; }); var Tower = Container.expand(function () { var self = Container.call(this); self.owner = 0; // 0 = neutral, 1 = player, 2 = enemy self.unitCount = 0; self.maxUnits = 100; self.spawnRate = 30; // ticks between spawns self.lastSpawn = 0; self.level = 1; self.attackPower = 2; self.range = 200; self.lastAttack = 0; self.attackRate = 60; // ticks between attacks // Initialize with neutral tower, will be updated by setOwner var towerGraphics = self.attachAsset('neutralTower', { anchorX: 0.5, anchorY: 0.5 }); self.countText = new Text2('0', { size: 30, fill: 0xFFFFFF }); self.countText.anchor.set(0.5, 1); self.countText.y = -85; // Position above tower graphic self.addChild(self.countText); // Health properties self.health = 100; self.maxHealth = 100; // Health bar container (positioned above level text) self.healthBarContainer = new Container(); self.healthBarContainer.y = -145; // Position above level text self.addChild(self.healthBarContainer); // Background bar (red) self.healthBarBg = LK.getAsset('particle', { anchorX: 0.5, anchorY: 0.5, scaleX: 4, scaleY: 0.6 }); self.healthBarBg.tint = 0xff0000; // Red background self.healthBarContainer.addChild(self.healthBarBg); // Foreground bar (green) self.healthBarFg = LK.getAsset('particle', { anchorX: 0.5, anchorY: 0.5, scaleX: 4, scaleY: 0.6 }); self.healthBarFg.tint = 0x00ff00; // Green foreground self.healthBarContainer.addChild(self.healthBarFg); // Level text (positioned above unit count) self.levelText = new Text2('Lv.1', { size: 25, fill: 0xFFD700 // Gold color for level }); self.levelText.anchor.set(0.5, 1); self.levelText.y = -115.5; // Position above unit count (5% higher: -110 * 1.05) self.addChild(self.levelText); // Enemy name text (positioned below tower) self.nameText = new Text2('', { size: 40, fill: 0xFFFFFF }); self.nameText.anchor.set(0.5, 0); self.nameText.y = 90; // Position below the tower self.addChild(self.nameText); // Upgrade button text (positioned below name text, only for player towers) self.upgradeText = new Text2('', { size: 30, fill: 0x00FF00 // Green color for upgrade button }); self.upgradeText.anchor.set(0.5, 0); self.upgradeText.y = 130; // Position below name text self.addChild(self.upgradeText); self.setOwner = function (newOwner) { var oldOwner = self.owner; self.owner = newOwner; var targetAsset; if (newOwner === 0) { targetAsset = 'neutralTower'; } else if (newOwner === 1) { targetAsset = 'playerTower'; } else if (newOwner === 2) { targetAsset = 'enemyTower1'; } else if (newOwner === 3) { targetAsset = 'enemyTower2'; } else if (newOwner === 4) { targetAsset = 'enemyTower3'; } else { targetAsset = 'enemyTower1'; // default enemy red } // Replace tower graphics with new asset if (oldOwner !== newOwner) { self.removeChild(towerGraphics); towerGraphics = self.attachAsset(targetAsset, { anchorX: 0.5, anchorY: 0.5 }); // Ensure text stays on top of the new graphics self.removeChild(self.countText); self.addChild(self.countText); self.removeChild(self.nameText); self.addChild(self.nameText); } // Update name display if (newOwner >= 2) { // Enemy tower - show name var enemyName = assignEnemyName(newOwner); self.nameText.setText(enemyName); self.nameText.alpha = 1; // Hide upgrade button for enemy towers self.upgradeText.setText(''); self.upgradeText.alpha = 0; } else if (newOwner === 1) { // Player tower - show "Player" self.nameText.setText('Player'); self.nameText.alpha = 1; // Show upgrade button for player towers var upgradeCost = self.level * 20; self.upgradeText.setText('Upgrade: ' + upgradeCost + ' coins'); self.upgradeText.alpha = 1; } else { // Neutral tower - hide name and upgrade button self.nameText.setText(''); self.nameText.alpha = 0; self.upgradeText.setText(''); self.upgradeText.alpha = 0; } // Graphics already updated above with new asset }; self.addUnits = function (count) { // Calculate current max units based on level: 100 * (1.1^level) var currentMaxUnits = Math.floor(100 * Math.pow(1.1, self.level)); self.maxUnits = currentMaxUnits; self.unitCount = Math.min(self.unitCount + count, self.maxUnits); self.countText.setText(Math.floor(self.unitCount)); }; self.removeUnits = function (count) { self.unitCount = Math.max(0, self.unitCount - count); self.countText.setText(Math.floor(self.unitCount)); return count; }; // Method to update health bar self.updateHealthBar = function () { var healthPercent = self.health / self.maxHealth; self.healthBarFg.scaleX = 4 * healthPercent; // Change color based on health percentage if (healthPercent > 0.6) { self.healthBarFg.tint = 0x00ff00; // Green for healthy } else if (healthPercent > 0.3) { self.healthBarFg.tint = 0xffff00; // Yellow for damaged } else { self.healthBarFg.tint = 0xff0000; // Red for critical } }; self.upgrade = function () { var upgradeCost = self.level * 20; if (coins >= upgradeCost) { coins -= upgradeCost; storage.coins = coins; coinsText.setText('Coins: ' + coins); self.level++; self.attackPower = self.level * 2; self.range = 200 + (self.level - 1) * 20; self.attackRate = Math.max(30, 60 - (self.level - 1) * 5); // Update maxUnits: 100 * (1.1^level) self.maxUnits = Math.floor(100 * Math.pow(1.1, self.level)); self.levelText.setText('Lv.' + self.level); // Update upgrade button text with new cost var newUpgradeCost = self.level * 20; self.upgradeText.setText('Upgrade: ' + newUpgradeCost + ' coins'); // Flash effect for upgrade LK.effects.flashObject(self, 0x00ff00, 500); return true; } return false; }; self.attack = function () { if (LK.ticks - self.lastAttack < self.attackRate) return; // Find enemy units in range var nearestEnemy = null; var minDist = Infinity; for (var i = 0; i < units.length; i++) { var unit = units[i]; if (unit.owner !== self.owner) { var dx = self.x - unit.x; var dy = self.y - unit.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist <= self.range && dist < minDist) { minDist = dist; nearestEnemy = unit; } } } if (nearestEnemy) { self.lastAttack = LK.ticks; var bullet = new Bullet(); bullet.x = self.x; bullet.y = self.y; bullet.damage = self.attackPower; bullet.owner = self.owner; bullet.setTarget(nearestEnemy); bullets.push(bullet); game.addChild(bullet); } }; self.update = function () { // Check if tower is destroyed (health <= 0) if (self.health <= 0) { // Tower destroyed - convert to neutral tower self.health = self.maxHealth; // Reset health self.unitCount = Math.floor(self.maxHealth / 4); // Set some units based on max health self.setOwner(0); // Make it neutral self.level = 1; // Reset to level 1 self.attackPower = 2; self.range = 200; self.attackRate = 60; self.levelText.setText('Lv.1'); // Flash effect for destruction LK.effects.flashObject(self, 0xff0000, 1000); return; } if (self.owner > 0) { // Calculate dynamic spawn rate based on unit count and level // More units = faster production (lower spawn rate number) // Base rate is 30, reduced by unit count factor and level bonus var levelSpeedBonus = (self.level - 1) * 0.1; // 10% faster per level var baseSpawnRate = self.spawnRate * (1 - levelSpeedBonus); // Apply level bonus var dynamicSpawnRate = Math.max(10, baseSpawnRate - Math.floor(self.unitCount / 10)); if (LK.ticks - self.lastSpawn > dynamicSpawnRate) { self.addUnits(1); self.lastSpawn = LK.ticks; } // Attack nearby enemies self.attack(); } // Update health bar display self.updateHealthBar(); }; return self; }); var Unit = Container.expand(function () { var self = Container.call(this); self.owner = 1; self.speed = 3.0; // Will be dynamically calculated during update self.targetTower = null; self.pathIndex = 0; self.path = []; self.health = 3; self.maxHealth = 3; // Initialize with player unit, will be updated by setOwner var unitGraphics = self.attachAsset('playerUnit', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); // Health bar for combat visualization self.healthBar = new Container(); self.addChild(self.healthBar); self.healthBar.y = -30; // Position above unit // Background bar (red) self.healthBg = LK.getAsset('particle', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 0.5 }); self.healthBg.tint = 0xff0000; // Red background self.healthBar.addChild(self.healthBg); // Foreground bar (green) self.healthFg = LK.getAsset('particle', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 0.5 }); self.healthFg.tint = 0x00ff00; // Green foreground self.healthBar.addChild(self.healthFg); // Initially hide health bar self.healthBar.visible = false; // Method to update health bar self.updateHealthBar = function () { if (self.health < self.maxHealth) { self.healthBar.visible = true; var healthPercent = self.health / self.maxHealth; self.healthFg.scaleX = 3 * healthPercent; } else { self.healthBar.visible = false; } }; self.setOwner = function (owner) { self.owner = owner; var targetAsset; if (owner === 1) { targetAsset = 'playerUnit'; } else if (owner === 2) { targetAsset = 'enemyUnit1'; } else if (owner === 3) { targetAsset = 'enemyUnit2'; } else if (owner === 4) { targetAsset = 'enemyUnit3'; } else { targetAsset = 'enemyUnit1'; // default enemy red } // Replace unit graphics with new asset self.removeChild(unitGraphics); unitGraphics = self.attachAsset(targetAsset, { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); }; self.setPath = function (path, target) { // Add random offset to create scattered movement var scatterRange = 40; // Maximum scatter distance var offsetX = (Math.random() - 0.5) * scatterRange; var offsetY = (Math.random() - 0.5) * scatterRange; // Create scattered path by adding random offsets to each point self.path = []; for (var i = 0; i < path.length; i++) { // Apply consistent offset throughout the path, but reduce it near the end var offsetFactor = Math.max(0.2, 1 - i / path.length * 0.8); self.path.push({ x: path[i].x + offsetX * offsetFactor, y: path[i].y + offsetY * offsetFactor }); } self.targetTower = target; self.pathIndex = 0; if (self.path.length > 0) { self.x = self.path[0].x; self.y = self.path[0].y; } }; self.lastWasIntersecting = false; self.combatProcessed = false; self.update = function () { if (!self.targetTower || self.pathIndex >= self.path.length) { return; } // Check if unit reached enemy tower and deal damage if (self.targetTower && self.targetTower.owner !== self.owner) { var distanceToTower = Math.sqrt(Math.pow(self.x - self.targetTower.x, 2) + Math.pow(self.y - self.targetTower.y, 2)); // If very close to enemy tower (within 80 pixels), attack it if (distanceToTower < 80) { // Deal damage to tower every 120 ticks (2 seconds) if (LK.ticks % 120 === 0) { self.targetTower.health -= 2; // Units deal 2 damage to towers // Create damage effect var sparkle = memoryManager.getSparkle(); game.addChild(sparkle); sparkle.x = self.targetTower.x + (Math.random() - 0.5) * 50; sparkle.y = self.targetTower.y + (Math.random() - 0.5) * 50; sparkle.alpha = 1; sparkle.scaleX = 0.8; sparkle.scaleY = 0.8; sparkle.tint = 0xff4444; // Red damage effect tween(sparkle, { alpha: 0, scaleX: 0, scaleY: 0 }, { duration: 400, onFinish: function onFinish() { if (sparkle && sparkle.parent) { memoryManager.recycleSparkle(sparkle); } } }); } } } // Calculate dynamic speed based on nearby units of same owner var nearbyUnits = 1; // Count self for (var i = 0; i < units.length; i++) { var otherUnit = units[i]; if (otherUnit !== self && otherUnit.owner === self.owner) { var dx = self.x - otherUnit.x; var dy = self.y - otherUnit.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 100) { // Same grouping distance as display nearbyUnits++; } } } // Calculate speed: fewer units = faster, more units = slower // Base speed of 3.0, reduced by group size but with less penalty // 1 unit: 3.0 speed, 10 units: 2.1 speed, 20+ units: 1.5 speed var dynamicSpeed = Math.max(1.5, 3.0 - (nearbyUnits - 1) * 0.1); self.speed = dynamicSpeed; // Check for combat with enemy units var currentIntersecting = false; var inCombat = false; for (var i = 0; i < units.length; i++) { var otherUnit = units[i]; if (otherUnit !== self && otherUnit.owner !== self.owner) { // Check if units are close enough to fight (within 40 pixels for engagement) var combatDx = self.x - otherUnit.x; var combatDy = self.y - otherUnit.y; var combatDist = Math.sqrt(combatDx * combatDx + combatDy * combatDy); if (combatDist < 40) { currentIntersecting = true; inCombat = true; // Stop movement when in combat self.speed = 0; otherUnit.speed = 0; // Deal damage over time (every 90 ticks = 1.5 seconds) if (LK.ticks % 90 === 0) { // Both units deal 1 damage to each other self.health -= 1; otherUnit.health -= 1; // Create small combat effect var battleX = (self.x + otherUnit.x) / 2; var battleY = (self.y + otherUnit.y) / 2; var sparkle = memoryManager.getSparkle(); game.addChild(sparkle); sparkle.x = battleX + (Math.random() - 0.5) * 20; sparkle.y = battleY + (Math.random() - 0.5) * 20; sparkle.alpha = 1; sparkle.scaleX = 0.3; sparkle.scaleY = 0.3; sparkle.tint = 0xff4444; // Red combat effect tween(sparkle, { alpha: 0, scaleX: 0, scaleY: 0 }, { duration: 200, onFinish: function onFinish() { if (sparkle && sparkle.parent) { memoryManager.recycleSparkle(sparkle); } } }); // Play combat sound occasionally if (Math.random() < 0.3) { LK.getSound('combat').play(); } } // Check if either unit died if (self.health <= 0 && !self.combatProcessed) { self.combatProcessed = true; // Award coin if enemy unit kills player unit if (otherUnit.owner === 1 && self.owner >= 2) { coins += 1; storage.coins = coins; coinsText.setText('Coins: ' + coins); } // Award coin if enemy unit dies while attacking player tower if (self.owner >= 2 && self.targetTower && self.targetTower.owner === 1) { coins += 1; storage.coins = coins; coinsText.setText('Coins: ' + coins); } // Create death explosion var battleX = self.x; var battleY = self.y; for (var sparkleIndex = 0; sparkleIndex < 6; sparkleIndex++) { var sparkle = memoryManager.getSparkle(); game.addChild(sparkle); sparkle.x = battleX + (Math.random() - 0.5) * 30; sparkle.y = battleY + (Math.random() - 0.5) * 30; sparkle.alpha = 1; sparkle.scaleX = 0.5 + Math.random() * 0.3; sparkle.scaleY = sparkle.scaleX; sparkle.tint = 0xff0000; // Red death effect var randomAngle = Math.random() * Math.PI * 2; var flyDistance = 30 + Math.random() * 20; var targetX = sparkle.x + Math.cos(randomAngle) * flyDistance; var targetY = sparkle.y + Math.sin(randomAngle) * flyDistance; tween(sparkle, { x: targetX, y: targetY, alpha: 0, scaleX: 0, scaleY: 0 }, { duration: 400 + Math.random() * 200, easing: tween.easeOut, onFinish: function onFinish() { if (sparkle && sparkle.parent) { memoryManager.recycleSparkle(sparkle); } } }); } // Flash and destroy self LK.effects.flashObject(self, 0xff0000, 300); LK.setTimeout(function () { if (self && self.parent) { memoryManager.recycleUnit(self); var selfIndex = units.indexOf(self); if (selfIndex !== -1) { units.splice(selfIndex, 1); } } }, 300); return; } if (otherUnit.health <= 0 && !otherUnit.combatProcessed) { otherUnit.combatProcessed = true; // Award coin if player unit kills enemy unit if (self.owner === 1 && otherUnit.owner >= 2) { coins += 1; storage.coins = coins; coinsText.setText('Coins: ' + coins); } // Create death explosion for other unit var battleX = otherUnit.x; var battleY = otherUnit.y; for (var sparkleIndex = 0; sparkleIndex < 6; sparkleIndex++) { var sparkle = memoryManager.getSparkle(); game.addChild(sparkle); sparkle.x = battleX + (Math.random() - 0.5) * 30; sparkle.y = battleY + (Math.random() - 0.5) * 30; sparkle.alpha = 1; sparkle.scaleX = 0.5 + Math.random() * 0.3; sparkle.scaleY = sparkle.scaleX; sparkle.tint = 0xff0000; // Red death effect var randomAngle = Math.random() * Math.PI * 2; var flyDistance = 30 + Math.random() * 20; var targetX = sparkle.x + Math.cos(randomAngle) * flyDistance; var targetY = sparkle.y + Math.sin(randomAngle) * flyDistance; tween(sparkle, { x: targetX, y: targetY, alpha: 0, scaleX: 0, scaleY: 0 }, { duration: 400 + Math.random() * 200, easing: tween.easeOut, onFinish: function onFinish() { if (sparkle && sparkle.parent) { memoryManager.recycleSparkle(sparkle); } } }); } // Flash and destroy other unit LK.effects.flashObject(otherUnit, 0xff0000, 300); LK.setTimeout(function () { if (otherUnit && otherUnit.parent) { memoryManager.recycleUnit(otherUnit); var otherIndex = units.indexOf(otherUnit); if (otherIndex !== -1) { units.splice(otherIndex, 1); } } }, 300); // Reset self's speed since combat partner is dead self.speed = 3.0; } break; // Only fight one unit at a time } } } // If not in combat, restore normal speed if (!inCombat && self.speed === 0) { self.speed = 3.0; } self.lastWasIntersecting = currentIntersecting; // Update health bar display self.updateHealthBar(); var target = self.path[self.pathIndex]; var dx = target.x - self.x; var dy = target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < self.speed) { self.pathIndex++; if (self.pathIndex >= self.path.length) { if (self.targetTower) { // Reached target tower if (self.targetTower.owner === self.owner) { self.targetTower.addUnits(1); } else { self.targetTower.unitCount--; if (self.targetTower.unitCount < 0) { self.targetTower.unitCount = 1; self.targetTower.setOwner(self.owner); LK.getSound('capture').play(); } self.targetTower.countText.setText(Math.floor(Math.abs(self.targetTower.unitCount))); } memoryManager.recycleUnit(self); units.splice(units.indexOf(self), 1); } else { // No target tower - this is a deployed unit, just wait at destination // Reset pathIndex to stay at final position self.pathIndex = self.path.length - 1; } } } else { // Add slight random movement variation for scattered appearance var moveX = dx / dist * self.speed; var moveY = dy / dist * self.speed; // Add small random deviation (5% of movement speed) var deviation = self.speed * 0.05; moveX += (Math.random() - 0.5) * deviation; moveY += (Math.random() - 0.5) * deviation; self.x += moveX; self.y += moveY; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a1a }); /**** * Game Code ****/ // Add background image to cover the full game area var backgroundImage = game.attachAsset('gameBackground', { anchorX: 0.5, anchorY: 0.5, x: 1024, // Center horizontally (2048/2) y: 1366 // Center vertically (2732/2) }); // Legacy shape assets for other elements // Unit Assets - Different for each faction // Tower Assets - Different types for each faction var towers = []; var units = []; var bullets = []; var movingUnitGroups = []; var currentPath = null; var selectionRing = null; var selectedTower = null; var selectedUnitGroup = null; var isDragging = false; var isDraggingUnits = false; var currentLevel = storage.currentLevel || 0; var coins = storage.coins || 0; var aiStartTime = null; // Track when AI should start (null = not started yet) // Enemy names for different factions var enemyNames = ["Karabekir", "Enver", "Talat", "Cemal", "İsmet", "Fevzi", "Kazım", "Refet", "Ali Fuat", "Rauf", "Bekir Sami", "Adnan", "Kâzım Özalp", "Salih", "Nureddin"]; var assignedEnemyNames = {}; // Track names assigned to each enemy faction // Level configurations - 10 levels with progressive difficulty (scaled up 100%) + 20 units per level var levels = [{ // Level 1 - Tutorial (Level 1 = +20 units) towers: [{ x: 300, y: 1366, owner: 1, units: 50 }, { x: 1024, y: 800, owner: 0, units: 30 }, { x: 1748, y: 1366, owner: 2, units: 40 }, { x: 1748, y: 400, owner: 3, units: 35 }] }, { // Level 2 - Basic Strategy (Level 2 = +40 units) towers: [{ x: 300, y: 400, owner: 1, units: 75 }, { x: 1024, y: 1366, owner: 0, units: 55 }, { x: 1748, y: 400, owner: 2, units: 65 }, { x: 1748, y: 2332, owner: 3, units: 65 }, { x: 300, y: 2332, owner: 4, units: 60 }] }, { // Level 3 - Multi-Front (Level 3 = +60 units) towers: [{ x: 300, y: 1366, owner: 1, units: 105 }, { x: 700, y: 800, owner: 0, units: 75 }, { x: 1348, y: 800, owner: 0, units: 75 }, { x: 700, y: 1932, owner: 0, units: 75 }, { x: 1348, y: 1932, owner: 0, units: 75 }, { x: 1748, y: 400, owner: 2, units: 90 }, { x: 1748, y: 1366, owner: 3, units: 90 }, { x: 1748, y: 2332, owner: 4, units: 90 }] }, { // Level 4 - Defensive Challenge (Level 4 = +80 units) towers: [{ x: 300, y: 1366, owner: 1, units: 130 }, { x: 600, y: 600, owner: 0, units: 100 }, { x: 1024, y: 400, owner: 2, units: 120 }, { x: 1448, y: 600, owner: 0, units: 100 }, { x: 1748, y: 1366, owner: 0, units: 100 }, { x: 1024, y: 2100, owner: 0, units: 100 }] }, { // Level 5 - Resource Management (Level 5 = +100 units) towers: [{ x: 200, y: 800, owner: 1, units: 155 }, { x: 200, y: 1932, owner: 1, units: 155 }, { x: 700, y: 1366, owner: 0, units: 125 }, { x: 1348, y: 1366, owner: 0, units: 125 }, { x: 1748, y: 600, owner: 2, units: 145 }, { x: 1748, y: 1366, owner: 0, units: 125 }, { x: 1748, y: 2132, owner: 0, units: 125 }] }, { // Level 6 - Territory Control (Level 6 = +120 units) towers: [{ x: 300, y: 400, owner: 1, units: 180 }, { x: 300, y: 2332, owner: 1, units: 180 }, { x: 600, y: 800, owner: 0, units: 150 }, { x: 1024, y: 1366, owner: 0, units: 150 }, { x: 1448, y: 1932, owner: 0, units: 150 }, { x: 1748, y: 800, owner: 2, units: 170 }, { x: 1748, y: 1500, owner: 0, units: 150 }, { x: 1748, y: 2200, owner: 0, units: 150 }] }, { // Level 7 - Advanced Tactics (Level 7 = +140 units) towers: [{ x: 200, y: 1366, owner: 1, units: 205 }, { x: 500, y: 600, owner: 0, units: 175 }, { x: 500, y: 1366, owner: 0, units: 175 }, { x: 500, y: 2132, owner: 0, units: 175 }, { x: 1024, y: 400, owner: 0, units: 175 }, { x: 1024, y: 2332, owner: 0, units: 175 }, { x: 1548, y: 600, owner: 0, units: 175 }, { x: 1548, y: 1366, owner: 0, units: 175 }, { x: 1548, y: 2132, owner: 0, units: 175 }, { x: 1848, y: 1366, owner: 2, units: 195 }] }, { // Level 8 - Siege Warfare (Level 8 = +160 units) towers: [{ x: 300, y: 600, owner: 1, units: 230 }, { x: 300, y: 1366, owner: 1, units: 230 }, { x: 300, y: 2132, owner: 1, units: 230 }, { x: 800, y: 800, owner: 0, units: 200 }, { x: 800, y: 1932, owner: 0, units: 200 }, { x: 1248, y: 800, owner: 0, units: 200 }, { x: 1248, y: 1932, owner: 0, units: 200 }, { x: 1650, y: 1366, owner: 2, units: 220 }, { x: 1650, y: 400, owner: 0, units: 200 }, { x: 1650, y: 2332, owner: 0, units: 200 }] }, { // Level 9 - Final Challenge (Level 9 = +180 units) towers: [{ x: 200, y: 800, owner: 1, units: 255 }, { x: 200, y: 1932, owner: 1, units: 255 }, { x: 600, y: 400, owner: 0, units: 225 }, { x: 600, y: 1366, owner: 0, units: 225 }, { x: 600, y: 2332, owner: 0, units: 225 }, { x: 1024, y: 600, owner: 0, units: 225 }, { x: 1024, y: 2132, owner: 0, units: 225 }, { x: 1448, y: 400, owner: 0, units: 225 }, { x: 1448, y: 1366, owner: 0, units: 225 }, { x: 1448, y: 2332, owner: 0, units: 225 }, { x: 1748, y: 1366, owner: 2, units: 245 }, { x: 1748, y: 600, owner: 0, units: 225 }, { x: 1748, y: 2132, owner: 0, units: 225 }] }, { // Level 10 - Master's Trial (Level 10 = +200 units) towers: [{ x: 150, y: 1366, owner: 1, units: 280 }, { x: 400, y: 600, owner: 0, units: 250 }, { x: 400, y: 1100, owner: 0, units: 250 }, { x: 400, y: 1632, owner: 0, units: 250 }, { x: 400, y: 2132, owner: 0, units: 250 }, { x: 800, y: 400, owner: 0, units: 250 }, { x: 800, y: 800, owner: 0, units: 250 }, { x: 800, y: 1932, owner: 0, units: 250 }, { x: 800, y: 2332, owner: 0, units: 250 }, { x: 1248, y: 400, owner: 0, units: 250 }, { x: 1248, y: 800, owner: 0, units: 250 }, { x: 1248, y: 1932, owner: 0, units: 250 }, { x: 1248, y: 2332, owner: 0, units: 250 }, { x: 1648, y: 600, owner: 0, units: 250 }, { x: 1648, y: 1100, owner: 0, units: 250 }, { x: 1648, y: 1632, owner: 0, units: 250 }, { x: 1648, y: 2132, owner: 0, units: 250 }, { x: 1898, y: 1366, owner: 2, units: 270 }] }]; // UI Elements var levelText = new Text2('Level ' + (currentLevel + 1), { size: 40, fill: 0xFFFFFF }); levelText.anchor.set(0.5, 0); LK.gui.top.addChild(levelText); // Sound toggle icon (same size as coins text) var soundEnabled = storage.soundEnabled !== undefined ? storage.soundEnabled : true; var soundIcon = new Text2(soundEnabled ? '🔊' : '🔇', { size: 30, fill: 0xFFFFFF }); soundIcon.anchor.set(1, 0); soundIcon.x = -140; // Position to the left of level text (moved 3 lines left) soundIcon.y = 20; LK.gui.top.addChild(soundIcon); // Sound icon click functionality soundIcon.down = function (x, y, obj) { soundEnabled = !soundEnabled; storage.soundEnabled = soundEnabled; soundIcon.setText(soundEnabled ? '🔊' : '🔇'); if (soundEnabled) { LK.playMusic('battle'); } else { LK.stopMusic(); } }; // Select All Troops button var selectAllButton = new Text2('Select All Troops', { size: 30, fill: 0x00FF00 // Green color }); selectAllButton.anchor.set(0.5, 0); selectAllButton.y = 50; // Position below level text LK.gui.top.addChild(selectAllButton); // Select All Towers button var selectAllTowersButton = new Text2('Select All Towers', { size: 30, fill: 0x00FF00 // Green color }); selectAllTowersButton.anchor.set(0.5, 0); selectAllTowersButton.y = 90; // Position below Select All Troops button LK.gui.top.addChild(selectAllTowersButton); // Coins display var coinsText = new Text2('Coins: ' + coins, { size: 30, fill: 0xFFD700 // Gold color }); coinsText.anchor.set(0, 0); coinsText.x = 137.5; coinsText.y = 20; LK.gui.topLeft.addChild(coinsText); // Game Time display with tracking var gameStartTime = LK.ticks; var gameTimeText = new Text2('Game Time: 0s', { size: 30, fill: 0xFFFFFF }); gameTimeText.anchor.set(1, 0); gameTimeText.x = -137.5; gameTimeText.y = 20; LK.gui.topRight.addChild(gameTimeText); // How to play button var howToPlayButton = new Text2('How to play?', { size: 25, fill: 0x00FF00 // Green color }); howToPlayButton.anchor.set(1, 0); howToPlayButton.x = -137.5; howToPlayButton.y = 60; // Position below game time LK.gui.topRight.addChild(howToPlayButton); // How to play panel (initially hidden) var howToPlayPanel = new Container(); howToPlayPanel.visible = false; LK.gui.center.addChild(howToPlayPanel); // Panel background var panelBackground = LK.getAsset('tower', { anchorX: 0.5, anchorY: 0.5, scaleX: 8, scaleY: 12 }); panelBackground.tint = 0x000000; panelBackground.alpha = 0.8; howToPlayPanel.addChild(panelBackground); // Panel title var panelTitle = new Text2('HOW TO PLAY', { size: 50, fill: 0xFFFFFF }); panelTitle.anchor.set(0.5, 0.5); panelTitle.y = -390; howToPlayPanel.addChild(panelTitle); // Game instructions text var instructionsText = new Text2('OBJECTIVE:\nDefeat all enemy factions by capturing their towers\n\nCONTROLS:\n• Tap a tower to select it\n• Drag to another tower to send units\n• Double-tap your towers to upgrade them\n• Tap empty space and drag to deploy units\n• Use "Select All" buttons for mass commands\n\nSTRATEGY:\n• Towers produce units automatically\n• Upgrade towers for more power\n• Coordinate attacks with multiple towers\n• Defend your territory while expanding\n• Earn coins by defeating enemies\n\nPlease check out my other games. I love you guys so much! Thank you so much for playing!', { size: 28, fill: 0xFFFFFF }); instructionsText.anchor.set(0.5, 0.5); instructionsText.y = -50; howToPlayPanel.addChild(instructionsText); // Close button var closeButton = new Text2('CLOSE', { size: 40, fill: 0xFF0000 // Red color }); closeButton.anchor.set(0.5, 0.5); closeButton.y = 340; // Moved down 3 lines (90 pixels) howToPlayPanel.addChild(closeButton); function assignEnemyName(owner) { if (!assignedEnemyNames[owner] && owner >= 2) { // Get available names (not already assigned) var availableNames = enemyNames.filter(function (name) { return !Object.values(assignedEnemyNames).includes(name); }); // If no available names, reuse from the pool if (availableNames.length === 0) { availableNames = enemyNames; } // Assign random name var randomIndex = Math.floor(Math.random() * availableNames.length); assignedEnemyNames[owner] = availableNames[randomIndex]; } return assignedEnemyNames[owner] || ""; } function loadLevel(levelIndex) { // Initialize movingUnitGroups array if undefined if (!movingUnitGroups) { movingUnitGroups = []; } // Clear existing game objects for (var i = 0; i < towers.length; i++) { towers[i].destroy(); } for (var i = 0; i < units.length; i++) { units[i].destroy(); } towers = []; units = []; bullets = []; if (currentPath) { currentPath.destroy(); currentPath = null; } // Clean up moving unit groups for (var i = 0; i < movingUnitGroups.length; i++) { if (movingUnitGroups[i].parent) { movingUnitGroups[i].destroy(); } } movingUnitGroups = []; // Reset game state variables selectedTower = null; selectedUnitGroup = null; isDragging = false; isDraggingUnits = false; if (selectionRing) { selectionRing.destroy(); selectionRing = null; } // Reset enemy name assignments assignedEnemyNames = {}; // Load new level var levelData = levels[levelIndex % levels.length]; for (var i = 0; i < levelData.towers.length; i++) { var towerData = levelData.towers[i]; var tower = new Tower(); tower.x = towerData.x; tower.y = towerData.y; tower.setOwner(towerData.owner); tower.addUnits(towerData.units); towers.push(tower); game.addChild(tower); } levelText.setText('Level ' + (levelIndex + 1)); // Set AI to start after 5 seconds (5000ms) aiStartTime = LK.ticks + 5 * 60; // 5 seconds at 60 FPS // Reset game start time for new level gameStartTime = LK.ticks; } function createPath(start, end) { var points = []; var steps = 20; for (var i = 0; i <= steps; i++) { points.push({ x: start.x + (end.x - start.x) * (i / steps), y: start.y + (end.y - start.y) * (i / steps) }); } return points; } function deployUnits(startX, startY, destX, destY, count) { // Create path from start to destination var path = createPath({ x: startX, y: startY }, { x: destX, y: destY }); for (var i = 0; i < count; i++) { var unit = memoryManager.getUnit(); unit.setOwner(1); // Player units // Create a waiting position path - units go to destination and wait var waitingPath = []; for (var j = 0; j < path.length; j++) { waitingPath.push(path[j]); } // Add the destination as the final waiting point waitingPath.push({ x: destX, y: destY }); unit.setPath(waitingPath, null); // No target tower, just move to position units.push(unit); game.addChild(unit); // Add random spawn delay and position offset for scattered deployment var randomDelay = i * (30 + Math.random() * 40); // Random delay between 30-70ms per unit var spawnOffsetX = (Math.random() - 0.5) * 60; // Random spawn position offset var spawnOffsetY = (Math.random() - 0.5) * 60; // Position unit at spawn location with offset unit.x = startX + spawnOffsetX; unit.y = startY + spawnOffsetY; // Stagger unit spawning with random timing tween(unit, { x: path[0].x, y: path[0].y }, { duration: randomDelay, onFinish: function onFinish() { LK.getSound('deploy').play(); } }); } } function sendUnits(fromTower, toTower, count) { var path = createPath(fromTower, toTower); var unitsToSend = Math.min(count, fromTower.unitCount); for (var i = 0; i < unitsToSend; i++) { var unit = memoryManager.getUnit(); unit.setOwner(fromTower.owner); unit.setPath(path, toTower); units.push(unit); game.addChild(unit); // Add random spawn delay and position offset for scattered deployment var randomDelay = i * (30 + Math.random() * 40); // Random delay between 30-70ms per unit var spawnOffsetX = (Math.random() - 0.5) * 60; // Random spawn position offset var spawnOffsetY = (Math.random() - 0.5) * 60; // Position unit at spawn location with offset unit.x = fromTower.x + spawnOffsetX; unit.y = fromTower.y + spawnOffsetY; // Stagger unit spawning with random timing tween(unit, { x: path[0].x, y: path[0].y }, { duration: randomDelay, onFinish: function onFinish() { LK.getSound('deploy').play(); } }); } fromTower.removeUnits(unitsToSend); } function checkWinCondition() { var playerTowers = 0; var enemyTowers = 0; for (var i = 0; i < towers.length; i++) { if (towers[i].owner === 1) playerTowers++; if (towers[i].owner >= 2) enemyTowers++; } if (enemyTowers === 0) { // Win condition - player defeated all enemy factions currentLevel++; storage.currentLevel = currentLevel; LK.showYouWin(); } else if (playerTowers === 0) { // Lose condition - reset coins and restart current level coins = 0; storage.coins = coins; coinsText.setText('Coins: ' + coins); LK.showGameOver(); } } // Multi-faction AI with sophisticated strategies function runAI() { // Don't run AI until 5 seconds have passed if (aiStartTime && LK.ticks < aiStartTime) { return; } // AI tower upgrade logic for (var i = 0; i < towers.length; i++) { var tower = towers[i]; if (tower.owner >= 2) { // AI upgrade chance based on level and available resources simulation var upgradeChance = Math.min(0.02, tower.level * 0.005); // Max 2% chance per frame if (Math.random() < upgradeChance && tower.level < 5) { tower.level++; tower.attackPower = tower.level * 2; tower.range = 200 + (tower.level - 1) * 20; tower.attackRate = Math.max(30, 60 - (tower.level - 1) * 5); tower.levelText.setText('Lv.' + tower.level); LK.effects.flashObject(tower, 0xff4444, 300); } } } // Analyze game state for strategic decisions var gameState = analyzeGameState(); for (var i = 0; i < towers.length; i++) { var tower = towers[i]; if (tower.owner >= 2) { // Force action when AI towers approach 100 units (at 80+ units) if (tower.unitCount >= 80) { // Find any nearby target to attack immediately var emergencyTarget = findNearestEnemy(tower); if (emergencyTarget) { // Send most units to prevent reaching 100 var unitsToSend = Math.floor(tower.unitCount * 0.7); if (unitsToSend > 0) { sendUnits(tower, emergencyTarget, unitsToSend); } } } else { // Normal strategic behavior for towers under 80 units var strategy = getOptimalStrategy(tower, gameState); executeStrategy(tower, strategy, gameState); } } } } // Analyze current game state for AI decision making function analyzeGameState() { var state = { playerStrength: 0, enemyStrength: 0, neutralTowers: 0, playerTowers: [], enemyTowers: [], neutralTowersByDistance: [], threats: [], opportunities: [] }; // Calculate faction strengths and positions for (var i = 0; i < towers.length; i++) { var tower = towers[i]; if (tower.owner === 1) { state.playerStrength += tower.unitCount; state.playerTowers.push(tower); } else if (tower.owner >= 2) { state.enemyStrength += tower.unitCount; state.enemyTowers.push(tower); } else { state.neutralTowers++; } } // Find strategic opportunities and threats for each enemy tower for (var i = 0; i < state.enemyTowers.length; i++) { var enemyTower = state.enemyTowers[i]; var towerThreats = []; var towerOpportunities = []; // Analyze threats from player and other enemy factions for (var j = 0; j < towers.length; j++) { var otherTower = towers[j]; if (otherTower.owner !== enemyTower.owner) { var distance = getDistance(enemyTower, otherTower); var threatLevel = calculateThreatLevel(enemyTower, otherTower, distance); if (threatLevel > 0) { towerThreats.push({ tower: otherTower, distance: distance, threatLevel: threatLevel }); } // Check for opportunities (weak targets) if (otherTower.unitCount < enemyTower.unitCount * 0.8) { towerOpportunities.push({ tower: otherTower, distance: distance, advantage: enemyTower.unitCount - otherTower.unitCount }); } } } state.threats[enemyTower.owner] = towerThreats.sort(function (a, b) { return b.threatLevel - a.threatLevel; }); state.opportunities[enemyTower.owner] = towerOpportunities.sort(function (a, b) { return b.advantage - a.advantage; }); } return state; } // Calculate threat level between two towers function calculateThreatLevel(myTower, enemyTower, distance) { if (distance > 1000) return 0; // Too far to be immediate threat var unitRatio = enemyTower.unitCount / Math.max(myTower.unitCount, 1); var distanceFactor = Math.max(0, 1 - distance / 1000); return unitRatio * distanceFactor * 100; } // Get distance between two towers function getDistance(tower1, tower2) { var dx = tower1.x - tower2.x; var dy = tower1.y - tower2.y; return Math.sqrt(dx * dx + dy * dy); } // Determine optimal strategy for a tower based on game state function getOptimalStrategy(tower, gameState) { var threats = gameState.threats[tower.owner] || []; var opportunities = gameState.opportunities[tower.owner] || []; var isPlayerWinning = gameState.playerStrength > gameState.enemyStrength * 1.2; var isPlayerLosing = gameState.enemyStrength > gameState.playerStrength * 1.2; // Emergency defense if under immediate threat if (threats.length > 0 && threats[0].threatLevel > 50) { return { type: 'EMERGENCY_DEFENSE', target: threats[0].tower, urgency: threats[0].threatLevel }; } // Different faction personalities with adaptive behavior if (tower.owner === 2) { // Faction 2: Adaptive Aggressor - changes tactics based on game state if (isPlayerLosing) { return { type: 'PRESS_ADVANTAGE', target: findWeakestPlayerTower(gameState.playerTowers, tower), minUnits: 8 }; } else if (opportunities.length > 0 && tower.unitCount > 15) { return { type: 'OPPORTUNISTIC_STRIKE', target: opportunities[0].tower, minUnits: 12 }; } else { return { type: 'AGGRESSIVE_EXPAND', target: findNearestEnemy(tower), minUnits: 10 }; } } else if (tower.owner === 3) { // Faction 3: Strategic Defender - focuses on timing and coordination if (tower.unitCount > 30 && isPlayerWinning) { return { type: 'COORDINATED_COUNTER', target: findStrongestPlayerTower(gameState.playerTowers, tower), minUnits: 25 }; } else if (opportunities.length > 0 && tower.unitCount > opportunities[0].tower.unitCount * 1.5) { return { type: 'CALCULATED_STRIKE', target: opportunities[0].tower, minUnits: 20 }; } else { return { type: 'DEFENSIVE_BUILD', target: null, minUnits: 35 }; } } else if (tower.owner === 4) { // Faction 4: Economic Opportunist - focuses on efficient expansion var nearbyNeutrals = findNearbyNeutralTowers(tower, 600); if (nearbyNeutrals.length > 0 && tower.unitCount > 15) { return { type: 'ECONOMIC_EXPANSION', target: nearbyNeutrals[0], minUnits: 12 }; } else if (opportunities.length > 0 && getDistance(tower, opportunities[0].tower) < 500) { return { type: 'EFFICIENT_CAPTURE', target: opportunities[0].tower, minUnits: opportunities[0].tower.unitCount + 5 }; } else { return { type: 'RESOURCE_BUILD', target: null, minUnits: 25 }; } } else { // Default faction: Balanced approach if (opportunities.length > 0 && tower.unitCount > 18) { return { type: 'BALANCED_ATTACK', target: opportunities[0].tower, minUnits: 15 }; } else { return { type: 'DEFENSIVE_BUILD', target: null, minUnits: 20 }; } } } // Execute the determined strategy function executeStrategy(tower, strategy, gameState) { if (!strategy.target && strategy.type.indexOf('BUILD') === -1) return; if (tower.unitCount < strategy.minUnits) return; var unitsToSend; switch (strategy.type) { case 'EMERGENCY_DEFENSE': unitsToSend = Math.floor(tower.unitCount * 0.8); // Send most units in emergency break; case 'PRESS_ADVANTAGE': unitsToSend = Math.floor(tower.unitCount * 0.7); // Aggressive when winning break; case 'COORDINATED_COUNTER': unitsToSend = Math.floor(tower.unitCount * 0.6); // Calculated counter-attack break; case 'OPPORTUNISTIC_STRIKE': case 'CALCULATED_STRIKE': case 'EFFICIENT_CAPTURE': unitsToSend = Math.min(Math.floor(tower.unitCount * 0.6), strategy.target.unitCount + 10); break; case 'ECONOMIC_EXPANSION': unitsToSend = Math.floor(tower.unitCount * 0.4); // Conservative expansion break; case 'AGGRESSIVE_EXPAND': case 'BALANCED_ATTACK': unitsToSend = Math.floor(tower.unitCount * 0.5); // Standard attack break; default: return; // No action for build strategies } if (strategy.target && unitsToSend > 0) { sendUnits(tower, strategy.target, unitsToSend); } } // Helper functions for target selection function findWeakestPlayerTower(playerTowers, fromTower) { var weakest = null; var minUnits = Infinity; for (var i = 0; i < playerTowers.length; i++) { if (playerTowers[i].unitCount < minUnits) { minUnits = playerTowers[i].unitCount; weakest = playerTowers[i]; } } return weakest; } function findStrongestPlayerTower(playerTowers, fromTower) { var strongest = null; var maxUnits = 0; for (var i = 0; i < playerTowers.length; i++) { if (playerTowers[i].unitCount > maxUnits) { maxUnits = playerTowers[i].unitCount; strongest = playerTowers[i]; } } return strongest; } function findNearestEnemy(fromTower) { var nearest = null; var minDist = Infinity; for (var i = 0; i < towers.length; i++) { if (towers[i].owner !== fromTower.owner) { var dist = getDistance(fromTower, towers[i]); if (dist < minDist) { minDist = dist; nearest = towers[i]; } } } return nearest; } function findNearbyNeutralTowers(fromTower, maxDistance) { var nearby = []; for (var i = 0; i < towers.length; i++) { if (towers[i].owner === 0) { var dist = getDistance(fromTower, towers[i]); if (dist <= maxDistance) { nearby.push(towers[i]); } } } return nearby.sort(function (a, b) { return getDistance(fromTower, a) - getDistance(fromTower, b); }); } var deploymentMode = false; var deploymentStartX = 0; var deploymentStartY = 0; game.down = function (x, y, obj) { // Clear previous selections selectedTower = null; selectedUnitGroup = null; isDragging = false; isDraggingUnits = false; if (selectionRing) { selectionRing.visible = false; } // Check for tower upgrade (double tap detection) var currentTime = LK.ticks; for (var i = 0; i < towers.length; i++) { var tower = towers[i]; if (tower.owner === 1 && Math.abs(tower.x - x) < 75 && Math.abs(tower.y - y) < 75) { if (tower.lastTapTime && currentTime - tower.lastTapTime < 30) { // Double tap within 0.5 seconds tower.upgrade(); tower.lastTapTime = null; return; } else { tower.lastTapTime = currentTime; } break; } } // Check if clicking on empty space to start deployment var clickedOnTower = false; var clickedOnGroup = false; // Check if clicked on any tower for (var i = 0; i < towers.length; i++) { var tower = towers[i]; if (Math.abs(tower.x - x) < 75 && Math.abs(tower.y - y) < 75) { clickedOnTower = true; break; } } // Check if clicked on any moving unit group for (var i = 0; i < movingUnitGroups.length; i++) { var group = movingUnitGroups[i]; if (group.visible && Math.abs(group.x - x) < 50 && Math.abs(group.y - y) < 50) { clickedOnGroup = true; break; } } // If clicked on empty space, start deployment mode if (!clickedOnTower && !clickedOnGroup) { deploymentMode = true; deploymentStartX = x; deploymentStartY = y; if (!currentPath) { currentPath = new PathLine(); game.addChild(currentPath); } currentPath.setPath([{ x: x, y: y }]); return; } // First check for moving unit group selection (player units only) for (var i = 0; i < movingUnitGroups.length; i++) { var group = movingUnitGroups[i]; if (group.owner === 1 && group.visible && Math.abs(group.x - x) < 50 && Math.abs(group.y - y) < 50) { selectedUnitGroup = group; isDraggingUnits = true; // Create selection ring if it doesn't exist if (!selectionRing) { selectionRing = game.attachAsset('selectionRing', { anchorX: 0.5, anchorY: 0.5 }); } selectionRing.x = group.x; selectionRing.y = group.y; selectionRing.visible = true; selectionRing.alpha = 0.7; if (!currentPath) { currentPath = new PathLine(); game.addChild(currentPath); } currentPath.setPath([{ x: group.x, y: group.y }]); return; } } // Then check for player tower selection for (var i = 0; i < towers.length; i++) { var tower = towers[i]; if (tower.owner === 1 && Math.abs(tower.x - x) < 75 && Math.abs(tower.y - y) < 75) { selectedTower = tower; isDragging = true; if (!currentPath) { currentPath = new PathLine(); game.addChild(currentPath); } currentPath.setPath([{ x: tower.x, y: tower.y }]); break; } } }; game.move = function (x, y, obj) { if (deploymentMode && currentPath) { currentPath.setPath([{ x: deploymentStartX, y: deploymentStartY }, { x: x, y: y }]); } else if (isDragging && selectedTower && currentPath) { currentPath.setPath([{ x: selectedTower.x, y: selectedTower.y }, { x: x, y: y }]); } else if (isDraggingUnits && selectedUnitGroup && currentPath) { currentPath.setPath([{ x: selectedUnitGroup.x, y: selectedUnitGroup.y }, { x: x, y: y }]); // Update selection ring position if (selectionRing) { selectionRing.x = selectedUnitGroup.x; selectionRing.y = selectedUnitGroup.y; } } }; game.up = function (x, y, obj) { if (deploymentMode) { // Deploy units from start position to destination var distance = Math.sqrt((x - deploymentStartX) * (x - deploymentStartX) + (y - deploymentStartY) * (y - deploymentStartY)); if (distance > 50) { // Minimum distance to deploy deployUnits(deploymentStartX, deploymentStartY, x, y, 5); // Deploy 5 units } deploymentMode = false; } else if (isDragging && selectedTower) { // Find target tower (scaled up detection area) var targetTower = null; for (var i = 0; i < towers.length; i++) { var tower = towers[i]; if (tower !== selectedTower && Math.abs(tower.x - x) < 75 && Math.abs(tower.y - y) < 75) { targetTower = tower; break; } } if (targetTower) { // Check if we have multiple towers selected if (selectedTower.selectedTowerGroup && selectedTower.selectedTowerGroup.length > 1) { // Send units from all selected towers for (var i = 0; i < selectedTower.selectedTowerGroup.length; i++) { var tower = selectedTower.selectedTowerGroup[i]; if (tower.unitCount > 0) { sendUnits(tower, targetTower, Math.floor(tower.unitCount * 0.5)); } } // Clear the group selection selectedTower.selectedTowerGroup = null; } else if (selectedTower.unitCount > 0) { // Single tower selection sendUnits(selectedTower, targetTower, Math.floor(selectedTower.unitCount * 0.5)); } } } else if (isDraggingUnits && selectedUnitGroup) { // Find target tower for redirection var targetTower = null; for (var i = 0; i < towers.length; i++) { var tower = towers[i]; if (Math.abs(tower.x - x) < 75 && Math.abs(tower.y - y) < 75) { targetTower = tower; break; } } // Find target enemy units for direct combat redirection var targetEnemyUnits = []; for (var i = 0; i < units.length; i++) { var enemyUnit = units[i]; if (enemyUnit.owner !== 1 && Math.abs(enemyUnit.x - x) < 50 && Math.abs(enemyUnit.y - y) < 50) { targetEnemyUnits.push(enemyUnit); } } if (targetTower && selectedUnitGroup.units && selectedUnitGroup.units.length > 0) { // Redirect all units in the selected group to target tower var newPath = createPath({ x: selectedUnitGroup.x, y: selectedUnitGroup.y }, targetTower); for (var i = 0; i < selectedUnitGroup.units.length; i++) { var unit = selectedUnitGroup.units[i]; if (unit && unit.parent) { unit.setPath(newPath, targetTower); } } } else if (targetEnemyUnits.length > 0 && selectedUnitGroup.units && selectedUnitGroup.units.length > 0) { // Redirect units to attack enemy units directly // Find the center point of enemy units for targeting var centerX = 0; var centerY = 0; for (var i = 0; i < targetEnemyUnits.length; i++) { centerX += targetEnemyUnits[i].x; centerY += targetEnemyUnits[i].y; } centerX /= targetEnemyUnits.length; centerY /= targetEnemyUnits.length; // Create path to enemy unit area var newPath = createPath({ x: selectedUnitGroup.x, y: selectedUnitGroup.y }, { x: centerX, y: centerY }); for (var i = 0; i < selectedUnitGroup.units.length; i++) { var unit = selectedUnitGroup.units[i]; if (unit && unit.parent) { unit.setPath(newPath, null); // No target tower, just move to combat position } } } else if (selectedUnitGroup.units && selectedUnitGroup.units.length > 0) { // No specific target found - redirect units to clicked position (free movement) var newPath = createPath({ x: selectedUnitGroup.x, y: selectedUnitGroup.y }, { x: x, y: y }); for (var i = 0; i < selectedUnitGroup.units.length; i++) { var unit = selectedUnitGroup.units[i]; if (unit && unit.parent) { // Create scattered path by adding random offsets around destination var scatterRange = 40; // Maximum scatter distance var offsetX = (Math.random() - 0.5) * scatterRange; var offsetY = (Math.random() - 0.5) * scatterRange; // Create path to destination with scatter var scatteredPath = []; for (var j = 0; j < newPath.length; j++) { if (j === newPath.length - 1) { // Final destination with scatter scatteredPath.push({ x: x + offsetX, y: y + offsetY }); } else { scatteredPath.push(newPath[j]); } } unit.setPath(scatteredPath, null); // No target tower, just move to position } } } } if (currentPath) { currentPath.clear(); } if (selectionRing) { selectionRing.visible = false; } isDragging = false; isDraggingUnits = false; selectedTower = null; selectedUnitGroup = null; deploymentMode = false; }; game.update = function () { // Update all units for (var i = units.length - 1; i >= 0; i--) { if (!units[i] || !units[i].parent) { units.splice(i, 1); } } // Update all bullets for (var i = bullets.length - 1; i >= 0; i--) { if (!bullets[i] || !bullets[i].parent) { bullets.splice(i, 1); } } // Run AI every 2 seconds if (LK.ticks % 120 === 0) { runAI(); } // Run memory cleanup memoryManager.performCleanup(); // Update game time display every second if (LK.ticks % 60 === 0) { var elapsedSeconds = Math.floor((LK.ticks - gameStartTime) / 60); var hours = Math.floor(elapsedSeconds / 3600); var minutes = Math.floor(elapsedSeconds % 3600 / 60); var seconds = elapsedSeconds % 60; var timeString = ''; if (hours > 0) { timeString = hours + 'h ' + minutes + 'm ' + seconds + 's'; } else if (minutes > 0) { timeString = minutes + 'm ' + seconds + 's'; } else { timeString = seconds + 's'; } gameTimeText.setText('Game Time: ' + timeString); } // Check win/lose conditions if (LK.ticks % 60 === 0) { checkWinCondition(); } // Update unit count display every 30 ticks (twice per second) if (LK.ticks % 30 === 0) { updateUnitCountDisplay(); updateMovingUnitGroups(); } }; // Create Reset button var resetButton = new Text2('Reset', { size: 60, fill: 0xFFFFFF }); resetButton.anchor.set(1, 1); // Anchor to bottom right LK.gui.bottomRight.addChild(resetButton); // Reset button functionality resetButton.down = function (x, y, obj) { // Clear existing game objects for (var i = 0; i < towers.length; i++) { towers[i].destroy(); } for (var i = 0; i < units.length; i++) { units[i].destroy(); } towers = []; units = []; // Clear bullets for (var i = 0; i < bullets.length; i++) { if (bullets[i] && bullets[i].parent) { bullets[i].destroy(); } } bullets = []; if (currentPath) { currentPath.destroy(); currentPath = null; } // Clean up moving unit groups for (var i = 0; i < movingUnitGroups.length; i++) { if (movingUnitGroups[i].parent) { movingUnitGroups[i].destroy(); } } movingUnitGroups = []; // Reset game state variables selectedTower = null; selectedUnitGroup = null; isDragging = false; isDraggingUnits = false; if (selectionRing) { selectionRing.destroy(); selectionRing = null; } aiStartTime = null; // Reset coins and level to 0 and update storage coins = 0; storage.coins = coins; coinsText.setText('Coins: ' + coins); currentLevel = 0; storage.currentLevel = currentLevel; // Reset game start time gameStartTime = LK.ticks; // Reset game time display immediately gameTimeText.setText('Game Time: 0s'); // Load level 0 (restart from beginning) loadLevel(currentLevel); }; // Initialize first level loadLevel(currentLevel); // Memory Management and Performance Optimization System var memoryManager = { // Object pools for reusing objects instead of creating new ones unitPool: [], sparklePool: [], pathDotPool: [], // Memory cleanup settings cleanupInterval: 300, // Clean up every 5 seconds (300 ticks at 60fps) lastCleanup: 0, // Get unit from pool or create new one getUnit: function getUnit() { if (this.unitPool.length > 0) { var unit = this.unitPool.pop(); // Reset unit properties unit.alpha = 1; unit.visible = true; unit.owner = 1; unit.speed = 3.0; // Will be dynamically calculated during update unit.targetTower = null; unit.pathIndex = 0; unit.path = []; unit.lastWasIntersecting = false; unit.combatProcessed = false; unit.health = 3; unit.maxHealth = 3; // Reset unit graphics to player unit (will be updated by setOwner) unit.setOwner(1); return unit; } return new Unit(); }, // Return unit to pool instead of destroying recycleUnit: function recycleUnit(unit) { if (unit && unit.parent) { unit.parent.removeChild(unit); unit.visible = false; this.unitPool.push(unit); } }, // Get sparkle from pool or create new one getSparkle: function getSparkle() { if (this.sparklePool.length > 0) { var sparkle = this.sparklePool.pop(); sparkle.alpha = 1; sparkle.visible = true; sparkle.scaleX = 0.5 + Math.random() * 0.5; sparkle.scaleY = sparkle.scaleX; return sparkle; } return game.attachAsset('sparkle', { anchorX: 0.5, anchorY: 0.5 }); }, // Return sparkle to pool recycleSparkle: function recycleSparkle(sparkle) { if (sparkle && sparkle.parent) { sparkle.parent.removeChild(sparkle); sparkle.visible = false; this.sparklePool.push(sparkle); } }, // Periodic memory cleanup performCleanup: function performCleanup() { if (LK.ticks - this.lastCleanup < this.cleanupInterval) { return; } this.lastCleanup = LK.ticks; // Clean up destroyed units from arrays for (var i = units.length - 1; i >= 0; i--) { if (!units[i] || !units[i].parent) { units.splice(i, 1); } } // Limit pool sizes to prevent excessive memory usage if (this.unitPool.length > 50) { // Actually destroy excess units var excess = this.unitPool.splice(50); for (var i = 0; i < excess.length; i++) { if (excess[i].destroy) { excess[i].destroy(); } } } if (this.sparklePool.length > 100) { var excess = this.sparklePool.splice(100); for (var i = 0; i < excess.length; i++) { if (excess[i].destroy) { excess[i].destroy(); } } } if (this.pathDotPool.length > 200) { var excess = this.pathDotPool.splice(200); for (var i = 0; i < excess.length; i++) { if (excess[i].destroy) { excess[i].destroy(); } } } // Force garbage collection hint by nullifying references var temp = []; temp = null; } }; // Moving unit group management var movingUnitGroups = []; // Create unit count panel in bottom left var unitCountPanel = new Container(); LK.gui.bottomLeft.addChild(unitCountPanel); // Panel background (optional visual enhancement) var panelBg = LK.getAsset('tower', { width: 300, height: 200, anchorX: 0, anchorY: 1, alpha: 0.3 }); unitCountPanel.addChild(panelBg); // Player unit count text var playerCountText = new Text2('Oyuncu: 0', { size: 40, fill: 0x4a90e2 // Player blue color }); playerCountText.anchor.set(0, 1); playerCountText.x = 10; playerCountText.y = -10; unitCountPanel.addChild(playerCountText); // Enemy faction count texts var enemyCountTexts = []; var enemyColors = [0xe74c3c, 0x9b59b6, 0xf39c12]; // Red, purple, orange for (var i = 0; i < 3; i++) { var enemyText = new Text2('', { size: 40, fill: enemyColors[i] }); enemyText.anchor.set(0, 1); enemyText.x = 10; enemyText.y = -50 - i * 40; unitCountPanel.addChild(enemyText); enemyCountTexts.push(enemyText); } // Function to update moving unit groups function updateMovingUnitGroups() { // Clear existing groups for (var i = 0; i < movingUnitGroups.length; i++) { if (movingUnitGroups[i].parent) { movingUnitGroups[i].parent.removeChild(movingUnitGroups[i]); } } movingUnitGroups = []; // Group units by owner and proximity var processedUnits = []; for (var i = 0; i < units.length; i++) { var unit = units[i]; if (processedUnits.indexOf(unit) !== -1) continue; // Find nearby units of same owner var group = [unit]; processedUnits.push(unit); for (var j = i + 1; j < units.length; j++) { var otherUnit = units[j]; if (processedUnits.indexOf(otherUnit) !== -1) continue; if (otherUnit.owner !== unit.owner) continue; // Check if within grouping distance (100 pixels) var dx = unit.x - otherUnit.x; var dy = unit.y - otherUnit.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 100) { group.push(otherUnit); processedUnits.push(otherUnit); } } // Create group display if more than 1 unit if (group.length > 1) { var groupDisplay = new MovingUnitGroup(); groupDisplay.owner = unit.owner; groupDisplay.updateGroup(group); game.addChild(groupDisplay); movingUnitGroups.push(groupDisplay); } } } // Function to update unit count display function updateUnitCountDisplay() { var playerUnits = 0; var enemyFactions = []; // Array to store enemy faction data // Count units in towers for (var i = 0; i < towers.length; i++) { var tower = towers[i]; if (tower.owner === 1) { playerUnits += tower.unitCount; } else if (tower.owner >= 2 && tower.owner <= 4) { // Find or create enemy faction entry var factionIndex = enemyFactions.findIndex(function (faction) { return faction.owner === tower.owner; }); if (factionIndex === -1) { enemyFactions.push({ owner: tower.owner, units: tower.unitCount, name: assignedEnemyNames[tower.owner] || 'Rakip ' + (tower.owner - 1) }); } else { enemyFactions[factionIndex].units += tower.unitCount; } } } // Count units in transit for (var i = 0; i < units.length; i++) { var unit = units[i]; if (unit.owner === 1) { playerUnits++; } else if (unit.owner >= 2 && unit.owner <= 4) { var factionIndex = enemyFactions.findIndex(function (faction) { return faction.owner === unit.owner; }); if (factionIndex !== -1) { enemyFactions[factionIndex].units++; } } } // Sort enemy factions by unit count (descending) enemyFactions.sort(function (a, b) { return b.units - a.units; }); // Update display texts playerCountText.setText('Oyuncu: ' + playerUnits); // Clear all enemy texts first for (var i = 0; i < enemyCountTexts.length; i++) { enemyCountTexts[i].setText(''); } // Update enemy faction displays with dynamic positioning for (var i = 0; i < Math.min(enemyFactions.length, enemyCountTexts.length); i++) { var faction = enemyFactions[i]; var displayText = faction.name + ': ' + faction.units; // Add strikethrough for zero units if (faction.units === 0) { displayText = '~~' + displayText + '~~'; } enemyCountTexts[i].setText(displayText); // Update position for dynamic sorting (reposition based on current index) enemyCountTexts[i].y = -50 - i * 40; } } // Select All Troops button functionality selectAllButton.down = function (x, y, obj) { // Clear previous selections selectedTower = null; selectedUnitGroup = null; isDragging = false; isDraggingUnits = false; if (selectionRing) { selectionRing.visible = false; } // Find all player units on the screen var allPlayerUnits = []; for (var i = 0; i < units.length; i++) { var unit = units[i]; if (unit.owner === 1 && unit.parent) { allPlayerUnits.push(unit); } } // If we have player units, create a group selection if (allPlayerUnits.length > 0) { // Calculate center position of all units var totalX = 0; var totalY = 0; for (var i = 0; i < allPlayerUnits.length; i++) { totalX += allPlayerUnits[i].x; totalY += allPlayerUnits[i].y; } var centerX = totalX / allPlayerUnits.length; var centerY = totalY / allPlayerUnits.length; // Create or find a group for all units var allUnitsGroup = null; for (var i = 0; i < movingUnitGroups.length; i++) { if (movingUnitGroups[i].owner === 1) { allUnitsGroup = movingUnitGroups[i]; break; } } // If no existing group, create a new one if (!allUnitsGroup) { allUnitsGroup = new MovingUnitGroup(); allUnitsGroup.owner = 1; game.addChild(allUnitsGroup); movingUnitGroups.push(allUnitsGroup); } // Update group with all player units allUnitsGroup.updateGroup(allPlayerUnits); allUnitsGroup.x = centerX; allUnitsGroup.y = centerY - 25; // Select this group selectedUnitGroup = allUnitsGroup; isDraggingUnits = true; // Create selection ring if it doesn't exist if (!selectionRing) { selectionRing = game.attachAsset('selectionRing', { anchorX: 0.5, anchorY: 0.5 }); } selectionRing.x = centerX; selectionRing.y = centerY; selectionRing.visible = true; selectionRing.alpha = 0.7; selectionRing.scaleX = 2; // Make it bigger for all units selection selectionRing.scaleY = 2; // Initialize path for visualization if (!currentPath) { currentPath = new PathLine(); game.addChild(currentPath); } currentPath.setPath([{ x: centerX, y: centerY }]); } }; // Select All Towers button functionality selectAllTowersButton.down = function (x, y, obj) { // Clear previous selections selectedTower = null; selectedUnitGroup = null; isDragging = false; isDraggingUnits = false; if (selectionRing) { selectionRing.visible = false; } // Find all player towers on the screen var allPlayerTowers = []; for (var i = 0; i < towers.length; i++) { var tower = towers[i]; if (tower.owner === 1) { allPlayerTowers.push(tower); } } // If we have player towers, select them all if (allPlayerTowers.length > 0) { // Calculate center position of all towers var totalX = 0; var totalY = 0; for (var i = 0; i < allPlayerTowers.length; i++) { totalX += allPlayerTowers[i].x; totalY += allPlayerTowers[i].y; } var centerX = totalX / allPlayerTowers.length; var centerY = totalY / allPlayerTowers.length; // Select the first tower (for dragging purposes) but indicate all are selected selectedTower = allPlayerTowers[0]; isDragging = true; // Create selection ring if it doesn't exist if (!selectionRing) { selectionRing = game.attachAsset('selectionRing', { anchorX: 0.5, anchorY: 0.5 }); } selectionRing.x = centerX; selectionRing.y = centerY; selectionRing.visible = true; selectionRing.alpha = 0.7; selectionRing.scaleX = 3; // Make it bigger for all towers selection selectionRing.scaleY = 3; // Store all selected towers for group operations selectedTower.selectedTowerGroup = allPlayerTowers; // Initialize path for visualization if (!currentPath) { currentPath = new PathLine(); game.addChild(currentPath); } currentPath.setPath([{ x: centerX, y: centerY }]); } }; // How to play button functionality howToPlayButton.down = function (x, y, obj) { howToPlayPanel.visible = true; }; // Close button functionality closeButton.down = function (x, y, obj) { howToPlayPanel.visible = false; }; // Play background music LK.playMusic('battle'); ; ;
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Bullet = Container.expand(function () {
var self = Container.call(this);
self.speed = 8;
self.damage = 1;
self.targetUnit = null;
self.owner = 1;
var bulletGraphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
self.setTarget = function (target) {
self.targetUnit = target;
};
self.update = function () {
if (!self.targetUnit || !self.targetUnit.parent) {
// Target destroyed, remove bullet
if (self.parent) {
self.destroy();
var index = bullets.indexOf(self);
if (index !== -1) {
bullets.splice(index, 1);
}
}
return;
}
// Move towards target
var dx = self.targetUnit.x - self.x;
var dy = self.targetUnit.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < self.speed) {
// Hit target
if (self.targetUnit.parent) {
// Damage target
self.targetUnit.health -= self.damage;
if (self.targetUnit.health <= 0) {
// Award coins when tower kills enemy unit
if (self.owner === 1 && self.targetUnit.owner >= 2) {
coins += 1;
storage.coins = coins;
coinsText.setText('Coins: ' + coins);
}
// Target destroyed
memoryManager.recycleUnit(self.targetUnit);
var unitIndex = units.indexOf(self.targetUnit);
if (unitIndex !== -1) {
units.splice(unitIndex, 1);
}
}
}
// Remove bullet
if (self.parent) {
self.destroy();
var index = bullets.indexOf(self);
if (index !== -1) {
bullets.splice(index, 1);
}
}
} else {
// Move towards target
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
}
};
return self;
});
var MovingUnitGroup = Container.expand(function () {
var self = Container.call(this);
self.owner = 1;
self.units = [];
self.lastUpdateTime = 0;
// Create count text with same size as tower unit count
self.countText = new Text2('0', {
size: 30,
fill: 0xFFFFFF
});
self.countText.anchor.set(0.5, 0.5);
self.addChild(self.countText);
self.updateGroup = function (nearbyUnits) {
self.units = nearbyUnits;
self.countText.setText(nearbyUnits.length);
// Calculate center position of the group
if (nearbyUnits.length > 0) {
var totalX = 0;
var totalY = 0;
for (var i = 0; i < nearbyUnits.length; i++) {
totalX += nearbyUnits[i].x;
totalY += nearbyUnits[i].y;
}
self.x = totalX / nearbyUnits.length;
self.y = totalY / nearbyUnits.length - 25; // Position above units
self.visible = true;
} else {
self.visible = false;
}
self.lastUpdateTime = LK.ticks;
};
// Add continuous update method to track unit movement
self.update = function () {
if (self.units && self.units.length > 0) {
var totalX = 0;
var totalY = 0;
var validUnits = 0;
// Calculate new center position based on current unit positions
for (var i = 0; i < self.units.length; i++) {
if (self.units[i] && self.units[i].parent) {
totalX += self.units[i].x;
totalY += self.units[i].y;
validUnits++;
}
}
// Update position if we have valid units
if (validUnits > 0) {
self.x = totalX / validUnits;
self.y = totalY / validUnits - 25; // Position above units
self.visible = true;
} else {
self.visible = false;
}
}
};
self.isStale = function () {
return LK.ticks - self.lastUpdateTime > 60; // 1 second at 60fps
};
return self;
});
var PathLine = Container.expand(function () {
var self = Container.call(this);
self.points = [];
self.graphics = [];
self.setPath = function (points) {
// Clear old graphics
for (var i = 0; i < self.graphics.length; i++) {
self.graphics[i].destroy();
}
self.graphics = [];
self.points = points;
// Draw new path
for (var i = 0; i < points.length - 1; i++) {
var dist = Math.sqrt(Math.pow(points[i + 1].x - points[i].x, 2) + Math.pow(points[i + 1].y - points[i].y, 2));
var segments = Math.floor(dist / 20);
for (var j = 0; j < segments; j++) {
var t = j / segments;
var dot;
if (memoryManager.pathDotPool.length > 0) {
dot = memoryManager.pathDotPool.pop();
dot.visible = true;
dot.alpha = 0.5;
self.addChild(dot);
} else {
dot = self.attachAsset('path', {
anchorX: 0.5,
anchorY: 0.5
});
}
dot.x = points[i].x + (points[i + 1].x - points[i].x) * t;
dot.y = points[i].y + (points[i + 1].y - points[i].y) * t;
dot.alpha = 0.5;
self.graphics.push(dot);
}
}
};
self.clear = function () {
for (var i = 0; i < self.graphics.length; i++) {
var dot = self.graphics[i];
if (dot && dot.parent) {
dot.parent.removeChild(dot);
dot.visible = false;
memoryManager.pathDotPool.push(dot);
}
}
self.graphics = [];
self.points = [];
};
return self;
});
var Tower = Container.expand(function () {
var self = Container.call(this);
self.owner = 0; // 0 = neutral, 1 = player, 2 = enemy
self.unitCount = 0;
self.maxUnits = 100;
self.spawnRate = 30; // ticks between spawns
self.lastSpawn = 0;
self.level = 1;
self.attackPower = 2;
self.range = 200;
self.lastAttack = 0;
self.attackRate = 60; // ticks between attacks
// Initialize with neutral tower, will be updated by setOwner
var towerGraphics = self.attachAsset('neutralTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.countText = new Text2('0', {
size: 30,
fill: 0xFFFFFF
});
self.countText.anchor.set(0.5, 1);
self.countText.y = -85; // Position above tower graphic
self.addChild(self.countText);
// Health properties
self.health = 100;
self.maxHealth = 100;
// Health bar container (positioned above level text)
self.healthBarContainer = new Container();
self.healthBarContainer.y = -145; // Position above level text
self.addChild(self.healthBarContainer);
// Background bar (red)
self.healthBarBg = LK.getAsset('particle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 0.6
});
self.healthBarBg.tint = 0xff0000; // Red background
self.healthBarContainer.addChild(self.healthBarBg);
// Foreground bar (green)
self.healthBarFg = LK.getAsset('particle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 0.6
});
self.healthBarFg.tint = 0x00ff00; // Green foreground
self.healthBarContainer.addChild(self.healthBarFg);
// Level text (positioned above unit count)
self.levelText = new Text2('Lv.1', {
size: 25,
fill: 0xFFD700 // Gold color for level
});
self.levelText.anchor.set(0.5, 1);
self.levelText.y = -115.5; // Position above unit count (5% higher: -110 * 1.05)
self.addChild(self.levelText);
// Enemy name text (positioned below tower)
self.nameText = new Text2('', {
size: 40,
fill: 0xFFFFFF
});
self.nameText.anchor.set(0.5, 0);
self.nameText.y = 90; // Position below the tower
self.addChild(self.nameText);
// Upgrade button text (positioned below name text, only for player towers)
self.upgradeText = new Text2('', {
size: 30,
fill: 0x00FF00 // Green color for upgrade button
});
self.upgradeText.anchor.set(0.5, 0);
self.upgradeText.y = 130; // Position below name text
self.addChild(self.upgradeText);
self.setOwner = function (newOwner) {
var oldOwner = self.owner;
self.owner = newOwner;
var targetAsset;
if (newOwner === 0) {
targetAsset = 'neutralTower';
} else if (newOwner === 1) {
targetAsset = 'playerTower';
} else if (newOwner === 2) {
targetAsset = 'enemyTower1';
} else if (newOwner === 3) {
targetAsset = 'enemyTower2';
} else if (newOwner === 4) {
targetAsset = 'enemyTower3';
} else {
targetAsset = 'enemyTower1'; // default enemy red
}
// Replace tower graphics with new asset
if (oldOwner !== newOwner) {
self.removeChild(towerGraphics);
towerGraphics = self.attachAsset(targetAsset, {
anchorX: 0.5,
anchorY: 0.5
});
// Ensure text stays on top of the new graphics
self.removeChild(self.countText);
self.addChild(self.countText);
self.removeChild(self.nameText);
self.addChild(self.nameText);
}
// Update name display
if (newOwner >= 2) {
// Enemy tower - show name
var enemyName = assignEnemyName(newOwner);
self.nameText.setText(enemyName);
self.nameText.alpha = 1;
// Hide upgrade button for enemy towers
self.upgradeText.setText('');
self.upgradeText.alpha = 0;
} else if (newOwner === 1) {
// Player tower - show "Player"
self.nameText.setText('Player');
self.nameText.alpha = 1;
// Show upgrade button for player towers
var upgradeCost = self.level * 20;
self.upgradeText.setText('Upgrade: ' + upgradeCost + ' coins');
self.upgradeText.alpha = 1;
} else {
// Neutral tower - hide name and upgrade button
self.nameText.setText('');
self.nameText.alpha = 0;
self.upgradeText.setText('');
self.upgradeText.alpha = 0;
}
// Graphics already updated above with new asset
};
self.addUnits = function (count) {
// Calculate current max units based on level: 100 * (1.1^level)
var currentMaxUnits = Math.floor(100 * Math.pow(1.1, self.level));
self.maxUnits = currentMaxUnits;
self.unitCount = Math.min(self.unitCount + count, self.maxUnits);
self.countText.setText(Math.floor(self.unitCount));
};
self.removeUnits = function (count) {
self.unitCount = Math.max(0, self.unitCount - count);
self.countText.setText(Math.floor(self.unitCount));
return count;
};
// Method to update health bar
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
self.healthBarFg.scaleX = 4 * healthPercent;
// Change color based on health percentage
if (healthPercent > 0.6) {
self.healthBarFg.tint = 0x00ff00; // Green for healthy
} else if (healthPercent > 0.3) {
self.healthBarFg.tint = 0xffff00; // Yellow for damaged
} else {
self.healthBarFg.tint = 0xff0000; // Red for critical
}
};
self.upgrade = function () {
var upgradeCost = self.level * 20;
if (coins >= upgradeCost) {
coins -= upgradeCost;
storage.coins = coins;
coinsText.setText('Coins: ' + coins);
self.level++;
self.attackPower = self.level * 2;
self.range = 200 + (self.level - 1) * 20;
self.attackRate = Math.max(30, 60 - (self.level - 1) * 5);
// Update maxUnits: 100 * (1.1^level)
self.maxUnits = Math.floor(100 * Math.pow(1.1, self.level));
self.levelText.setText('Lv.' + self.level);
// Update upgrade button text with new cost
var newUpgradeCost = self.level * 20;
self.upgradeText.setText('Upgrade: ' + newUpgradeCost + ' coins');
// Flash effect for upgrade
LK.effects.flashObject(self, 0x00ff00, 500);
return true;
}
return false;
};
self.attack = function () {
if (LK.ticks - self.lastAttack < self.attackRate) return;
// Find enemy units in range
var nearestEnemy = null;
var minDist = Infinity;
for (var i = 0; i < units.length; i++) {
var unit = units[i];
if (unit.owner !== self.owner) {
var dx = self.x - unit.x;
var dy = self.y - unit.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist <= self.range && dist < minDist) {
minDist = dist;
nearestEnemy = unit;
}
}
}
if (nearestEnemy) {
self.lastAttack = LK.ticks;
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.damage = self.attackPower;
bullet.owner = self.owner;
bullet.setTarget(nearestEnemy);
bullets.push(bullet);
game.addChild(bullet);
}
};
self.update = function () {
// Check if tower is destroyed (health <= 0)
if (self.health <= 0) {
// Tower destroyed - convert to neutral tower
self.health = self.maxHealth; // Reset health
self.unitCount = Math.floor(self.maxHealth / 4); // Set some units based on max health
self.setOwner(0); // Make it neutral
self.level = 1; // Reset to level 1
self.attackPower = 2;
self.range = 200;
self.attackRate = 60;
self.levelText.setText('Lv.1');
// Flash effect for destruction
LK.effects.flashObject(self, 0xff0000, 1000);
return;
}
if (self.owner > 0) {
// Calculate dynamic spawn rate based on unit count and level
// More units = faster production (lower spawn rate number)
// Base rate is 30, reduced by unit count factor and level bonus
var levelSpeedBonus = (self.level - 1) * 0.1; // 10% faster per level
var baseSpawnRate = self.spawnRate * (1 - levelSpeedBonus); // Apply level bonus
var dynamicSpawnRate = Math.max(10, baseSpawnRate - Math.floor(self.unitCount / 10));
if (LK.ticks - self.lastSpawn > dynamicSpawnRate) {
self.addUnits(1);
self.lastSpawn = LK.ticks;
}
// Attack nearby enemies
self.attack();
}
// Update health bar display
self.updateHealthBar();
};
return self;
});
var Unit = Container.expand(function () {
var self = Container.call(this);
self.owner = 1;
self.speed = 3.0; // Will be dynamically calculated during update
self.targetTower = null;
self.pathIndex = 0;
self.path = [];
self.health = 3;
self.maxHealth = 3;
// Initialize with player unit, will be updated by setOwner
var unitGraphics = self.attachAsset('playerUnit', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
// Health bar for combat visualization
self.healthBar = new Container();
self.addChild(self.healthBar);
self.healthBar.y = -30; // Position above unit
// Background bar (red)
self.healthBg = LK.getAsset('particle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 0.5
});
self.healthBg.tint = 0xff0000; // Red background
self.healthBar.addChild(self.healthBg);
// Foreground bar (green)
self.healthFg = LK.getAsset('particle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 0.5
});
self.healthFg.tint = 0x00ff00; // Green foreground
self.healthBar.addChild(self.healthFg);
// Initially hide health bar
self.healthBar.visible = false;
// Method to update health bar
self.updateHealthBar = function () {
if (self.health < self.maxHealth) {
self.healthBar.visible = true;
var healthPercent = self.health / self.maxHealth;
self.healthFg.scaleX = 3 * healthPercent;
} else {
self.healthBar.visible = false;
}
};
self.setOwner = function (owner) {
self.owner = owner;
var targetAsset;
if (owner === 1) {
targetAsset = 'playerUnit';
} else if (owner === 2) {
targetAsset = 'enemyUnit1';
} else if (owner === 3) {
targetAsset = 'enemyUnit2';
} else if (owner === 4) {
targetAsset = 'enemyUnit3';
} else {
targetAsset = 'enemyUnit1'; // default enemy red
}
// Replace unit graphics with new asset
self.removeChild(unitGraphics);
unitGraphics = self.attachAsset(targetAsset, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
};
self.setPath = function (path, target) {
// Add random offset to create scattered movement
var scatterRange = 40; // Maximum scatter distance
var offsetX = (Math.random() - 0.5) * scatterRange;
var offsetY = (Math.random() - 0.5) * scatterRange;
// Create scattered path by adding random offsets to each point
self.path = [];
for (var i = 0; i < path.length; i++) {
// Apply consistent offset throughout the path, but reduce it near the end
var offsetFactor = Math.max(0.2, 1 - i / path.length * 0.8);
self.path.push({
x: path[i].x + offsetX * offsetFactor,
y: path[i].y + offsetY * offsetFactor
});
}
self.targetTower = target;
self.pathIndex = 0;
if (self.path.length > 0) {
self.x = self.path[0].x;
self.y = self.path[0].y;
}
};
self.lastWasIntersecting = false;
self.combatProcessed = false;
self.update = function () {
if (!self.targetTower || self.pathIndex >= self.path.length) {
return;
}
// Check if unit reached enemy tower and deal damage
if (self.targetTower && self.targetTower.owner !== self.owner) {
var distanceToTower = Math.sqrt(Math.pow(self.x - self.targetTower.x, 2) + Math.pow(self.y - self.targetTower.y, 2));
// If very close to enemy tower (within 80 pixels), attack it
if (distanceToTower < 80) {
// Deal damage to tower every 120 ticks (2 seconds)
if (LK.ticks % 120 === 0) {
self.targetTower.health -= 2; // Units deal 2 damage to towers
// Create damage effect
var sparkle = memoryManager.getSparkle();
game.addChild(sparkle);
sparkle.x = self.targetTower.x + (Math.random() - 0.5) * 50;
sparkle.y = self.targetTower.y + (Math.random() - 0.5) * 50;
sparkle.alpha = 1;
sparkle.scaleX = 0.8;
sparkle.scaleY = 0.8;
sparkle.tint = 0xff4444; // Red damage effect
tween(sparkle, {
alpha: 0,
scaleX: 0,
scaleY: 0
}, {
duration: 400,
onFinish: function onFinish() {
if (sparkle && sparkle.parent) {
memoryManager.recycleSparkle(sparkle);
}
}
});
}
}
}
// Calculate dynamic speed based on nearby units of same owner
var nearbyUnits = 1; // Count self
for (var i = 0; i < units.length; i++) {
var otherUnit = units[i];
if (otherUnit !== self && otherUnit.owner === self.owner) {
var dx = self.x - otherUnit.x;
var dy = self.y - otherUnit.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 100) {
// Same grouping distance as display
nearbyUnits++;
}
}
}
// Calculate speed: fewer units = faster, more units = slower
// Base speed of 3.0, reduced by group size but with less penalty
// 1 unit: 3.0 speed, 10 units: 2.1 speed, 20+ units: 1.5 speed
var dynamicSpeed = Math.max(1.5, 3.0 - (nearbyUnits - 1) * 0.1);
self.speed = dynamicSpeed;
// Check for combat with enemy units
var currentIntersecting = false;
var inCombat = false;
for (var i = 0; i < units.length; i++) {
var otherUnit = units[i];
if (otherUnit !== self && otherUnit.owner !== self.owner) {
// Check if units are close enough to fight (within 40 pixels for engagement)
var combatDx = self.x - otherUnit.x;
var combatDy = self.y - otherUnit.y;
var combatDist = Math.sqrt(combatDx * combatDx + combatDy * combatDy);
if (combatDist < 40) {
currentIntersecting = true;
inCombat = true;
// Stop movement when in combat
self.speed = 0;
otherUnit.speed = 0;
// Deal damage over time (every 90 ticks = 1.5 seconds)
if (LK.ticks % 90 === 0) {
// Both units deal 1 damage to each other
self.health -= 1;
otherUnit.health -= 1;
// Create small combat effect
var battleX = (self.x + otherUnit.x) / 2;
var battleY = (self.y + otherUnit.y) / 2;
var sparkle = memoryManager.getSparkle();
game.addChild(sparkle);
sparkle.x = battleX + (Math.random() - 0.5) * 20;
sparkle.y = battleY + (Math.random() - 0.5) * 20;
sparkle.alpha = 1;
sparkle.scaleX = 0.3;
sparkle.scaleY = 0.3;
sparkle.tint = 0xff4444; // Red combat effect
tween(sparkle, {
alpha: 0,
scaleX: 0,
scaleY: 0
}, {
duration: 200,
onFinish: function onFinish() {
if (sparkle && sparkle.parent) {
memoryManager.recycleSparkle(sparkle);
}
}
});
// Play combat sound occasionally
if (Math.random() < 0.3) {
LK.getSound('combat').play();
}
}
// Check if either unit died
if (self.health <= 0 && !self.combatProcessed) {
self.combatProcessed = true;
// Award coin if enemy unit kills player unit
if (otherUnit.owner === 1 && self.owner >= 2) {
coins += 1;
storage.coins = coins;
coinsText.setText('Coins: ' + coins);
}
// Award coin if enemy unit dies while attacking player tower
if (self.owner >= 2 && self.targetTower && self.targetTower.owner === 1) {
coins += 1;
storage.coins = coins;
coinsText.setText('Coins: ' + coins);
}
// Create death explosion
var battleX = self.x;
var battleY = self.y;
for (var sparkleIndex = 0; sparkleIndex < 6; sparkleIndex++) {
var sparkle = memoryManager.getSparkle();
game.addChild(sparkle);
sparkle.x = battleX + (Math.random() - 0.5) * 30;
sparkle.y = battleY + (Math.random() - 0.5) * 30;
sparkle.alpha = 1;
sparkle.scaleX = 0.5 + Math.random() * 0.3;
sparkle.scaleY = sparkle.scaleX;
sparkle.tint = 0xff0000; // Red death effect
var randomAngle = Math.random() * Math.PI * 2;
var flyDistance = 30 + Math.random() * 20;
var targetX = sparkle.x + Math.cos(randomAngle) * flyDistance;
var targetY = sparkle.y + Math.sin(randomAngle) * flyDistance;
tween(sparkle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0,
scaleY: 0
}, {
duration: 400 + Math.random() * 200,
easing: tween.easeOut,
onFinish: function onFinish() {
if (sparkle && sparkle.parent) {
memoryManager.recycleSparkle(sparkle);
}
}
});
}
// Flash and destroy self
LK.effects.flashObject(self, 0xff0000, 300);
LK.setTimeout(function () {
if (self && self.parent) {
memoryManager.recycleUnit(self);
var selfIndex = units.indexOf(self);
if (selfIndex !== -1) {
units.splice(selfIndex, 1);
}
}
}, 300);
return;
}
if (otherUnit.health <= 0 && !otherUnit.combatProcessed) {
otherUnit.combatProcessed = true;
// Award coin if player unit kills enemy unit
if (self.owner === 1 && otherUnit.owner >= 2) {
coins += 1;
storage.coins = coins;
coinsText.setText('Coins: ' + coins);
}
// Create death explosion for other unit
var battleX = otherUnit.x;
var battleY = otherUnit.y;
for (var sparkleIndex = 0; sparkleIndex < 6; sparkleIndex++) {
var sparkle = memoryManager.getSparkle();
game.addChild(sparkle);
sparkle.x = battleX + (Math.random() - 0.5) * 30;
sparkle.y = battleY + (Math.random() - 0.5) * 30;
sparkle.alpha = 1;
sparkle.scaleX = 0.5 + Math.random() * 0.3;
sparkle.scaleY = sparkle.scaleX;
sparkle.tint = 0xff0000; // Red death effect
var randomAngle = Math.random() * Math.PI * 2;
var flyDistance = 30 + Math.random() * 20;
var targetX = sparkle.x + Math.cos(randomAngle) * flyDistance;
var targetY = sparkle.y + Math.sin(randomAngle) * flyDistance;
tween(sparkle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0,
scaleY: 0
}, {
duration: 400 + Math.random() * 200,
easing: tween.easeOut,
onFinish: function onFinish() {
if (sparkle && sparkle.parent) {
memoryManager.recycleSparkle(sparkle);
}
}
});
}
// Flash and destroy other unit
LK.effects.flashObject(otherUnit, 0xff0000, 300);
LK.setTimeout(function () {
if (otherUnit && otherUnit.parent) {
memoryManager.recycleUnit(otherUnit);
var otherIndex = units.indexOf(otherUnit);
if (otherIndex !== -1) {
units.splice(otherIndex, 1);
}
}
}, 300);
// Reset self's speed since combat partner is dead
self.speed = 3.0;
}
break; // Only fight one unit at a time
}
}
}
// If not in combat, restore normal speed
if (!inCombat && self.speed === 0) {
self.speed = 3.0;
}
self.lastWasIntersecting = currentIntersecting;
// Update health bar display
self.updateHealthBar();
var target = self.path[self.pathIndex];
var dx = target.x - self.x;
var dy = target.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < self.speed) {
self.pathIndex++;
if (self.pathIndex >= self.path.length) {
if (self.targetTower) {
// Reached target tower
if (self.targetTower.owner === self.owner) {
self.targetTower.addUnits(1);
} else {
self.targetTower.unitCount--;
if (self.targetTower.unitCount < 0) {
self.targetTower.unitCount = 1;
self.targetTower.setOwner(self.owner);
LK.getSound('capture').play();
}
self.targetTower.countText.setText(Math.floor(Math.abs(self.targetTower.unitCount)));
}
memoryManager.recycleUnit(self);
units.splice(units.indexOf(self), 1);
} else {
// No target tower - this is a deployed unit, just wait at destination
// Reset pathIndex to stay at final position
self.pathIndex = self.path.length - 1;
}
}
} else {
// Add slight random movement variation for scattered appearance
var moveX = dx / dist * self.speed;
var moveY = dy / dist * self.speed;
// Add small random deviation (5% of movement speed)
var deviation = self.speed * 0.05;
moveX += (Math.random() - 0.5) * deviation;
moveY += (Math.random() - 0.5) * deviation;
self.x += moveX;
self.y += moveY;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
// Add background image to cover the full game area
var backgroundImage = game.attachAsset('gameBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
// Center horizontally (2048/2)
y: 1366 // Center vertically (2732/2)
});
// Legacy shape assets for other elements
// Unit Assets - Different for each faction
// Tower Assets - Different types for each faction
var towers = [];
var units = [];
var bullets = [];
var movingUnitGroups = [];
var currentPath = null;
var selectionRing = null;
var selectedTower = null;
var selectedUnitGroup = null;
var isDragging = false;
var isDraggingUnits = false;
var currentLevel = storage.currentLevel || 0;
var coins = storage.coins || 0;
var aiStartTime = null; // Track when AI should start (null = not started yet)
// Enemy names for different factions
var enemyNames = ["Karabekir", "Enver", "Talat", "Cemal", "İsmet", "Fevzi", "Kazım", "Refet", "Ali Fuat", "Rauf", "Bekir Sami", "Adnan", "Kâzım Özalp", "Salih", "Nureddin"];
var assignedEnemyNames = {}; // Track names assigned to each enemy faction
// Level configurations - 10 levels with progressive difficulty (scaled up 100%) + 20 units per level
var levels = [{
// Level 1 - Tutorial (Level 1 = +20 units)
towers: [{
x: 300,
y: 1366,
owner: 1,
units: 50
}, {
x: 1024,
y: 800,
owner: 0,
units: 30
}, {
x: 1748,
y: 1366,
owner: 2,
units: 40
}, {
x: 1748,
y: 400,
owner: 3,
units: 35
}]
}, {
// Level 2 - Basic Strategy (Level 2 = +40 units)
towers: [{
x: 300,
y: 400,
owner: 1,
units: 75
}, {
x: 1024,
y: 1366,
owner: 0,
units: 55
}, {
x: 1748,
y: 400,
owner: 2,
units: 65
}, {
x: 1748,
y: 2332,
owner: 3,
units: 65
}, {
x: 300,
y: 2332,
owner: 4,
units: 60
}]
}, {
// Level 3 - Multi-Front (Level 3 = +60 units)
towers: [{
x: 300,
y: 1366,
owner: 1,
units: 105
}, {
x: 700,
y: 800,
owner: 0,
units: 75
}, {
x: 1348,
y: 800,
owner: 0,
units: 75
}, {
x: 700,
y: 1932,
owner: 0,
units: 75
}, {
x: 1348,
y: 1932,
owner: 0,
units: 75
}, {
x: 1748,
y: 400,
owner: 2,
units: 90
}, {
x: 1748,
y: 1366,
owner: 3,
units: 90
}, {
x: 1748,
y: 2332,
owner: 4,
units: 90
}]
}, {
// Level 4 - Defensive Challenge (Level 4 = +80 units)
towers: [{
x: 300,
y: 1366,
owner: 1,
units: 130
}, {
x: 600,
y: 600,
owner: 0,
units: 100
}, {
x: 1024,
y: 400,
owner: 2,
units: 120
}, {
x: 1448,
y: 600,
owner: 0,
units: 100
}, {
x: 1748,
y: 1366,
owner: 0,
units: 100
}, {
x: 1024,
y: 2100,
owner: 0,
units: 100
}]
}, {
// Level 5 - Resource Management (Level 5 = +100 units)
towers: [{
x: 200,
y: 800,
owner: 1,
units: 155
}, {
x: 200,
y: 1932,
owner: 1,
units: 155
}, {
x: 700,
y: 1366,
owner: 0,
units: 125
}, {
x: 1348,
y: 1366,
owner: 0,
units: 125
}, {
x: 1748,
y: 600,
owner: 2,
units: 145
}, {
x: 1748,
y: 1366,
owner: 0,
units: 125
}, {
x: 1748,
y: 2132,
owner: 0,
units: 125
}]
}, {
// Level 6 - Territory Control (Level 6 = +120 units)
towers: [{
x: 300,
y: 400,
owner: 1,
units: 180
}, {
x: 300,
y: 2332,
owner: 1,
units: 180
}, {
x: 600,
y: 800,
owner: 0,
units: 150
}, {
x: 1024,
y: 1366,
owner: 0,
units: 150
}, {
x: 1448,
y: 1932,
owner: 0,
units: 150
}, {
x: 1748,
y: 800,
owner: 2,
units: 170
}, {
x: 1748,
y: 1500,
owner: 0,
units: 150
}, {
x: 1748,
y: 2200,
owner: 0,
units: 150
}]
}, {
// Level 7 - Advanced Tactics (Level 7 = +140 units)
towers: [{
x: 200,
y: 1366,
owner: 1,
units: 205
}, {
x: 500,
y: 600,
owner: 0,
units: 175
}, {
x: 500,
y: 1366,
owner: 0,
units: 175
}, {
x: 500,
y: 2132,
owner: 0,
units: 175
}, {
x: 1024,
y: 400,
owner: 0,
units: 175
}, {
x: 1024,
y: 2332,
owner: 0,
units: 175
}, {
x: 1548,
y: 600,
owner: 0,
units: 175
}, {
x: 1548,
y: 1366,
owner: 0,
units: 175
}, {
x: 1548,
y: 2132,
owner: 0,
units: 175
}, {
x: 1848,
y: 1366,
owner: 2,
units: 195
}]
}, {
// Level 8 - Siege Warfare (Level 8 = +160 units)
towers: [{
x: 300,
y: 600,
owner: 1,
units: 230
}, {
x: 300,
y: 1366,
owner: 1,
units: 230
}, {
x: 300,
y: 2132,
owner: 1,
units: 230
}, {
x: 800,
y: 800,
owner: 0,
units: 200
}, {
x: 800,
y: 1932,
owner: 0,
units: 200
}, {
x: 1248,
y: 800,
owner: 0,
units: 200
}, {
x: 1248,
y: 1932,
owner: 0,
units: 200
}, {
x: 1650,
y: 1366,
owner: 2,
units: 220
}, {
x: 1650,
y: 400,
owner: 0,
units: 200
}, {
x: 1650,
y: 2332,
owner: 0,
units: 200
}]
}, {
// Level 9 - Final Challenge (Level 9 = +180 units)
towers: [{
x: 200,
y: 800,
owner: 1,
units: 255
}, {
x: 200,
y: 1932,
owner: 1,
units: 255
}, {
x: 600,
y: 400,
owner: 0,
units: 225
}, {
x: 600,
y: 1366,
owner: 0,
units: 225
}, {
x: 600,
y: 2332,
owner: 0,
units: 225
}, {
x: 1024,
y: 600,
owner: 0,
units: 225
}, {
x: 1024,
y: 2132,
owner: 0,
units: 225
}, {
x: 1448,
y: 400,
owner: 0,
units: 225
}, {
x: 1448,
y: 1366,
owner: 0,
units: 225
}, {
x: 1448,
y: 2332,
owner: 0,
units: 225
}, {
x: 1748,
y: 1366,
owner: 2,
units: 245
}, {
x: 1748,
y: 600,
owner: 0,
units: 225
}, {
x: 1748,
y: 2132,
owner: 0,
units: 225
}]
}, {
// Level 10 - Master's Trial (Level 10 = +200 units)
towers: [{
x: 150,
y: 1366,
owner: 1,
units: 280
}, {
x: 400,
y: 600,
owner: 0,
units: 250
}, {
x: 400,
y: 1100,
owner: 0,
units: 250
}, {
x: 400,
y: 1632,
owner: 0,
units: 250
}, {
x: 400,
y: 2132,
owner: 0,
units: 250
}, {
x: 800,
y: 400,
owner: 0,
units: 250
}, {
x: 800,
y: 800,
owner: 0,
units: 250
}, {
x: 800,
y: 1932,
owner: 0,
units: 250
}, {
x: 800,
y: 2332,
owner: 0,
units: 250
}, {
x: 1248,
y: 400,
owner: 0,
units: 250
}, {
x: 1248,
y: 800,
owner: 0,
units: 250
}, {
x: 1248,
y: 1932,
owner: 0,
units: 250
}, {
x: 1248,
y: 2332,
owner: 0,
units: 250
}, {
x: 1648,
y: 600,
owner: 0,
units: 250
}, {
x: 1648,
y: 1100,
owner: 0,
units: 250
}, {
x: 1648,
y: 1632,
owner: 0,
units: 250
}, {
x: 1648,
y: 2132,
owner: 0,
units: 250
}, {
x: 1898,
y: 1366,
owner: 2,
units: 270
}]
}];
// UI Elements
var levelText = new Text2('Level ' + (currentLevel + 1), {
size: 40,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
LK.gui.top.addChild(levelText);
// Sound toggle icon (same size as coins text)
var soundEnabled = storage.soundEnabled !== undefined ? storage.soundEnabled : true;
var soundIcon = new Text2(soundEnabled ? '🔊' : '🔇', {
size: 30,
fill: 0xFFFFFF
});
soundIcon.anchor.set(1, 0);
soundIcon.x = -140; // Position to the left of level text (moved 3 lines left)
soundIcon.y = 20;
LK.gui.top.addChild(soundIcon);
// Sound icon click functionality
soundIcon.down = function (x, y, obj) {
soundEnabled = !soundEnabled;
storage.soundEnabled = soundEnabled;
soundIcon.setText(soundEnabled ? '🔊' : '🔇');
if (soundEnabled) {
LK.playMusic('battle');
} else {
LK.stopMusic();
}
};
// Select All Troops button
var selectAllButton = new Text2('Select All Troops', {
size: 30,
fill: 0x00FF00 // Green color
});
selectAllButton.anchor.set(0.5, 0);
selectAllButton.y = 50; // Position below level text
LK.gui.top.addChild(selectAllButton);
// Select All Towers button
var selectAllTowersButton = new Text2('Select All Towers', {
size: 30,
fill: 0x00FF00 // Green color
});
selectAllTowersButton.anchor.set(0.5, 0);
selectAllTowersButton.y = 90; // Position below Select All Troops button
LK.gui.top.addChild(selectAllTowersButton);
// Coins display
var coinsText = new Text2('Coins: ' + coins, {
size: 30,
fill: 0xFFD700 // Gold color
});
coinsText.anchor.set(0, 0);
coinsText.x = 137.5;
coinsText.y = 20;
LK.gui.topLeft.addChild(coinsText);
// Game Time display with tracking
var gameStartTime = LK.ticks;
var gameTimeText = new Text2('Game Time: 0s', {
size: 30,
fill: 0xFFFFFF
});
gameTimeText.anchor.set(1, 0);
gameTimeText.x = -137.5;
gameTimeText.y = 20;
LK.gui.topRight.addChild(gameTimeText);
// How to play button
var howToPlayButton = new Text2('How to play?', {
size: 25,
fill: 0x00FF00 // Green color
});
howToPlayButton.anchor.set(1, 0);
howToPlayButton.x = -137.5;
howToPlayButton.y = 60; // Position below game time
LK.gui.topRight.addChild(howToPlayButton);
// How to play panel (initially hidden)
var howToPlayPanel = new Container();
howToPlayPanel.visible = false;
LK.gui.center.addChild(howToPlayPanel);
// Panel background
var panelBackground = LK.getAsset('tower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 8,
scaleY: 12
});
panelBackground.tint = 0x000000;
panelBackground.alpha = 0.8;
howToPlayPanel.addChild(panelBackground);
// Panel title
var panelTitle = new Text2('HOW TO PLAY', {
size: 50,
fill: 0xFFFFFF
});
panelTitle.anchor.set(0.5, 0.5);
panelTitle.y = -390;
howToPlayPanel.addChild(panelTitle);
// Game instructions text
var instructionsText = new Text2('OBJECTIVE:\nDefeat all enemy factions by capturing their towers\n\nCONTROLS:\n• Tap a tower to select it\n• Drag to another tower to send units\n• Double-tap your towers to upgrade them\n• Tap empty space and drag to deploy units\n• Use "Select All" buttons for mass commands\n\nSTRATEGY:\n• Towers produce units automatically\n• Upgrade towers for more power\n• Coordinate attacks with multiple towers\n• Defend your territory while expanding\n• Earn coins by defeating enemies\n\nPlease check out my other games. I love you guys so much! Thank you so much for playing!', {
size: 28,
fill: 0xFFFFFF
});
instructionsText.anchor.set(0.5, 0.5);
instructionsText.y = -50;
howToPlayPanel.addChild(instructionsText);
// Close button
var closeButton = new Text2('CLOSE', {
size: 40,
fill: 0xFF0000 // Red color
});
closeButton.anchor.set(0.5, 0.5);
closeButton.y = 340; // Moved down 3 lines (90 pixels)
howToPlayPanel.addChild(closeButton);
function assignEnemyName(owner) {
if (!assignedEnemyNames[owner] && owner >= 2) {
// Get available names (not already assigned)
var availableNames = enemyNames.filter(function (name) {
return !Object.values(assignedEnemyNames).includes(name);
});
// If no available names, reuse from the pool
if (availableNames.length === 0) {
availableNames = enemyNames;
}
// Assign random name
var randomIndex = Math.floor(Math.random() * availableNames.length);
assignedEnemyNames[owner] = availableNames[randomIndex];
}
return assignedEnemyNames[owner] || "";
}
function loadLevel(levelIndex) {
// Initialize movingUnitGroups array if undefined
if (!movingUnitGroups) {
movingUnitGroups = [];
}
// Clear existing game objects
for (var i = 0; i < towers.length; i++) {
towers[i].destroy();
}
for (var i = 0; i < units.length; i++) {
units[i].destroy();
}
towers = [];
units = [];
bullets = [];
if (currentPath) {
currentPath.destroy();
currentPath = null;
}
// Clean up moving unit groups
for (var i = 0; i < movingUnitGroups.length; i++) {
if (movingUnitGroups[i].parent) {
movingUnitGroups[i].destroy();
}
}
movingUnitGroups = [];
// Reset game state variables
selectedTower = null;
selectedUnitGroup = null;
isDragging = false;
isDraggingUnits = false;
if (selectionRing) {
selectionRing.destroy();
selectionRing = null;
}
// Reset enemy name assignments
assignedEnemyNames = {};
// Load new level
var levelData = levels[levelIndex % levels.length];
for (var i = 0; i < levelData.towers.length; i++) {
var towerData = levelData.towers[i];
var tower = new Tower();
tower.x = towerData.x;
tower.y = towerData.y;
tower.setOwner(towerData.owner);
tower.addUnits(towerData.units);
towers.push(tower);
game.addChild(tower);
}
levelText.setText('Level ' + (levelIndex + 1));
// Set AI to start after 5 seconds (5000ms)
aiStartTime = LK.ticks + 5 * 60; // 5 seconds at 60 FPS
// Reset game start time for new level
gameStartTime = LK.ticks;
}
function createPath(start, end) {
var points = [];
var steps = 20;
for (var i = 0; i <= steps; i++) {
points.push({
x: start.x + (end.x - start.x) * (i / steps),
y: start.y + (end.y - start.y) * (i / steps)
});
}
return points;
}
function deployUnits(startX, startY, destX, destY, count) {
// Create path from start to destination
var path = createPath({
x: startX,
y: startY
}, {
x: destX,
y: destY
});
for (var i = 0; i < count; i++) {
var unit = memoryManager.getUnit();
unit.setOwner(1); // Player units
// Create a waiting position path - units go to destination and wait
var waitingPath = [];
for (var j = 0; j < path.length; j++) {
waitingPath.push(path[j]);
}
// Add the destination as the final waiting point
waitingPath.push({
x: destX,
y: destY
});
unit.setPath(waitingPath, null); // No target tower, just move to position
units.push(unit);
game.addChild(unit);
// Add random spawn delay and position offset for scattered deployment
var randomDelay = i * (30 + Math.random() * 40); // Random delay between 30-70ms per unit
var spawnOffsetX = (Math.random() - 0.5) * 60; // Random spawn position offset
var spawnOffsetY = (Math.random() - 0.5) * 60;
// Position unit at spawn location with offset
unit.x = startX + spawnOffsetX;
unit.y = startY + spawnOffsetY;
// Stagger unit spawning with random timing
tween(unit, {
x: path[0].x,
y: path[0].y
}, {
duration: randomDelay,
onFinish: function onFinish() {
LK.getSound('deploy').play();
}
});
}
}
function sendUnits(fromTower, toTower, count) {
var path = createPath(fromTower, toTower);
var unitsToSend = Math.min(count, fromTower.unitCount);
for (var i = 0; i < unitsToSend; i++) {
var unit = memoryManager.getUnit();
unit.setOwner(fromTower.owner);
unit.setPath(path, toTower);
units.push(unit);
game.addChild(unit);
// Add random spawn delay and position offset for scattered deployment
var randomDelay = i * (30 + Math.random() * 40); // Random delay between 30-70ms per unit
var spawnOffsetX = (Math.random() - 0.5) * 60; // Random spawn position offset
var spawnOffsetY = (Math.random() - 0.5) * 60;
// Position unit at spawn location with offset
unit.x = fromTower.x + spawnOffsetX;
unit.y = fromTower.y + spawnOffsetY;
// Stagger unit spawning with random timing
tween(unit, {
x: path[0].x,
y: path[0].y
}, {
duration: randomDelay,
onFinish: function onFinish() {
LK.getSound('deploy').play();
}
});
}
fromTower.removeUnits(unitsToSend);
}
function checkWinCondition() {
var playerTowers = 0;
var enemyTowers = 0;
for (var i = 0; i < towers.length; i++) {
if (towers[i].owner === 1) playerTowers++;
if (towers[i].owner >= 2) enemyTowers++;
}
if (enemyTowers === 0) {
// Win condition - player defeated all enemy factions
currentLevel++;
storage.currentLevel = currentLevel;
LK.showYouWin();
} else if (playerTowers === 0) {
// Lose condition - reset coins and restart current level
coins = 0;
storage.coins = coins;
coinsText.setText('Coins: ' + coins);
LK.showGameOver();
}
}
// Multi-faction AI with sophisticated strategies
function runAI() {
// Don't run AI until 5 seconds have passed
if (aiStartTime && LK.ticks < aiStartTime) {
return;
}
// AI tower upgrade logic
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
if (tower.owner >= 2) {
// AI upgrade chance based on level and available resources simulation
var upgradeChance = Math.min(0.02, tower.level * 0.005); // Max 2% chance per frame
if (Math.random() < upgradeChance && tower.level < 5) {
tower.level++;
tower.attackPower = tower.level * 2;
tower.range = 200 + (tower.level - 1) * 20;
tower.attackRate = Math.max(30, 60 - (tower.level - 1) * 5);
tower.levelText.setText('Lv.' + tower.level);
LK.effects.flashObject(tower, 0xff4444, 300);
}
}
}
// Analyze game state for strategic decisions
var gameState = analyzeGameState();
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
if (tower.owner >= 2) {
// Force action when AI towers approach 100 units (at 80+ units)
if (tower.unitCount >= 80) {
// Find any nearby target to attack immediately
var emergencyTarget = findNearestEnemy(tower);
if (emergencyTarget) {
// Send most units to prevent reaching 100
var unitsToSend = Math.floor(tower.unitCount * 0.7);
if (unitsToSend > 0) {
sendUnits(tower, emergencyTarget, unitsToSend);
}
}
} else {
// Normal strategic behavior for towers under 80 units
var strategy = getOptimalStrategy(tower, gameState);
executeStrategy(tower, strategy, gameState);
}
}
}
}
// Analyze current game state for AI decision making
function analyzeGameState() {
var state = {
playerStrength: 0,
enemyStrength: 0,
neutralTowers: 0,
playerTowers: [],
enemyTowers: [],
neutralTowersByDistance: [],
threats: [],
opportunities: []
};
// Calculate faction strengths and positions
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
if (tower.owner === 1) {
state.playerStrength += tower.unitCount;
state.playerTowers.push(tower);
} else if (tower.owner >= 2) {
state.enemyStrength += tower.unitCount;
state.enemyTowers.push(tower);
} else {
state.neutralTowers++;
}
}
// Find strategic opportunities and threats for each enemy tower
for (var i = 0; i < state.enemyTowers.length; i++) {
var enemyTower = state.enemyTowers[i];
var towerThreats = [];
var towerOpportunities = [];
// Analyze threats from player and other enemy factions
for (var j = 0; j < towers.length; j++) {
var otherTower = towers[j];
if (otherTower.owner !== enemyTower.owner) {
var distance = getDistance(enemyTower, otherTower);
var threatLevel = calculateThreatLevel(enemyTower, otherTower, distance);
if (threatLevel > 0) {
towerThreats.push({
tower: otherTower,
distance: distance,
threatLevel: threatLevel
});
}
// Check for opportunities (weak targets)
if (otherTower.unitCount < enemyTower.unitCount * 0.8) {
towerOpportunities.push({
tower: otherTower,
distance: distance,
advantage: enemyTower.unitCount - otherTower.unitCount
});
}
}
}
state.threats[enemyTower.owner] = towerThreats.sort(function (a, b) {
return b.threatLevel - a.threatLevel;
});
state.opportunities[enemyTower.owner] = towerOpportunities.sort(function (a, b) {
return b.advantage - a.advantage;
});
}
return state;
}
// Calculate threat level between two towers
function calculateThreatLevel(myTower, enemyTower, distance) {
if (distance > 1000) return 0; // Too far to be immediate threat
var unitRatio = enemyTower.unitCount / Math.max(myTower.unitCount, 1);
var distanceFactor = Math.max(0, 1 - distance / 1000);
return unitRatio * distanceFactor * 100;
}
// Get distance between two towers
function getDistance(tower1, tower2) {
var dx = tower1.x - tower2.x;
var dy = tower1.y - tower2.y;
return Math.sqrt(dx * dx + dy * dy);
}
// Determine optimal strategy for a tower based on game state
function getOptimalStrategy(tower, gameState) {
var threats = gameState.threats[tower.owner] || [];
var opportunities = gameState.opportunities[tower.owner] || [];
var isPlayerWinning = gameState.playerStrength > gameState.enemyStrength * 1.2;
var isPlayerLosing = gameState.enemyStrength > gameState.playerStrength * 1.2;
// Emergency defense if under immediate threat
if (threats.length > 0 && threats[0].threatLevel > 50) {
return {
type: 'EMERGENCY_DEFENSE',
target: threats[0].tower,
urgency: threats[0].threatLevel
};
}
// Different faction personalities with adaptive behavior
if (tower.owner === 2) {
// Faction 2: Adaptive Aggressor - changes tactics based on game state
if (isPlayerLosing) {
return {
type: 'PRESS_ADVANTAGE',
target: findWeakestPlayerTower(gameState.playerTowers, tower),
minUnits: 8
};
} else if (opportunities.length > 0 && tower.unitCount > 15) {
return {
type: 'OPPORTUNISTIC_STRIKE',
target: opportunities[0].tower,
minUnits: 12
};
} else {
return {
type: 'AGGRESSIVE_EXPAND',
target: findNearestEnemy(tower),
minUnits: 10
};
}
} else if (tower.owner === 3) {
// Faction 3: Strategic Defender - focuses on timing and coordination
if (tower.unitCount > 30 && isPlayerWinning) {
return {
type: 'COORDINATED_COUNTER',
target: findStrongestPlayerTower(gameState.playerTowers, tower),
minUnits: 25
};
} else if (opportunities.length > 0 && tower.unitCount > opportunities[0].tower.unitCount * 1.5) {
return {
type: 'CALCULATED_STRIKE',
target: opportunities[0].tower,
minUnits: 20
};
} else {
return {
type: 'DEFENSIVE_BUILD',
target: null,
minUnits: 35
};
}
} else if (tower.owner === 4) {
// Faction 4: Economic Opportunist - focuses on efficient expansion
var nearbyNeutrals = findNearbyNeutralTowers(tower, 600);
if (nearbyNeutrals.length > 0 && tower.unitCount > 15) {
return {
type: 'ECONOMIC_EXPANSION',
target: nearbyNeutrals[0],
minUnits: 12
};
} else if (opportunities.length > 0 && getDistance(tower, opportunities[0].tower) < 500) {
return {
type: 'EFFICIENT_CAPTURE',
target: opportunities[0].tower,
minUnits: opportunities[0].tower.unitCount + 5
};
} else {
return {
type: 'RESOURCE_BUILD',
target: null,
minUnits: 25
};
}
} else {
// Default faction: Balanced approach
if (opportunities.length > 0 && tower.unitCount > 18) {
return {
type: 'BALANCED_ATTACK',
target: opportunities[0].tower,
minUnits: 15
};
} else {
return {
type: 'DEFENSIVE_BUILD',
target: null,
minUnits: 20
};
}
}
}
// Execute the determined strategy
function executeStrategy(tower, strategy, gameState) {
if (!strategy.target && strategy.type.indexOf('BUILD') === -1) return;
if (tower.unitCount < strategy.minUnits) return;
var unitsToSend;
switch (strategy.type) {
case 'EMERGENCY_DEFENSE':
unitsToSend = Math.floor(tower.unitCount * 0.8); // Send most units in emergency
break;
case 'PRESS_ADVANTAGE':
unitsToSend = Math.floor(tower.unitCount * 0.7); // Aggressive when winning
break;
case 'COORDINATED_COUNTER':
unitsToSend = Math.floor(tower.unitCount * 0.6); // Calculated counter-attack
break;
case 'OPPORTUNISTIC_STRIKE':
case 'CALCULATED_STRIKE':
case 'EFFICIENT_CAPTURE':
unitsToSend = Math.min(Math.floor(tower.unitCount * 0.6), strategy.target.unitCount + 10);
break;
case 'ECONOMIC_EXPANSION':
unitsToSend = Math.floor(tower.unitCount * 0.4); // Conservative expansion
break;
case 'AGGRESSIVE_EXPAND':
case 'BALANCED_ATTACK':
unitsToSend = Math.floor(tower.unitCount * 0.5); // Standard attack
break;
default:
return;
// No action for build strategies
}
if (strategy.target && unitsToSend > 0) {
sendUnits(tower, strategy.target, unitsToSend);
}
}
// Helper functions for target selection
function findWeakestPlayerTower(playerTowers, fromTower) {
var weakest = null;
var minUnits = Infinity;
for (var i = 0; i < playerTowers.length; i++) {
if (playerTowers[i].unitCount < minUnits) {
minUnits = playerTowers[i].unitCount;
weakest = playerTowers[i];
}
}
return weakest;
}
function findStrongestPlayerTower(playerTowers, fromTower) {
var strongest = null;
var maxUnits = 0;
for (var i = 0; i < playerTowers.length; i++) {
if (playerTowers[i].unitCount > maxUnits) {
maxUnits = playerTowers[i].unitCount;
strongest = playerTowers[i];
}
}
return strongest;
}
function findNearestEnemy(fromTower) {
var nearest = null;
var minDist = Infinity;
for (var i = 0; i < towers.length; i++) {
if (towers[i].owner !== fromTower.owner) {
var dist = getDistance(fromTower, towers[i]);
if (dist < minDist) {
minDist = dist;
nearest = towers[i];
}
}
}
return nearest;
}
function findNearbyNeutralTowers(fromTower, maxDistance) {
var nearby = [];
for (var i = 0; i < towers.length; i++) {
if (towers[i].owner === 0) {
var dist = getDistance(fromTower, towers[i]);
if (dist <= maxDistance) {
nearby.push(towers[i]);
}
}
}
return nearby.sort(function (a, b) {
return getDistance(fromTower, a) - getDistance(fromTower, b);
});
}
var deploymentMode = false;
var deploymentStartX = 0;
var deploymentStartY = 0;
game.down = function (x, y, obj) {
// Clear previous selections
selectedTower = null;
selectedUnitGroup = null;
isDragging = false;
isDraggingUnits = false;
if (selectionRing) {
selectionRing.visible = false;
}
// Check for tower upgrade (double tap detection)
var currentTime = LK.ticks;
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
if (tower.owner === 1 && Math.abs(tower.x - x) < 75 && Math.abs(tower.y - y) < 75) {
if (tower.lastTapTime && currentTime - tower.lastTapTime < 30) {
// Double tap within 0.5 seconds
tower.upgrade();
tower.lastTapTime = null;
return;
} else {
tower.lastTapTime = currentTime;
}
break;
}
}
// Check if clicking on empty space to start deployment
var clickedOnTower = false;
var clickedOnGroup = false;
// Check if clicked on any tower
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
if (Math.abs(tower.x - x) < 75 && Math.abs(tower.y - y) < 75) {
clickedOnTower = true;
break;
}
}
// Check if clicked on any moving unit group
for (var i = 0; i < movingUnitGroups.length; i++) {
var group = movingUnitGroups[i];
if (group.visible && Math.abs(group.x - x) < 50 && Math.abs(group.y - y) < 50) {
clickedOnGroup = true;
break;
}
}
// If clicked on empty space, start deployment mode
if (!clickedOnTower && !clickedOnGroup) {
deploymentMode = true;
deploymentStartX = x;
deploymentStartY = y;
if (!currentPath) {
currentPath = new PathLine();
game.addChild(currentPath);
}
currentPath.setPath([{
x: x,
y: y
}]);
return;
}
// First check for moving unit group selection (player units only)
for (var i = 0; i < movingUnitGroups.length; i++) {
var group = movingUnitGroups[i];
if (group.owner === 1 && group.visible && Math.abs(group.x - x) < 50 && Math.abs(group.y - y) < 50) {
selectedUnitGroup = group;
isDraggingUnits = true;
// Create selection ring if it doesn't exist
if (!selectionRing) {
selectionRing = game.attachAsset('selectionRing', {
anchorX: 0.5,
anchorY: 0.5
});
}
selectionRing.x = group.x;
selectionRing.y = group.y;
selectionRing.visible = true;
selectionRing.alpha = 0.7;
if (!currentPath) {
currentPath = new PathLine();
game.addChild(currentPath);
}
currentPath.setPath([{
x: group.x,
y: group.y
}]);
return;
}
}
// Then check for player tower selection
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
if (tower.owner === 1 && Math.abs(tower.x - x) < 75 && Math.abs(tower.y - y) < 75) {
selectedTower = tower;
isDragging = true;
if (!currentPath) {
currentPath = new PathLine();
game.addChild(currentPath);
}
currentPath.setPath([{
x: tower.x,
y: tower.y
}]);
break;
}
}
};
game.move = function (x, y, obj) {
if (deploymentMode && currentPath) {
currentPath.setPath([{
x: deploymentStartX,
y: deploymentStartY
}, {
x: x,
y: y
}]);
} else if (isDragging && selectedTower && currentPath) {
currentPath.setPath([{
x: selectedTower.x,
y: selectedTower.y
}, {
x: x,
y: y
}]);
} else if (isDraggingUnits && selectedUnitGroup && currentPath) {
currentPath.setPath([{
x: selectedUnitGroup.x,
y: selectedUnitGroup.y
}, {
x: x,
y: y
}]);
// Update selection ring position
if (selectionRing) {
selectionRing.x = selectedUnitGroup.x;
selectionRing.y = selectedUnitGroup.y;
}
}
};
game.up = function (x, y, obj) {
if (deploymentMode) {
// Deploy units from start position to destination
var distance = Math.sqrt((x - deploymentStartX) * (x - deploymentStartX) + (y - deploymentStartY) * (y - deploymentStartY));
if (distance > 50) {
// Minimum distance to deploy
deployUnits(deploymentStartX, deploymentStartY, x, y, 5); // Deploy 5 units
}
deploymentMode = false;
} else if (isDragging && selectedTower) {
// Find target tower (scaled up detection area)
var targetTower = null;
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
if (tower !== selectedTower && Math.abs(tower.x - x) < 75 && Math.abs(tower.y - y) < 75) {
targetTower = tower;
break;
}
}
if (targetTower) {
// Check if we have multiple towers selected
if (selectedTower.selectedTowerGroup && selectedTower.selectedTowerGroup.length > 1) {
// Send units from all selected towers
for (var i = 0; i < selectedTower.selectedTowerGroup.length; i++) {
var tower = selectedTower.selectedTowerGroup[i];
if (tower.unitCount > 0) {
sendUnits(tower, targetTower, Math.floor(tower.unitCount * 0.5));
}
}
// Clear the group selection
selectedTower.selectedTowerGroup = null;
} else if (selectedTower.unitCount > 0) {
// Single tower selection
sendUnits(selectedTower, targetTower, Math.floor(selectedTower.unitCount * 0.5));
}
}
} else if (isDraggingUnits && selectedUnitGroup) {
// Find target tower for redirection
var targetTower = null;
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
if (Math.abs(tower.x - x) < 75 && Math.abs(tower.y - y) < 75) {
targetTower = tower;
break;
}
}
// Find target enemy units for direct combat redirection
var targetEnemyUnits = [];
for (var i = 0; i < units.length; i++) {
var enemyUnit = units[i];
if (enemyUnit.owner !== 1 && Math.abs(enemyUnit.x - x) < 50 && Math.abs(enemyUnit.y - y) < 50) {
targetEnemyUnits.push(enemyUnit);
}
}
if (targetTower && selectedUnitGroup.units && selectedUnitGroup.units.length > 0) {
// Redirect all units in the selected group to target tower
var newPath = createPath({
x: selectedUnitGroup.x,
y: selectedUnitGroup.y
}, targetTower);
for (var i = 0; i < selectedUnitGroup.units.length; i++) {
var unit = selectedUnitGroup.units[i];
if (unit && unit.parent) {
unit.setPath(newPath, targetTower);
}
}
} else if (targetEnemyUnits.length > 0 && selectedUnitGroup.units && selectedUnitGroup.units.length > 0) {
// Redirect units to attack enemy units directly
// Find the center point of enemy units for targeting
var centerX = 0;
var centerY = 0;
for (var i = 0; i < targetEnemyUnits.length; i++) {
centerX += targetEnemyUnits[i].x;
centerY += targetEnemyUnits[i].y;
}
centerX /= targetEnemyUnits.length;
centerY /= targetEnemyUnits.length;
// Create path to enemy unit area
var newPath = createPath({
x: selectedUnitGroup.x,
y: selectedUnitGroup.y
}, {
x: centerX,
y: centerY
});
for (var i = 0; i < selectedUnitGroup.units.length; i++) {
var unit = selectedUnitGroup.units[i];
if (unit && unit.parent) {
unit.setPath(newPath, null); // No target tower, just move to combat position
}
}
} else if (selectedUnitGroup.units && selectedUnitGroup.units.length > 0) {
// No specific target found - redirect units to clicked position (free movement)
var newPath = createPath({
x: selectedUnitGroup.x,
y: selectedUnitGroup.y
}, {
x: x,
y: y
});
for (var i = 0; i < selectedUnitGroup.units.length; i++) {
var unit = selectedUnitGroup.units[i];
if (unit && unit.parent) {
// Create scattered path by adding random offsets around destination
var scatterRange = 40; // Maximum scatter distance
var offsetX = (Math.random() - 0.5) * scatterRange;
var offsetY = (Math.random() - 0.5) * scatterRange;
// Create path to destination with scatter
var scatteredPath = [];
for (var j = 0; j < newPath.length; j++) {
if (j === newPath.length - 1) {
// Final destination with scatter
scatteredPath.push({
x: x + offsetX,
y: y + offsetY
});
} else {
scatteredPath.push(newPath[j]);
}
}
unit.setPath(scatteredPath, null); // No target tower, just move to position
}
}
}
}
if (currentPath) {
currentPath.clear();
}
if (selectionRing) {
selectionRing.visible = false;
}
isDragging = false;
isDraggingUnits = false;
selectedTower = null;
selectedUnitGroup = null;
deploymentMode = false;
};
game.update = function () {
// Update all units
for (var i = units.length - 1; i >= 0; i--) {
if (!units[i] || !units[i].parent) {
units.splice(i, 1);
}
}
// Update all bullets
for (var i = bullets.length - 1; i >= 0; i--) {
if (!bullets[i] || !bullets[i].parent) {
bullets.splice(i, 1);
}
}
// Run AI every 2 seconds
if (LK.ticks % 120 === 0) {
runAI();
}
// Run memory cleanup
memoryManager.performCleanup();
// Update game time display every second
if (LK.ticks % 60 === 0) {
var elapsedSeconds = Math.floor((LK.ticks - gameStartTime) / 60);
var hours = Math.floor(elapsedSeconds / 3600);
var minutes = Math.floor(elapsedSeconds % 3600 / 60);
var seconds = elapsedSeconds % 60;
var timeString = '';
if (hours > 0) {
timeString = hours + 'h ' + minutes + 'm ' + seconds + 's';
} else if (minutes > 0) {
timeString = minutes + 'm ' + seconds + 's';
} else {
timeString = seconds + 's';
}
gameTimeText.setText('Game Time: ' + timeString);
}
// Check win/lose conditions
if (LK.ticks % 60 === 0) {
checkWinCondition();
}
// Update unit count display every 30 ticks (twice per second)
if (LK.ticks % 30 === 0) {
updateUnitCountDisplay();
updateMovingUnitGroups();
}
};
// Create Reset button
var resetButton = new Text2('Reset', {
size: 60,
fill: 0xFFFFFF
});
resetButton.anchor.set(1, 1); // Anchor to bottom right
LK.gui.bottomRight.addChild(resetButton);
// Reset button functionality
resetButton.down = function (x, y, obj) {
// Clear existing game objects
for (var i = 0; i < towers.length; i++) {
towers[i].destroy();
}
for (var i = 0; i < units.length; i++) {
units[i].destroy();
}
towers = [];
units = [];
// Clear bullets
for (var i = 0; i < bullets.length; i++) {
if (bullets[i] && bullets[i].parent) {
bullets[i].destroy();
}
}
bullets = [];
if (currentPath) {
currentPath.destroy();
currentPath = null;
}
// Clean up moving unit groups
for (var i = 0; i < movingUnitGroups.length; i++) {
if (movingUnitGroups[i].parent) {
movingUnitGroups[i].destroy();
}
}
movingUnitGroups = [];
// Reset game state variables
selectedTower = null;
selectedUnitGroup = null;
isDragging = false;
isDraggingUnits = false;
if (selectionRing) {
selectionRing.destroy();
selectionRing = null;
}
aiStartTime = null;
// Reset coins and level to 0 and update storage
coins = 0;
storage.coins = coins;
coinsText.setText('Coins: ' + coins);
currentLevel = 0;
storage.currentLevel = currentLevel;
// Reset game start time
gameStartTime = LK.ticks;
// Reset game time display immediately
gameTimeText.setText('Game Time: 0s');
// Load level 0 (restart from beginning)
loadLevel(currentLevel);
};
// Initialize first level
loadLevel(currentLevel);
// Memory Management and Performance Optimization System
var memoryManager = {
// Object pools for reusing objects instead of creating new ones
unitPool: [],
sparklePool: [],
pathDotPool: [],
// Memory cleanup settings
cleanupInterval: 300,
// Clean up every 5 seconds (300 ticks at 60fps)
lastCleanup: 0,
// Get unit from pool or create new one
getUnit: function getUnit() {
if (this.unitPool.length > 0) {
var unit = this.unitPool.pop();
// Reset unit properties
unit.alpha = 1;
unit.visible = true;
unit.owner = 1;
unit.speed = 3.0; // Will be dynamically calculated during update
unit.targetTower = null;
unit.pathIndex = 0;
unit.path = [];
unit.lastWasIntersecting = false;
unit.combatProcessed = false;
unit.health = 3;
unit.maxHealth = 3;
// Reset unit graphics to player unit (will be updated by setOwner)
unit.setOwner(1);
return unit;
}
return new Unit();
},
// Return unit to pool instead of destroying
recycleUnit: function recycleUnit(unit) {
if (unit && unit.parent) {
unit.parent.removeChild(unit);
unit.visible = false;
this.unitPool.push(unit);
}
},
// Get sparkle from pool or create new one
getSparkle: function getSparkle() {
if (this.sparklePool.length > 0) {
var sparkle = this.sparklePool.pop();
sparkle.alpha = 1;
sparkle.visible = true;
sparkle.scaleX = 0.5 + Math.random() * 0.5;
sparkle.scaleY = sparkle.scaleX;
return sparkle;
}
return game.attachAsset('sparkle', {
anchorX: 0.5,
anchorY: 0.5
});
},
// Return sparkle to pool
recycleSparkle: function recycleSparkle(sparkle) {
if (sparkle && sparkle.parent) {
sparkle.parent.removeChild(sparkle);
sparkle.visible = false;
this.sparklePool.push(sparkle);
}
},
// Periodic memory cleanup
performCleanup: function performCleanup() {
if (LK.ticks - this.lastCleanup < this.cleanupInterval) {
return;
}
this.lastCleanup = LK.ticks;
// Clean up destroyed units from arrays
for (var i = units.length - 1; i >= 0; i--) {
if (!units[i] || !units[i].parent) {
units.splice(i, 1);
}
}
// Limit pool sizes to prevent excessive memory usage
if (this.unitPool.length > 50) {
// Actually destroy excess units
var excess = this.unitPool.splice(50);
for (var i = 0; i < excess.length; i++) {
if (excess[i].destroy) {
excess[i].destroy();
}
}
}
if (this.sparklePool.length > 100) {
var excess = this.sparklePool.splice(100);
for (var i = 0; i < excess.length; i++) {
if (excess[i].destroy) {
excess[i].destroy();
}
}
}
if (this.pathDotPool.length > 200) {
var excess = this.pathDotPool.splice(200);
for (var i = 0; i < excess.length; i++) {
if (excess[i].destroy) {
excess[i].destroy();
}
}
}
// Force garbage collection hint by nullifying references
var temp = [];
temp = null;
}
};
// Moving unit group management
var movingUnitGroups = [];
// Create unit count panel in bottom left
var unitCountPanel = new Container();
LK.gui.bottomLeft.addChild(unitCountPanel);
// Panel background (optional visual enhancement)
var panelBg = LK.getAsset('tower', {
width: 300,
height: 200,
anchorX: 0,
anchorY: 1,
alpha: 0.3
});
unitCountPanel.addChild(panelBg);
// Player unit count text
var playerCountText = new Text2('Oyuncu: 0', {
size: 40,
fill: 0x4a90e2 // Player blue color
});
playerCountText.anchor.set(0, 1);
playerCountText.x = 10;
playerCountText.y = -10;
unitCountPanel.addChild(playerCountText);
// Enemy faction count texts
var enemyCountTexts = [];
var enemyColors = [0xe74c3c, 0x9b59b6, 0xf39c12]; // Red, purple, orange
for (var i = 0; i < 3; i++) {
var enemyText = new Text2('', {
size: 40,
fill: enemyColors[i]
});
enemyText.anchor.set(0, 1);
enemyText.x = 10;
enemyText.y = -50 - i * 40;
unitCountPanel.addChild(enemyText);
enemyCountTexts.push(enemyText);
}
// Function to update moving unit groups
function updateMovingUnitGroups() {
// Clear existing groups
for (var i = 0; i < movingUnitGroups.length; i++) {
if (movingUnitGroups[i].parent) {
movingUnitGroups[i].parent.removeChild(movingUnitGroups[i]);
}
}
movingUnitGroups = [];
// Group units by owner and proximity
var processedUnits = [];
for (var i = 0; i < units.length; i++) {
var unit = units[i];
if (processedUnits.indexOf(unit) !== -1) continue;
// Find nearby units of same owner
var group = [unit];
processedUnits.push(unit);
for (var j = i + 1; j < units.length; j++) {
var otherUnit = units[j];
if (processedUnits.indexOf(otherUnit) !== -1) continue;
if (otherUnit.owner !== unit.owner) continue;
// Check if within grouping distance (100 pixels)
var dx = unit.x - otherUnit.x;
var dy = unit.y - otherUnit.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 100) {
group.push(otherUnit);
processedUnits.push(otherUnit);
}
}
// Create group display if more than 1 unit
if (group.length > 1) {
var groupDisplay = new MovingUnitGroup();
groupDisplay.owner = unit.owner;
groupDisplay.updateGroup(group);
game.addChild(groupDisplay);
movingUnitGroups.push(groupDisplay);
}
}
}
// Function to update unit count display
function updateUnitCountDisplay() {
var playerUnits = 0;
var enemyFactions = []; // Array to store enemy faction data
// Count units in towers
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
if (tower.owner === 1) {
playerUnits += tower.unitCount;
} else if (tower.owner >= 2 && tower.owner <= 4) {
// Find or create enemy faction entry
var factionIndex = enemyFactions.findIndex(function (faction) {
return faction.owner === tower.owner;
});
if (factionIndex === -1) {
enemyFactions.push({
owner: tower.owner,
units: tower.unitCount,
name: assignedEnemyNames[tower.owner] || 'Rakip ' + (tower.owner - 1)
});
} else {
enemyFactions[factionIndex].units += tower.unitCount;
}
}
}
// Count units in transit
for (var i = 0; i < units.length; i++) {
var unit = units[i];
if (unit.owner === 1) {
playerUnits++;
} else if (unit.owner >= 2 && unit.owner <= 4) {
var factionIndex = enemyFactions.findIndex(function (faction) {
return faction.owner === unit.owner;
});
if (factionIndex !== -1) {
enemyFactions[factionIndex].units++;
}
}
}
// Sort enemy factions by unit count (descending)
enemyFactions.sort(function (a, b) {
return b.units - a.units;
});
// Update display texts
playerCountText.setText('Oyuncu: ' + playerUnits);
// Clear all enemy texts first
for (var i = 0; i < enemyCountTexts.length; i++) {
enemyCountTexts[i].setText('');
}
// Update enemy faction displays with dynamic positioning
for (var i = 0; i < Math.min(enemyFactions.length, enemyCountTexts.length); i++) {
var faction = enemyFactions[i];
var displayText = faction.name + ': ' + faction.units;
// Add strikethrough for zero units
if (faction.units === 0) {
displayText = '~~' + displayText + '~~';
}
enemyCountTexts[i].setText(displayText);
// Update position for dynamic sorting (reposition based on current index)
enemyCountTexts[i].y = -50 - i * 40;
}
}
// Select All Troops button functionality
selectAllButton.down = function (x, y, obj) {
// Clear previous selections
selectedTower = null;
selectedUnitGroup = null;
isDragging = false;
isDraggingUnits = false;
if (selectionRing) {
selectionRing.visible = false;
}
// Find all player units on the screen
var allPlayerUnits = [];
for (var i = 0; i < units.length; i++) {
var unit = units[i];
if (unit.owner === 1 && unit.parent) {
allPlayerUnits.push(unit);
}
}
// If we have player units, create a group selection
if (allPlayerUnits.length > 0) {
// Calculate center position of all units
var totalX = 0;
var totalY = 0;
for (var i = 0; i < allPlayerUnits.length; i++) {
totalX += allPlayerUnits[i].x;
totalY += allPlayerUnits[i].y;
}
var centerX = totalX / allPlayerUnits.length;
var centerY = totalY / allPlayerUnits.length;
// Create or find a group for all units
var allUnitsGroup = null;
for (var i = 0; i < movingUnitGroups.length; i++) {
if (movingUnitGroups[i].owner === 1) {
allUnitsGroup = movingUnitGroups[i];
break;
}
}
// If no existing group, create a new one
if (!allUnitsGroup) {
allUnitsGroup = new MovingUnitGroup();
allUnitsGroup.owner = 1;
game.addChild(allUnitsGroup);
movingUnitGroups.push(allUnitsGroup);
}
// Update group with all player units
allUnitsGroup.updateGroup(allPlayerUnits);
allUnitsGroup.x = centerX;
allUnitsGroup.y = centerY - 25;
// Select this group
selectedUnitGroup = allUnitsGroup;
isDraggingUnits = true;
// Create selection ring if it doesn't exist
if (!selectionRing) {
selectionRing = game.attachAsset('selectionRing', {
anchorX: 0.5,
anchorY: 0.5
});
}
selectionRing.x = centerX;
selectionRing.y = centerY;
selectionRing.visible = true;
selectionRing.alpha = 0.7;
selectionRing.scaleX = 2; // Make it bigger for all units selection
selectionRing.scaleY = 2;
// Initialize path for visualization
if (!currentPath) {
currentPath = new PathLine();
game.addChild(currentPath);
}
currentPath.setPath([{
x: centerX,
y: centerY
}]);
}
};
// Select All Towers button functionality
selectAllTowersButton.down = function (x, y, obj) {
// Clear previous selections
selectedTower = null;
selectedUnitGroup = null;
isDragging = false;
isDraggingUnits = false;
if (selectionRing) {
selectionRing.visible = false;
}
// Find all player towers on the screen
var allPlayerTowers = [];
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
if (tower.owner === 1) {
allPlayerTowers.push(tower);
}
}
// If we have player towers, select them all
if (allPlayerTowers.length > 0) {
// Calculate center position of all towers
var totalX = 0;
var totalY = 0;
for (var i = 0; i < allPlayerTowers.length; i++) {
totalX += allPlayerTowers[i].x;
totalY += allPlayerTowers[i].y;
}
var centerX = totalX / allPlayerTowers.length;
var centerY = totalY / allPlayerTowers.length;
// Select the first tower (for dragging purposes) but indicate all are selected
selectedTower = allPlayerTowers[0];
isDragging = true;
// Create selection ring if it doesn't exist
if (!selectionRing) {
selectionRing = game.attachAsset('selectionRing', {
anchorX: 0.5,
anchorY: 0.5
});
}
selectionRing.x = centerX;
selectionRing.y = centerY;
selectionRing.visible = true;
selectionRing.alpha = 0.7;
selectionRing.scaleX = 3; // Make it bigger for all towers selection
selectionRing.scaleY = 3;
// Store all selected towers for group operations
selectedTower.selectedTowerGroup = allPlayerTowers;
// Initialize path for visualization
if (!currentPath) {
currentPath = new PathLine();
game.addChild(currentPath);
}
currentPath.setPath([{
x: centerX,
y: centerY
}]);
}
};
// How to play button functionality
howToPlayButton.down = function (x, y, obj) {
howToPlayPanel.visible = true;
};
// Close button functionality
closeButton.down = function (x, y, obj) {
howToPlayPanel.visible = false;
};
// Play background music
LK.playMusic('battle');
;
;
Saldırı kulesi, Gerçekçi, kuş bakışı görünüm, yazısız.. In-Game asset. High contrast. No shadows, 3d olsun.
Saldırı kulesi, Gerçekçi, kuş bakışı görünüm, yazısız.. In-Game asset. High contrast. No shadows, 3d olsun.
Gece çölü arkaplan resmi, Gerçekçi, kuş bakışı görünüm, yazısız. susuz. In-Game asset. High contrast. No shadows, 3d olsun..
Kılıç ve kalkanlı çağı saldırı askeri, Gerçekçi, kuş bakışı görünüm, yazısız.. In-Game asset. High contrast. No shadows, renkli 3d olsun.