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