User prompt
İconu 3 satır sola konumlandır.
User prompt
Level yazısının sol tarafına, Coins yazısı boyutunda bütün sesi açma-kapatma iconu ekle. icon dosyasının resim dosyasını oluştur.
User prompt
Açılan panelin Başlık yazısını 3 satır yukarı konumlandır.
User prompt
Açılan panelin Close butonunu 3 satır aşağı konumlandır. Ayrıca panelin içinde ki yazıların en altına ''Please check out my other games. I love you guys so much! Thank you so much for playing!'' yazısı ekle.
User prompt
''Game Time'' Altına; ''How to play?'' isminde buton oluştur ve tıklayınca açılan panelde oyun hakkında kısa bilgi ve nasıl oynanacağı konusunda kısa bilgilendirme yazısı oluştur.
User prompt
seviye 4 ve devamında ki seviyeler de, her düşman birliği yalnızca 1 tane kule sahibi olarak başlasınlar.
User prompt
Düşman birliği çok güçlü. gücünü %25 oranında azalt.
User prompt
Kulelerimize saldırıp ölen birliklerden de coins kazanalım.
User prompt
Seviye 3'de ölümsüz kale var. Ölümsüz hiçbir seviyede, kale olmasın.
User prompt
Düşman birliği çok güçlü. gücünü %50 oranında azalt.
User prompt
Kulede ki 100 olan max birlik sayısı, her seviyede max birlik sayısı %10 oranında artsın.
User prompt
Kulede ki max birlik sayısı her seviyede %10 oranında artsın.
User prompt
Toplu halde giden birlikler çabuk ölüyorlar. bunu düzelt.
User prompt
Select All Troops yazısının altında, ''Select All Towers'' butonu ekle ve basınca oyun ekranında olan bütün kelelerimiz seçili olsun.
User prompt
Seviye yazısının altında, ''Select All Troops'' butonu ekle ve basınca oyun ekranında olan bütün birlikler seçili olsun.
User prompt
Seviye yazısının altında, ''Select All Troops'' butonu ekle ve basınca oyun ekranında olan bütün birlikler seçili olsun.
User prompt
Eğer kuledeki birim sayısı max ise, kuleye giren birim kenarda beklesin. Yok olmasın.
User prompt
Game Time süresi dakika saat ve saniye olarak görünsün.
User prompt
Çıkarttığımız birlikleri, yönlendirmeyi bıraktığımız yere gelsin .
User prompt
Çıkarttığımız birlikleri, oyun ekranında herhangi bir yere getirebilelim.
User prompt
Çıkarttığımız birlikleri, düşman birliklerine yönlendirebilelim.
User prompt
''Slow down combat speed by increasing damage interval from 30 to 60 ticks (1 second)'' bunu geri al.
User prompt
Kulelerin can hp barı seviye yazlarının üst kısmında görünsün.
User prompt
Seviye yükselince, birim üretim hızı %10 oranında artsın.
User prompt
Birimlerin savaşı yavaş sürsün. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
/**** * 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); // 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) { 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; }; 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); 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 () { if (self.owner > 0) { // Calculate dynamic spawn rate based on unit count // More units = faster production (lower spawn rate number) // Base rate is 30, reduced by unit count factor var dynamicSpawnRate = Math.max(10, self.spawnRate - Math.floor(self.unitCount / 10)); if (LK.ticks - self.lastSpawn > dynamicSpawnRate) { self.addUnits(1); self.lastSpawn = LK.ticks; } // Attack nearby enemies self.attack(); } }; 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; } // 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 // 1 unit: 3.0 speed, 10 units: 1.5 speed, 20+ units: 0.75 speed var dynamicSpeed = Math.max(0.75, 3.0 - (nearbyUnits - 1) * 0.15); 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 60 ticks = 1 second) if (LK.ticks % 60 === 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); } // 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: 2, units: 120 }, { x: 1024, y: 2100, owner: 2, units: 120 }] }, { // 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: 2, units: 145 }, { x: 1748, y: 2132, owner: 2, units: 145 }] }, { // 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: 2, units: 170 }, { x: 1748, y: 2200, owner: 2, units: 170 }] }, { // 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: 400, owner: 2, units: 220 }, { x: 1650, y: 1366, owner: 2, units: 220 }, { x: 1650, y: 2332, owner: 2, units: 220 }] }, { // 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: 600, owner: 2, units: 245 }, { x: 1748, y: 1366, owner: 2, units: 245 }, { x: 1748, y: 2132, owner: 2, units: 245 }] }, { // 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); // 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); 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 && selectedTower.unitCount > 0) { sendUnits(selectedTower, targetTower, Math.floor(selectedTower.unitCount * 0.5)); } } else if (isDraggingUnits && selectedUnitGroup) { // Find target tower or unit group 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; } } if (targetTower && selectedUnitGroup.units && selectedUnitGroup.units.length > 0) { // Redirect all units in the selected group to new target 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); } } } } 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); gameTimeText.setText('Game Time: ' + elapsedSeconds + 's'); } // 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; // 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; } } // 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);
// 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) {
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;
};
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);
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 () {
if (self.owner > 0) {
// Calculate dynamic spawn rate based on unit count
// More units = faster production (lower spawn rate number)
// Base rate is 30, reduced by unit count factor
var dynamicSpawnRate = Math.max(10, self.spawnRate - Math.floor(self.unitCount / 10));
if (LK.ticks - self.lastSpawn > dynamicSpawnRate) {
self.addUnits(1);
self.lastSpawn = LK.ticks;
}
// Attack nearby enemies
self.attack();
}
};
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;
}
// 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
// 1 unit: 3.0 speed, 10 units: 1.5 speed, 20+ units: 0.75 speed
var dynamicSpeed = Math.max(0.75, 3.0 - (nearbyUnits - 1) * 0.15);
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 60 ticks = 1 second)
if (LK.ticks % 60 === 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);
}
// 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: 2,
units: 120
}, {
x: 1024,
y: 2100,
owner: 2,
units: 120
}]
}, {
// 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: 2,
units: 145
}, {
x: 1748,
y: 2132,
owner: 2,
units: 145
}]
}, {
// 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: 2,
units: 170
}, {
x: 1748,
y: 2200,
owner: 2,
units: 170
}]
}, {
// 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: 400,
owner: 2,
units: 220
}, {
x: 1650,
y: 1366,
owner: 2,
units: 220
}, {
x: 1650,
y: 2332,
owner: 2,
units: 220
}]
}, {
// 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: 600,
owner: 2,
units: 245
}, {
x: 1748,
y: 1366,
owner: 2,
units: 245
}, {
x: 1748,
y: 2132,
owner: 2,
units: 245
}]
}, {
// 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);
// 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);
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 && selectedTower.unitCount > 0) {
sendUnits(selectedTower, targetTower, Math.floor(selectedTower.unitCount * 0.5));
}
} else if (isDraggingUnits && selectedUnitGroup) {
// Find target tower or unit group 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;
}
}
if (targetTower && selectedUnitGroup.units && selectedUnitGroup.units.length > 0) {
// Redirect all units in the selected group to new target
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);
}
}
}
}
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);
gameTimeText.setText('Game Time: ' + elapsedSeconds + 's');
}
// 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;
// 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;
}
}
// 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.