/**** * 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