/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Ballista = Container.expand(function (isAI) {
var self = Container.call(this);
self.isAI = isAI || false;
self.type = 'ballista';
self.health = 10;
self.maxHealth = 10;
self.range = 300;
self.lastShotTime = 0;
self.target = null;
var assetName = self.isAI ? 'aiBallista' : 'ballista';
var ballistaGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
if (self.health <= 0) return;
self.findTarget();
if (self.target) {
self.shootAtTarget();
}
};
self.findTarget = function () {
var closestDistance = self.range;
self.target = null;
// Check for enemy soldiers
var enemies = self.isAI ? playerSoldiers : aiSoldiers;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy.health <= 0) continue;
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestDistance = distance;
self.target = enemy;
}
}
};
self.shootAtTarget = function () {
if (!self.target) return;
var currentTime = Date.now();
if (currentTime - self.lastShotTime >= 2000) {
// Shoot every 2 seconds
self.target.health -= 1;
LK.getSound('shoot').play();
// Flash effect when shooting
tween(self, {
alpha: 0.5
}, {
duration: 100,
onFinish: function onFinish() {
tween(self, {
alpha: 1
}, {
duration: 100
});
}
});
if (self.target.health <= 0) {
self.target = null;
}
self.lastShotTime = currentTime;
}
};
return self;
});
var Building = Container.expand(function (type, isAI) {
var self = Container.call(this);
self.type = type;
self.isAI = isAI || false;
self.health = self.type === 'mainBuilding' || self.type === 'aiMainBuilding' ? 15 : 10;
self.maxHealth = self.type === 'mainBuilding' || self.type === 'aiMainBuilding' ? 15 : 10;
self.lastSpawnTime = 0;
var assetName = self.isAI ? 'ai' + type.charAt(0).toUpperCase() + type.slice(1) : type;
var buildingGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
if (self.type === 'barracks') {
var currentTime = Date.now();
if (currentTime - self.lastSpawnTime >= 10000) {
self.spawnSoldier();
self.lastSpawnTime = currentTime;
}
}
};
self.spawnSoldier = function () {
var soldier = new Soldier(self.isAI);
soldier.x = self.x + (Math.random() - 0.5) * 100;
soldier.y = self.y + (Math.random() - 0.5) * 100;
if (self.isAI) {
aiSoldiers.push(soldier);
} else {
playerSoldiers.push(soldier);
}
game.addChild(soldier);
};
return self;
});
var Soldier = Container.expand(function (isAI) {
var self = Container.call(this);
self.isAI = isAI || false;
self.health = 5;
self.maxHealth = 5;
self.speed = 1;
self.targetX = 0;
self.targetY = 0;
self.inCombat = false;
self.visionRange = 400;
self.target = null;
self.lastDamageTime = 0;
var assetName = self.isAI ? 'enemySoldier' : 'soldier';
var soldierGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Set initial patrol target - straight line to opposite side
if (self.isAI) {
self.targetX = 0;
self.targetY = 1366;
} else {
self.targetX = 2048;
self.targetY = 1366;
}
self.update = function () {
if (self.health <= 0) {
return;
}
self.findTarget();
if (self.target) {
self.moveToTarget();
self.attackTarget();
} else {
self.patrol();
}
};
self.findTarget = function () {
var closestDistance = self.visionRange;
self.target = null;
// Check for enemy soldiers
var enemies = self.isAI ? playerSoldiers : aiSoldiers;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy.health <= 0) continue;
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestDistance = distance;
self.target = enemy;
}
}
// Check for enemy buildings if no soldiers in range
if (!self.target) {
var enemyBuildings = self.isAI ? playerBuildings : aiBuildings;
for (var i = 0; i < enemyBuildings.length; i++) {
var building = enemyBuildings[i];
if (building.health <= 0) continue;
var dx = building.x - self.x;
var dy = building.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestDistance = distance;
self.target = building;
}
}
}
// Check for enemy walls if no other targets
if (!self.target) {
var enemyWalls = self.isAI ? playerWalls : aiWalls;
for (var i = 0; i < enemyWalls.length; i++) {
var wall = enemyWalls[i];
if (wall.health <= 0) continue;
var dx = wall.x - self.x;
var dy = wall.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestDistance = distance;
self.target = wall;
}
}
}
// Check for enemy ballistae if no other targets
if (!self.target) {
var enemyBallistae = self.isAI ? playerBallistae : aiBallistae;
for (var i = 0; i < enemyBallistae.length; i++) {
var ballista = enemyBallistae[i];
if (ballista.health <= 0) continue;
var dx = ballista.x - self.x;
var dy = ballista.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestDistance = distance;
self.target = ballista;
}
}
}
};
self.moveToTarget = function () {
if (!self.target) return;
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 50) {
// Check for walls blocking movement
var newX = self.x + dx / distance * self.speed;
var newY = self.y + dy / distance * self.speed;
var blocked = false;
// Check player walls
for (var i = 0; i < playerWalls.length; i++) {
var wall = playerWalls[i];
if (wall.health <= 0) continue;
var wallDx = wall.x - newX;
var wallDy = wall.y - newY;
var wallDistance = Math.sqrt(wallDx * wallDx + wallDy * wallDy);
if (wallDistance < 60) {
blocked = true;
break;
}
}
// Check AI walls if not already blocked
if (!blocked) {
for (var i = 0; i < aiWalls.length; i++) {
var wall = aiWalls[i];
if (wall.health <= 0) continue;
var wallDx = wall.x - newX;
var wallDy = wall.y - newY;
var wallDistance = Math.sqrt(wallDx * wallDx + wallDy * wallDy);
if (wallDistance < 60) {
blocked = true;
break;
}
}
}
if (!blocked) {
self.x = newX;
self.y = newY;
}
}
};
self.patrol = function () {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
// Check for walls blocking movement
var newX = self.x + dx / distance * self.speed;
var newY = self.y + dy / distance * self.speed;
var blocked = false;
// Check player walls
for (var i = 0; i < playerWalls.length; i++) {
var wall = playerWalls[i];
if (wall.health <= 0) continue;
var wallDx = wall.x - newX;
var wallDy = wall.y - newY;
var wallDistance = Math.sqrt(wallDx * wallDx + wallDy * wallDy);
if (wallDistance < 60) {
blocked = true;
break;
}
}
// Check AI walls if not already blocked
if (!blocked) {
for (var i = 0; i < aiWalls.length; i++) {
var wall = aiWalls[i];
if (wall.health <= 0) continue;
var wallDx = wall.x - newX;
var wallDy = wall.y - newY;
var wallDistance = Math.sqrt(wallDx * wallDx + wallDy * wallDy);
if (wallDistance < 60) {
blocked = true;
break;
}
}
}
if (!blocked) {
self.x = newX;
self.y = newY;
}
}
};
self.attackTarget = function () {
if (!self.target) return;
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 50) {
var currentTime = Date.now();
if (currentTime - self.lastDamageTime >= 1000) {
// Attack every 1 second
if (self.target.type) {
// Target is a building
self.target.health -= 1;
if (self.target.health <= 0) {
self.target = null;
}
} else {
// Target is a soldier
self.target.health -= 1;
if (self.target.health <= 0) {
self.target = null;
LK.getSound('combat').play();
}
// Counter attack
if (self.target && self.target.health > 0) {
self.health -= 1;
if (self.health <= 0) {
LK.getSound('combat').play();
}
}
}
self.lastDamageTime = currentTime;
}
}
};
self.checkForEnemies = function () {
// This method is now handled by findTarget
};
self.combat = function (enemy) {
// This method is now handled by attackTarget
};
return self;
});
var Wall = Container.expand(function (isAI) {
var self = Container.call(this);
self.isAI = isAI || false;
self.type = 'wall';
self.health = 10;
self.maxHealth = 10;
var assetName = 'wall';
var wallGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228B22
});
/****
* Game Code
****/
var playerGold = 100;
var aiGold = 100;
var playerBuildings = [];
var aiBuildings = [];
var playerSoldiers = [];
var aiSoldiers = [];
var selectedBuildingType = 'goldMine';
var goldMinePrice = 50;
var barracksPrice = 80;
var wallPrice = 10;
var ballistaPrice = 60;
var playerWalls = [];
var aiWalls = [];
var playerBallistae = [];
var aiBallistae = [];
var lastGoldUpdate = Date.now();
var lastAIAction = Date.now();
var territoryLine = 1024;
// UI Elements
var goldText = new Text2('Gold: 100', {
size: 60,
fill: 0xFFD700
});
goldText.anchor.set(0, 0);
goldText.x = 120;
goldText.y = 50;
LK.gui.topLeft.addChild(goldText);
var instructionText = new Text2('Tap to build Gold Mine (50g)', {
size: 40,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0);
instructionText.x = 1024;
instructionText.y = 100;
game.addChild(instructionText);
// Build mode buttons
var goldMineButton = new Text2('Mine', {
size: 50,
fill: 0xFFD700
});
goldMineButton.anchor.set(0.5, 0.5);
goldMineButton.x = 200;
goldMineButton.y = 2600;
game.addChild(goldMineButton);
var barracksButton = new Text2('Barracks', {
size: 50,
fill: 0x8B4513
});
barracksButton.anchor.set(0.5, 0.5);
barracksButton.x = 400;
barracksButton.y = 2600;
game.addChild(barracksButton);
var wallButton = new Text2('Wall', {
size: 50,
fill: 0x8B4513
});
wallButton.anchor.set(0.5, 0.5);
wallButton.x = 600;
wallButton.y = 2600;
game.addChild(wallButton);
var ballistaButton = new Text2('Ballista', {
size: 50,
fill: 0x654321
});
ballistaButton.anchor.set(0.5, 0.5);
ballistaButton.x = 800;
ballistaButton.y = 2600;
game.addChild(ballistaButton);
// Territory divider line
var territoryDivider = LK.getAsset('mainBuilding', {
width: 10,
height: 2732,
anchorX: 0.5,
anchorY: 0
});
territoryDivider.x = territoryLine;
territoryDivider.y = 0;
territoryDivider.alpha = 0.3;
game.addChild(territoryDivider);
// Initialize main buildings
var playerMainBuilding = new Building('mainBuilding', false);
playerMainBuilding.x = 300;
playerMainBuilding.y = 1366;
playerBuildings.push(playerMainBuilding);
game.addChild(playerMainBuilding);
// Create AI main building from scratch
var aiMainBuilding = new Container();
var aiMainGraphics = LK.getAsset('aiMainBuildingShape', {
anchorX: 0.5,
anchorY: 0.5
});
aiMainBuilding.addChild(aiMainGraphics);
aiMainBuilding.x = 1700;
aiMainBuilding.y = 1366;
aiMainBuilding.type = 'aiMainBuilding';
aiMainBuilding.isAI = true;
aiMainBuilding.health = 15;
aiMainBuilding.maxHealth = 15;
aiBuildings.push(aiMainBuilding);
game.addChild(aiMainBuilding);
// Button interactions
goldMineButton.down = function (x, y, obj) {
selectedBuildingType = 'goldMine';
instructionText.setText('Tap to build Gold Mine (50g)');
goldMineButton.alpha = 1.0;
barracksButton.alpha = 0.6;
wallButton.alpha = 0.6;
ballistaButton.alpha = 0.6;
};
barracksButton.down = function (x, y, obj) {
selectedBuildingType = 'barracks';
instructionText.setText('Tap to build Barracks (80g)');
barracksButton.alpha = 1.0;
goldMineButton.alpha = 0.6;
wallButton.alpha = 0.6;
ballistaButton.alpha = 0.6;
};
wallButton.down = function (x, y, obj) {
selectedBuildingType = 'wall';
instructionText.setText('Tap to build Wall (10g)');
wallButton.alpha = 1.0;
goldMineButton.alpha = 0.6;
barracksButton.alpha = 0.6;
ballistaButton.alpha = 0.6;
};
ballistaButton.down = function (x, y, obj) {
selectedBuildingType = 'ballista';
instructionText.setText('Tap to build Ballista (60g)');
ballistaButton.alpha = 1.0;
goldMineButton.alpha = 0.6;
barracksButton.alpha = 0.6;
wallButton.alpha = 0.6;
};
// Initial button states
goldMineButton.alpha = 1.0;
barracksButton.alpha = 0.6;
wallButton.alpha = 0.6;
ballistaButton.alpha = 0.6;
// Game interactions
game.down = function (x, y, obj) {
// Only allow building on player side
if (x > territoryLine) return;
// Prevent building in bottom UI area (bottom 200 pixels)
if (y > 2532) return;
var price;
if (selectedBuildingType === 'goldMine') price = goldMinePrice;else if (selectedBuildingType === 'barracks') price = barracksPrice;else if (selectedBuildingType === 'wall') price = wallPrice;else if (selectedBuildingType === 'ballista') price = ballistaPrice;
if (playerGold >= price) {
// Check if location is not too close to existing buildings (except walls which can be closer)
var canBuild = true;
var minDistance = selectedBuildingType === 'wall' ? 60 : 120;
// Check against buildings
for (var i = 0; i < playerBuildings.length; i++) {
var building = playerBuildings[i];
var dx = building.x - x;
var dy = building.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
canBuild = false;
break;
}
}
// Check against walls
if (canBuild) {
for (var i = 0; i < playerWalls.length; i++) {
var wall = playerWalls[i];
var dx = wall.x - x;
var dy = wall.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 60) {
canBuild = false;
break;
}
}
}
// Check against ballistae
if (canBuild) {
for (var i = 0; i < playerBallistae.length; i++) {
var ballista = playerBallistae[i];
var dx = ballista.x - x;
var dy = ballista.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
canBuild = false;
break;
}
}
}
if (canBuild) {
var newObject;
if (selectedBuildingType === 'wall') {
newObject = new Wall(false);
newObject.x = x;
newObject.y = y;
playerWalls.push(newObject);
} else if (selectedBuildingType === 'ballista') {
newObject = new Ballista(false);
newObject.x = x;
newObject.y = y;
playerBallistae.push(newObject);
} else {
newObject = new Building(selectedBuildingType, false);
newObject.x = x;
newObject.y = y;
newObject.lastSpawnTime = Date.now();
playerBuildings.push(newObject);
}
game.addChild(newObject);
playerGold -= price;
LK.getSound('build').play();
}
}
};
function updateGold() {
var currentTime = Date.now();
var deltaTime = currentTime - lastGoldUpdate;
if (deltaTime >= 2000) {
// Generate gold every 2 seconds
// Count gold mines
var playerMines = 0;
var aiMines = 0;
for (var i = 0; i < playerBuildings.length; i++) {
if (playerBuildings[i].type === 'goldMine') {
playerMines++;
}
}
for (var i = 0; i < aiBuildings.length; i++) {
if (aiBuildings[i].type === 'goldMine') {
aiMines++;
}
}
playerGold += playerMines * 10;
aiGold += aiMines * 10; // AI generates same rate as player
goldText.setText('Gold: ' + playerGold);
lastGoldUpdate = currentTime;
}
}
function updateAI() {
var currentTime = Date.now();
if (currentTime - lastAIAction >= 5000) {
// AI acts every 5 seconds
// Count AI buildings to make intelligent decisions
var aiMineCount = 0;
var aiBarracksCount = 0;
var aiBallistaCount = 0;
var aiWallCount = 0;
for (var i = 0; i < aiBuildings.length; i++) {
if (aiBuildings[i].type === 'goldMine') {
aiMineCount++;
}
if (aiBuildings[i].type === 'barracks') {
aiBarracksCount++;
}
}
for (var i = 0; i < aiBallistae.length; i++) {
if (aiBallistae[i].health > 0) {
aiBallistaCount++;
}
}
for (var i = 0; i < aiWalls.length; i++) {
if (aiWalls[i].health > 0) {
aiWallCount++;
}
}
// Always build gold mine first, then make intelligent decisions
var buildingType;
if (aiMineCount === 0) {
buildingType = 'goldMine';
} else {
// Create weighted choices based on what AI has least of
var choices = [];
// Base weights
var mineWeight = aiMineCount === 0 ? 50 : aiMineCount < 2 ? 30 : 10;
var barracksWeight = aiBarracksCount === 0 ? 40 : aiBarracksCount < 2 ? 25 : 15;
var ballistaWeight = aiBallistaCount === 0 ? 35 : aiBallistaCount < 2 ? 20 : 10;
var wallWeight = aiWallCount === 0 && aiMineCount > 0 ? 30 : aiWallCount < 3 && aiMineCount > 0 ? 15 : 0;
// Add choices based on weights
for (var w = 0; w < mineWeight; w++) choices.push('goldMine');
for (var w = 0; w < barracksWeight; w++) choices.push('barracks');
for (var w = 0; w < ballistaWeight; w++) choices.push('ballista');
for (var w = 0; w < wallWeight; w++) choices.push('wall');
// Choose randomly from weighted array
if (choices.length > 0) {
buildingType = choices[Math.floor(Math.random() * choices.length)];
} else {
buildingType = 'goldMine';
}
}
var price;
if (buildingType === 'goldMine') price = goldMinePrice;else if (buildingType === 'barracks') price = barracksPrice;else if (buildingType === 'ballista') price = ballistaPrice;else if (buildingType === 'wall') price = wallPrice;
if (aiGold >= price) {
var attempts = 0;
var built = false;
while (attempts < 10 && !built) {
var x, y;
var canBuild = true;
var minDistance = buildingType === 'wall' ? 60 : 120;
if (buildingType === 'wall') {
// Build walls in a straight line from AI HQ towards player side
var wallSpacing = 150;
var baseX = 1700 - aiWallCount * wallSpacing;
if (baseX < territoryLine + 50) baseX = territoryLine + 50;
x = baseX;
y = 1366 + (Math.random() - 0.5) * 100; // Slight Y variation
} else {
x = territoryLine + 100 + Math.random() * (2048 - territoryLine - 200);
y = 200 + Math.random() * (2732 - 400);
}
// Check against buildings
for (var i = 0; i < aiBuildings.length; i++) {
var building = aiBuildings[i];
var dx = building.x - x;
var dy = building.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
canBuild = false;
break;
}
}
// Check against walls
if (canBuild && buildingType !== 'wall') {
for (var i = 0; i < aiWalls.length; i++) {
var wall = aiWalls[i];
var dx = wall.x - x;
var dy = wall.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 60) {
canBuild = false;
break;
}
}
}
// Check against ballistae
if (canBuild) {
for (var i = 0; i < aiBallistae.length; i++) {
var ballista = aiBallistae[i];
var dx = ballista.x - x;
var dy = ballista.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
canBuild = false;
break;
}
}
}
if (canBuild) {
var newObject;
if (buildingType === 'ballista') {
// Place ballista closer to main building for defense
x = 1700 + (Math.random() - 0.5) * 400;
y = 1366 + (Math.random() - 0.5) * 400;
// Ensure it's still in AI territory
if (x < territoryLine + 50) x = territoryLine + 50;
if (x > 2048 - 50) x = 2048 - 50;
if (y < 200) y = 200;
if (y > 2532) y = 2532;
newObject = new Ballista(true);
newObject.x = x;
newObject.y = y;
aiBallistae.push(newObject);
} else if (buildingType === 'wall') {
newObject = new Wall(true);
newObject.x = x;
newObject.y = y;
aiWalls.push(newObject);
} else {
newObject = new Building(buildingType, true);
newObject.x = x;
newObject.y = y;
newObject.lastSpawnTime = Date.now();
aiBuildings.push(newObject);
}
game.addChild(newObject);
aiGold -= price;
built = true;
}
attempts++;
}
}
lastAIAction = currentTime;
}
}
function checkGameEnd() {
// Clean up dead soldiers
for (var i = playerSoldiers.length - 1; i >= 0; i--) {
if (playerSoldiers[i].health <= 0) {
playerSoldiers[i].destroy();
playerSoldiers.splice(i, 1);
}
}
for (var i = aiSoldiers.length - 1; i >= 0; i--) {
if (aiSoldiers[i].health <= 0) {
aiSoldiers[i].destroy();
aiSoldiers.splice(i, 1);
}
}
// Clean up destroyed buildings
for (var i = playerBuildings.length - 1; i >= 0; i--) {
if (playerBuildings[i].health <= 0) {
playerBuildings[i].destroy();
playerBuildings.splice(i, 1);
}
}
for (var i = aiBuildings.length - 1; i >= 0; i--) {
if (aiBuildings[i].health <= 0) {
aiBuildings[i].destroy();
aiBuildings.splice(i, 1);
}
}
// Clean up destroyed walls
for (var i = playerWalls.length - 1; i >= 0; i--) {
if (playerWalls[i].health <= 0) {
playerWalls[i].destroy();
playerWalls.splice(i, 1);
}
}
for (var i = aiWalls.length - 1; i >= 0; i--) {
if (aiWalls[i].health <= 0) {
aiWalls[i].destroy();
aiWalls.splice(i, 1);
}
}
// Clean up destroyed ballistae
for (var i = playerBallistae.length - 1; i >= 0; i--) {
if (playerBallistae[i].health <= 0) {
playerBallistae[i].destroy();
playerBallistae.splice(i, 1);
}
}
for (var i = aiBallistae.length - 1; i >= 0; i--) {
if (aiBallistae[i].health <= 0) {
aiBallistae[i].destroy();
aiBallistae.splice(i, 1);
}
}
// Check win conditions - main building destruction
var playerMainDestroyed = true;
var aiMainDestroyed = true;
for (var i = 0; i < playerBuildings.length; i++) {
if (playerBuildings[i].type === 'mainBuilding' && playerBuildings[i].health > 0) {
playerMainDestroyed = false;
break;
}
}
for (var i = 0; i < aiBuildings.length; i++) {
if (aiBuildings[i].type === 'aiMainBuilding' && aiBuildings[i].health > 0) {
aiMainDestroyed = false;
break;
}
}
// Win condition: Destroy AI main building
if (aiMainDestroyed) {
LK.showYouWin();
}
// Lose condition: Player main building destroyed
if (playerMainDestroyed) {
LK.showGameOver();
}
}
game.update = function () {
updateGold();
updateAI();
checkGameEnd();
};
// Start background music
LK.playMusic('background'); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Ballista = Container.expand(function (isAI) {
var self = Container.call(this);
self.isAI = isAI || false;
self.type = 'ballista';
self.health = 10;
self.maxHealth = 10;
self.range = 300;
self.lastShotTime = 0;
self.target = null;
var assetName = self.isAI ? 'aiBallista' : 'ballista';
var ballistaGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
if (self.health <= 0) return;
self.findTarget();
if (self.target) {
self.shootAtTarget();
}
};
self.findTarget = function () {
var closestDistance = self.range;
self.target = null;
// Check for enemy soldiers
var enemies = self.isAI ? playerSoldiers : aiSoldiers;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy.health <= 0) continue;
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestDistance = distance;
self.target = enemy;
}
}
};
self.shootAtTarget = function () {
if (!self.target) return;
var currentTime = Date.now();
if (currentTime - self.lastShotTime >= 2000) {
// Shoot every 2 seconds
self.target.health -= 1;
LK.getSound('shoot').play();
// Flash effect when shooting
tween(self, {
alpha: 0.5
}, {
duration: 100,
onFinish: function onFinish() {
tween(self, {
alpha: 1
}, {
duration: 100
});
}
});
if (self.target.health <= 0) {
self.target = null;
}
self.lastShotTime = currentTime;
}
};
return self;
});
var Building = Container.expand(function (type, isAI) {
var self = Container.call(this);
self.type = type;
self.isAI = isAI || false;
self.health = self.type === 'mainBuilding' || self.type === 'aiMainBuilding' ? 15 : 10;
self.maxHealth = self.type === 'mainBuilding' || self.type === 'aiMainBuilding' ? 15 : 10;
self.lastSpawnTime = 0;
var assetName = self.isAI ? 'ai' + type.charAt(0).toUpperCase() + type.slice(1) : type;
var buildingGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
if (self.type === 'barracks') {
var currentTime = Date.now();
if (currentTime - self.lastSpawnTime >= 10000) {
self.spawnSoldier();
self.lastSpawnTime = currentTime;
}
}
};
self.spawnSoldier = function () {
var soldier = new Soldier(self.isAI);
soldier.x = self.x + (Math.random() - 0.5) * 100;
soldier.y = self.y + (Math.random() - 0.5) * 100;
if (self.isAI) {
aiSoldiers.push(soldier);
} else {
playerSoldiers.push(soldier);
}
game.addChild(soldier);
};
return self;
});
var Soldier = Container.expand(function (isAI) {
var self = Container.call(this);
self.isAI = isAI || false;
self.health = 5;
self.maxHealth = 5;
self.speed = 1;
self.targetX = 0;
self.targetY = 0;
self.inCombat = false;
self.visionRange = 400;
self.target = null;
self.lastDamageTime = 0;
var assetName = self.isAI ? 'enemySoldier' : 'soldier';
var soldierGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Set initial patrol target - straight line to opposite side
if (self.isAI) {
self.targetX = 0;
self.targetY = 1366;
} else {
self.targetX = 2048;
self.targetY = 1366;
}
self.update = function () {
if (self.health <= 0) {
return;
}
self.findTarget();
if (self.target) {
self.moveToTarget();
self.attackTarget();
} else {
self.patrol();
}
};
self.findTarget = function () {
var closestDistance = self.visionRange;
self.target = null;
// Check for enemy soldiers
var enemies = self.isAI ? playerSoldiers : aiSoldiers;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy.health <= 0) continue;
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestDistance = distance;
self.target = enemy;
}
}
// Check for enemy buildings if no soldiers in range
if (!self.target) {
var enemyBuildings = self.isAI ? playerBuildings : aiBuildings;
for (var i = 0; i < enemyBuildings.length; i++) {
var building = enemyBuildings[i];
if (building.health <= 0) continue;
var dx = building.x - self.x;
var dy = building.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestDistance = distance;
self.target = building;
}
}
}
// Check for enemy walls if no other targets
if (!self.target) {
var enemyWalls = self.isAI ? playerWalls : aiWalls;
for (var i = 0; i < enemyWalls.length; i++) {
var wall = enemyWalls[i];
if (wall.health <= 0) continue;
var dx = wall.x - self.x;
var dy = wall.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestDistance = distance;
self.target = wall;
}
}
}
// Check for enemy ballistae if no other targets
if (!self.target) {
var enemyBallistae = self.isAI ? playerBallistae : aiBallistae;
for (var i = 0; i < enemyBallistae.length; i++) {
var ballista = enemyBallistae[i];
if (ballista.health <= 0) continue;
var dx = ballista.x - self.x;
var dy = ballista.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestDistance = distance;
self.target = ballista;
}
}
}
};
self.moveToTarget = function () {
if (!self.target) return;
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 50) {
// Check for walls blocking movement
var newX = self.x + dx / distance * self.speed;
var newY = self.y + dy / distance * self.speed;
var blocked = false;
// Check player walls
for (var i = 0; i < playerWalls.length; i++) {
var wall = playerWalls[i];
if (wall.health <= 0) continue;
var wallDx = wall.x - newX;
var wallDy = wall.y - newY;
var wallDistance = Math.sqrt(wallDx * wallDx + wallDy * wallDy);
if (wallDistance < 60) {
blocked = true;
break;
}
}
// Check AI walls if not already blocked
if (!blocked) {
for (var i = 0; i < aiWalls.length; i++) {
var wall = aiWalls[i];
if (wall.health <= 0) continue;
var wallDx = wall.x - newX;
var wallDy = wall.y - newY;
var wallDistance = Math.sqrt(wallDx * wallDx + wallDy * wallDy);
if (wallDistance < 60) {
blocked = true;
break;
}
}
}
if (!blocked) {
self.x = newX;
self.y = newY;
}
}
};
self.patrol = function () {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
// Check for walls blocking movement
var newX = self.x + dx / distance * self.speed;
var newY = self.y + dy / distance * self.speed;
var blocked = false;
// Check player walls
for (var i = 0; i < playerWalls.length; i++) {
var wall = playerWalls[i];
if (wall.health <= 0) continue;
var wallDx = wall.x - newX;
var wallDy = wall.y - newY;
var wallDistance = Math.sqrt(wallDx * wallDx + wallDy * wallDy);
if (wallDistance < 60) {
blocked = true;
break;
}
}
// Check AI walls if not already blocked
if (!blocked) {
for (var i = 0; i < aiWalls.length; i++) {
var wall = aiWalls[i];
if (wall.health <= 0) continue;
var wallDx = wall.x - newX;
var wallDy = wall.y - newY;
var wallDistance = Math.sqrt(wallDx * wallDx + wallDy * wallDy);
if (wallDistance < 60) {
blocked = true;
break;
}
}
}
if (!blocked) {
self.x = newX;
self.y = newY;
}
}
};
self.attackTarget = function () {
if (!self.target) return;
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 50) {
var currentTime = Date.now();
if (currentTime - self.lastDamageTime >= 1000) {
// Attack every 1 second
if (self.target.type) {
// Target is a building
self.target.health -= 1;
if (self.target.health <= 0) {
self.target = null;
}
} else {
// Target is a soldier
self.target.health -= 1;
if (self.target.health <= 0) {
self.target = null;
LK.getSound('combat').play();
}
// Counter attack
if (self.target && self.target.health > 0) {
self.health -= 1;
if (self.health <= 0) {
LK.getSound('combat').play();
}
}
}
self.lastDamageTime = currentTime;
}
}
};
self.checkForEnemies = function () {
// This method is now handled by findTarget
};
self.combat = function (enemy) {
// This method is now handled by attackTarget
};
return self;
});
var Wall = Container.expand(function (isAI) {
var self = Container.call(this);
self.isAI = isAI || false;
self.type = 'wall';
self.health = 10;
self.maxHealth = 10;
var assetName = 'wall';
var wallGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228B22
});
/****
* Game Code
****/
var playerGold = 100;
var aiGold = 100;
var playerBuildings = [];
var aiBuildings = [];
var playerSoldiers = [];
var aiSoldiers = [];
var selectedBuildingType = 'goldMine';
var goldMinePrice = 50;
var barracksPrice = 80;
var wallPrice = 10;
var ballistaPrice = 60;
var playerWalls = [];
var aiWalls = [];
var playerBallistae = [];
var aiBallistae = [];
var lastGoldUpdate = Date.now();
var lastAIAction = Date.now();
var territoryLine = 1024;
// UI Elements
var goldText = new Text2('Gold: 100', {
size: 60,
fill: 0xFFD700
});
goldText.anchor.set(0, 0);
goldText.x = 120;
goldText.y = 50;
LK.gui.topLeft.addChild(goldText);
var instructionText = new Text2('Tap to build Gold Mine (50g)', {
size: 40,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0);
instructionText.x = 1024;
instructionText.y = 100;
game.addChild(instructionText);
// Build mode buttons
var goldMineButton = new Text2('Mine', {
size: 50,
fill: 0xFFD700
});
goldMineButton.anchor.set(0.5, 0.5);
goldMineButton.x = 200;
goldMineButton.y = 2600;
game.addChild(goldMineButton);
var barracksButton = new Text2('Barracks', {
size: 50,
fill: 0x8B4513
});
barracksButton.anchor.set(0.5, 0.5);
barracksButton.x = 400;
barracksButton.y = 2600;
game.addChild(barracksButton);
var wallButton = new Text2('Wall', {
size: 50,
fill: 0x8B4513
});
wallButton.anchor.set(0.5, 0.5);
wallButton.x = 600;
wallButton.y = 2600;
game.addChild(wallButton);
var ballistaButton = new Text2('Ballista', {
size: 50,
fill: 0x654321
});
ballistaButton.anchor.set(0.5, 0.5);
ballistaButton.x = 800;
ballistaButton.y = 2600;
game.addChild(ballistaButton);
// Territory divider line
var territoryDivider = LK.getAsset('mainBuilding', {
width: 10,
height: 2732,
anchorX: 0.5,
anchorY: 0
});
territoryDivider.x = territoryLine;
territoryDivider.y = 0;
territoryDivider.alpha = 0.3;
game.addChild(territoryDivider);
// Initialize main buildings
var playerMainBuilding = new Building('mainBuilding', false);
playerMainBuilding.x = 300;
playerMainBuilding.y = 1366;
playerBuildings.push(playerMainBuilding);
game.addChild(playerMainBuilding);
// Create AI main building from scratch
var aiMainBuilding = new Container();
var aiMainGraphics = LK.getAsset('aiMainBuildingShape', {
anchorX: 0.5,
anchorY: 0.5
});
aiMainBuilding.addChild(aiMainGraphics);
aiMainBuilding.x = 1700;
aiMainBuilding.y = 1366;
aiMainBuilding.type = 'aiMainBuilding';
aiMainBuilding.isAI = true;
aiMainBuilding.health = 15;
aiMainBuilding.maxHealth = 15;
aiBuildings.push(aiMainBuilding);
game.addChild(aiMainBuilding);
// Button interactions
goldMineButton.down = function (x, y, obj) {
selectedBuildingType = 'goldMine';
instructionText.setText('Tap to build Gold Mine (50g)');
goldMineButton.alpha = 1.0;
barracksButton.alpha = 0.6;
wallButton.alpha = 0.6;
ballistaButton.alpha = 0.6;
};
barracksButton.down = function (x, y, obj) {
selectedBuildingType = 'barracks';
instructionText.setText('Tap to build Barracks (80g)');
barracksButton.alpha = 1.0;
goldMineButton.alpha = 0.6;
wallButton.alpha = 0.6;
ballistaButton.alpha = 0.6;
};
wallButton.down = function (x, y, obj) {
selectedBuildingType = 'wall';
instructionText.setText('Tap to build Wall (10g)');
wallButton.alpha = 1.0;
goldMineButton.alpha = 0.6;
barracksButton.alpha = 0.6;
ballistaButton.alpha = 0.6;
};
ballistaButton.down = function (x, y, obj) {
selectedBuildingType = 'ballista';
instructionText.setText('Tap to build Ballista (60g)');
ballistaButton.alpha = 1.0;
goldMineButton.alpha = 0.6;
barracksButton.alpha = 0.6;
wallButton.alpha = 0.6;
};
// Initial button states
goldMineButton.alpha = 1.0;
barracksButton.alpha = 0.6;
wallButton.alpha = 0.6;
ballistaButton.alpha = 0.6;
// Game interactions
game.down = function (x, y, obj) {
// Only allow building on player side
if (x > territoryLine) return;
// Prevent building in bottom UI area (bottom 200 pixels)
if (y > 2532) return;
var price;
if (selectedBuildingType === 'goldMine') price = goldMinePrice;else if (selectedBuildingType === 'barracks') price = barracksPrice;else if (selectedBuildingType === 'wall') price = wallPrice;else if (selectedBuildingType === 'ballista') price = ballistaPrice;
if (playerGold >= price) {
// Check if location is not too close to existing buildings (except walls which can be closer)
var canBuild = true;
var minDistance = selectedBuildingType === 'wall' ? 60 : 120;
// Check against buildings
for (var i = 0; i < playerBuildings.length; i++) {
var building = playerBuildings[i];
var dx = building.x - x;
var dy = building.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
canBuild = false;
break;
}
}
// Check against walls
if (canBuild) {
for (var i = 0; i < playerWalls.length; i++) {
var wall = playerWalls[i];
var dx = wall.x - x;
var dy = wall.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 60) {
canBuild = false;
break;
}
}
}
// Check against ballistae
if (canBuild) {
for (var i = 0; i < playerBallistae.length; i++) {
var ballista = playerBallistae[i];
var dx = ballista.x - x;
var dy = ballista.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
canBuild = false;
break;
}
}
}
if (canBuild) {
var newObject;
if (selectedBuildingType === 'wall') {
newObject = new Wall(false);
newObject.x = x;
newObject.y = y;
playerWalls.push(newObject);
} else if (selectedBuildingType === 'ballista') {
newObject = new Ballista(false);
newObject.x = x;
newObject.y = y;
playerBallistae.push(newObject);
} else {
newObject = new Building(selectedBuildingType, false);
newObject.x = x;
newObject.y = y;
newObject.lastSpawnTime = Date.now();
playerBuildings.push(newObject);
}
game.addChild(newObject);
playerGold -= price;
LK.getSound('build').play();
}
}
};
function updateGold() {
var currentTime = Date.now();
var deltaTime = currentTime - lastGoldUpdate;
if (deltaTime >= 2000) {
// Generate gold every 2 seconds
// Count gold mines
var playerMines = 0;
var aiMines = 0;
for (var i = 0; i < playerBuildings.length; i++) {
if (playerBuildings[i].type === 'goldMine') {
playerMines++;
}
}
for (var i = 0; i < aiBuildings.length; i++) {
if (aiBuildings[i].type === 'goldMine') {
aiMines++;
}
}
playerGold += playerMines * 10;
aiGold += aiMines * 10; // AI generates same rate as player
goldText.setText('Gold: ' + playerGold);
lastGoldUpdate = currentTime;
}
}
function updateAI() {
var currentTime = Date.now();
if (currentTime - lastAIAction >= 5000) {
// AI acts every 5 seconds
// Count AI buildings to make intelligent decisions
var aiMineCount = 0;
var aiBarracksCount = 0;
var aiBallistaCount = 0;
var aiWallCount = 0;
for (var i = 0; i < aiBuildings.length; i++) {
if (aiBuildings[i].type === 'goldMine') {
aiMineCount++;
}
if (aiBuildings[i].type === 'barracks') {
aiBarracksCount++;
}
}
for (var i = 0; i < aiBallistae.length; i++) {
if (aiBallistae[i].health > 0) {
aiBallistaCount++;
}
}
for (var i = 0; i < aiWalls.length; i++) {
if (aiWalls[i].health > 0) {
aiWallCount++;
}
}
// Always build gold mine first, then make intelligent decisions
var buildingType;
if (aiMineCount === 0) {
buildingType = 'goldMine';
} else {
// Create weighted choices based on what AI has least of
var choices = [];
// Base weights
var mineWeight = aiMineCount === 0 ? 50 : aiMineCount < 2 ? 30 : 10;
var barracksWeight = aiBarracksCount === 0 ? 40 : aiBarracksCount < 2 ? 25 : 15;
var ballistaWeight = aiBallistaCount === 0 ? 35 : aiBallistaCount < 2 ? 20 : 10;
var wallWeight = aiWallCount === 0 && aiMineCount > 0 ? 30 : aiWallCount < 3 && aiMineCount > 0 ? 15 : 0;
// Add choices based on weights
for (var w = 0; w < mineWeight; w++) choices.push('goldMine');
for (var w = 0; w < barracksWeight; w++) choices.push('barracks');
for (var w = 0; w < ballistaWeight; w++) choices.push('ballista');
for (var w = 0; w < wallWeight; w++) choices.push('wall');
// Choose randomly from weighted array
if (choices.length > 0) {
buildingType = choices[Math.floor(Math.random() * choices.length)];
} else {
buildingType = 'goldMine';
}
}
var price;
if (buildingType === 'goldMine') price = goldMinePrice;else if (buildingType === 'barracks') price = barracksPrice;else if (buildingType === 'ballista') price = ballistaPrice;else if (buildingType === 'wall') price = wallPrice;
if (aiGold >= price) {
var attempts = 0;
var built = false;
while (attempts < 10 && !built) {
var x, y;
var canBuild = true;
var minDistance = buildingType === 'wall' ? 60 : 120;
if (buildingType === 'wall') {
// Build walls in a straight line from AI HQ towards player side
var wallSpacing = 150;
var baseX = 1700 - aiWallCount * wallSpacing;
if (baseX < territoryLine + 50) baseX = territoryLine + 50;
x = baseX;
y = 1366 + (Math.random() - 0.5) * 100; // Slight Y variation
} else {
x = territoryLine + 100 + Math.random() * (2048 - territoryLine - 200);
y = 200 + Math.random() * (2732 - 400);
}
// Check against buildings
for (var i = 0; i < aiBuildings.length; i++) {
var building = aiBuildings[i];
var dx = building.x - x;
var dy = building.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
canBuild = false;
break;
}
}
// Check against walls
if (canBuild && buildingType !== 'wall') {
for (var i = 0; i < aiWalls.length; i++) {
var wall = aiWalls[i];
var dx = wall.x - x;
var dy = wall.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 60) {
canBuild = false;
break;
}
}
}
// Check against ballistae
if (canBuild) {
for (var i = 0; i < aiBallistae.length; i++) {
var ballista = aiBallistae[i];
var dx = ballista.x - x;
var dy = ballista.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
canBuild = false;
break;
}
}
}
if (canBuild) {
var newObject;
if (buildingType === 'ballista') {
// Place ballista closer to main building for defense
x = 1700 + (Math.random() - 0.5) * 400;
y = 1366 + (Math.random() - 0.5) * 400;
// Ensure it's still in AI territory
if (x < territoryLine + 50) x = territoryLine + 50;
if (x > 2048 - 50) x = 2048 - 50;
if (y < 200) y = 200;
if (y > 2532) y = 2532;
newObject = new Ballista(true);
newObject.x = x;
newObject.y = y;
aiBallistae.push(newObject);
} else if (buildingType === 'wall') {
newObject = new Wall(true);
newObject.x = x;
newObject.y = y;
aiWalls.push(newObject);
} else {
newObject = new Building(buildingType, true);
newObject.x = x;
newObject.y = y;
newObject.lastSpawnTime = Date.now();
aiBuildings.push(newObject);
}
game.addChild(newObject);
aiGold -= price;
built = true;
}
attempts++;
}
}
lastAIAction = currentTime;
}
}
function checkGameEnd() {
// Clean up dead soldiers
for (var i = playerSoldiers.length - 1; i >= 0; i--) {
if (playerSoldiers[i].health <= 0) {
playerSoldiers[i].destroy();
playerSoldiers.splice(i, 1);
}
}
for (var i = aiSoldiers.length - 1; i >= 0; i--) {
if (aiSoldiers[i].health <= 0) {
aiSoldiers[i].destroy();
aiSoldiers.splice(i, 1);
}
}
// Clean up destroyed buildings
for (var i = playerBuildings.length - 1; i >= 0; i--) {
if (playerBuildings[i].health <= 0) {
playerBuildings[i].destroy();
playerBuildings.splice(i, 1);
}
}
for (var i = aiBuildings.length - 1; i >= 0; i--) {
if (aiBuildings[i].health <= 0) {
aiBuildings[i].destroy();
aiBuildings.splice(i, 1);
}
}
// Clean up destroyed walls
for (var i = playerWalls.length - 1; i >= 0; i--) {
if (playerWalls[i].health <= 0) {
playerWalls[i].destroy();
playerWalls.splice(i, 1);
}
}
for (var i = aiWalls.length - 1; i >= 0; i--) {
if (aiWalls[i].health <= 0) {
aiWalls[i].destroy();
aiWalls.splice(i, 1);
}
}
// Clean up destroyed ballistae
for (var i = playerBallistae.length - 1; i >= 0; i--) {
if (playerBallistae[i].health <= 0) {
playerBallistae[i].destroy();
playerBallistae.splice(i, 1);
}
}
for (var i = aiBallistae.length - 1; i >= 0; i--) {
if (aiBallistae[i].health <= 0) {
aiBallistae[i].destroy();
aiBallistae.splice(i, 1);
}
}
// Check win conditions - main building destruction
var playerMainDestroyed = true;
var aiMainDestroyed = true;
for (var i = 0; i < playerBuildings.length; i++) {
if (playerBuildings[i].type === 'mainBuilding' && playerBuildings[i].health > 0) {
playerMainDestroyed = false;
break;
}
}
for (var i = 0; i < aiBuildings.length; i++) {
if (aiBuildings[i].type === 'aiMainBuilding' && aiBuildings[i].health > 0) {
aiMainDestroyed = false;
break;
}
}
// Win condition: Destroy AI main building
if (aiMainDestroyed) {
LK.showYouWin();
}
// Lose condition: Player main building destroyed
if (playerMainDestroyed) {
LK.showGameOver();
}
}
game.update = function () {
updateGold();
updateAI();
checkGameEnd();
};
// Start background music
LK.playMusic('background');
Tempale of fantasy games. In-Game asset. 2d. High contrast. No shadows
Anciet world barracks, no words. In-Game asset. 2d. High contrast. No shadows
Gold mine of clash of clans. In-Game asset. 2d. High contrast. No shadows
חייל עם חרב ומגן. In-Game asset. 2d. High contrast. No shadows
מכונה שיורה חצים. In-Game asset. 2d. High contrast. No shadows