User prompt
WHEN YOU DEVELOP THE CASTLE WITH 850 GOLD, IT SHOULD APPEAR NEXT TO THE WALL_BUTTON AND IT SHOULD SAY 250 G UNDER IT
User prompt
For the enemy's easy mode, wait 0.9 seconds before sending each unit, for normal mode, wait 0.7 seconds, for hard mode, wait 0.5 seconds.
User prompt
Cannot damage other units of opponents that touch wall_player and wall_enemy. After defeating them, damages others.
User prompt
A unit cannot damage wall_player and wall_enemy alone, there must be at least two people.
User prompt
Also remove the time limit for me to send an archer unit
User prompt
There is no time limit for me, I can upgrade my castle whenever I want.3
User prompt
Initial Phase (0-30 seconds): If I send soldiers, let them build walls If there are more than 3 soldiers alive, let them build walls Only send soldiers. Do not upgrade at all. Middle Phase (30-60 seconds): Soldier + Archer + Wall alternately. Think of each one as rolling 1-3 dice and send them accordingly. Do not send archers without doing the 1st upgrade Even if you have enough gold for the 1st upgrade, do it with a 50% chance (not immediately). Enemy units start coming in mixed. Advanced Phase (60 seconds +): Upgradeable. Soldier + Archer + Wall alternately. Think of each one as rolling 1-3 dice and send them accordingly. After the upgrade, archer sending is activated. For upgrade: Let the battle progress. If the player has upgraded or lost 10 units, let the enemy think of the 2nd upgrade.
User prompt
Starting Phase (0-30 seconds): Do not upgrade in any way.
User prompt
Starting Phase (0-30 seconds): Send only soldiers. Do not upgrade in any way. Blowing up walls is not prohibited. Blowing up archers is prohibited.
User prompt
Middle Phase (30-60 seconds): 5 soldiers, 2 archers, 1 wall alternate Even if you have enough gold for the 1st upgrade, do it with 50% chance (not immediately). Enemy units start coming in mixed.
User prompt
Starting Phase (0-30 seconds): Send soldiers only. Do not upgrade in any way. Blowing up walls is prohibited.
User prompt
It is forbidden for the enemy to develop it by giving 350 gold, wait 15 seconds and then develop it
User prompt
The enemy should not develop his castle immediately, wait 15 seconds
User prompt
Find the mistake the enemy AI made and prevent it from doing it
User prompt
Look, there is a bug right now. Enemy_castle is upgraded immediately with 350 gold. This shouldn't happen.
User prompt
Even if the enemy has gold, he should not give 350 gold and strengthen his castle without taking out 5 units.
User prompt
These are integers, the enemy should neither develop it before nor after, in easy mode, after increasing the first development of the castle by 10 units, after increasing the second castle reinforcement by 30 units.
User prompt
Let the enemy do the first castle development in easy mode after removing 10 units and the second castle reinforcement after removing 30 units.
User prompt
Find a solution for this and somehow let her play like a player like me, send soldiers, build walls, send archers, raise the castle level and then raise it again and do these.
User prompt
The enemy should not skip castle upgrades, when they are done the gold amount may go to minus, then when the gold increases again, they should continue sending units.
User prompt
Let the enemy castle upgrade 350 gold as well as 800 gold, of course first upgrade the castle 350 gold and then upgrade the castle 800 gold after a while.
User prompt
add speed mode where gold flows 3x faster this is extra, add it under these in easy, normal, hard menu
User prompt
Add speed mode where gold flows 3x faster extra
User prompt
When the enemy gives 800 gold and upgrades the 2nd stage castle, the health of the castle will be 2500 like the health of player_castle and the arrow shooting speed will increase like in castle_player.
User prompt
If the enemy gives 800 gold and uses wall_enemy 15 times since the game starts to upgrade the 2nd stage castle, it should be done automatically.
/**** * Plugins ****/ var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Arrow class for castle arrow shooting var Arrow = Container.expand(function () { var self = Container.call(this); // Increase arrow speed for longer distance self.speed = 40; // px per frame (was 32) self.damage = 2 + Math.floor(Math.random() * 3); // 2-4 damage self.target = null; self.team = null; self.destroyed = false; self.asset = null; self.lastX = 0; self.lastY = 0; // Initialize arrow self.init = function (x, y, target, damage, team) { self.x = x; self.y = y; self.target = target; // Arrow damage is randomized 2-4, ignore passed damage param for castle arrows self.damage = 2 + Math.floor(Math.random() * 3); self.team = team; // Remove previous asset if any if (self.asset) { self.removeChild(self.asset); } // Use a visible arrow using a rectangle shape since centerCircle is removed self.asset = self.attachAsset('arrow_shape', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 0.3, tint: team === 'player' ? 0x00aaff : 0xffaa00 // more visible blue/orange }); }; // Update arrow position and check for hit self.update = function () { self.lastX = self.x; self.lastY = self.y; if (!self.target || self.target.destroyed || self.destroyed) { self.destroyed = true; self.destroy(); return; } // Move towards target var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 1) dist = 1; var vx = dx / dist * self.speed; var vy = dy / dist * self.speed; self.x += vx; self.y += vy; // Rotate arrow to face target self.asset.rotation = Math.atan2(dy, dx); // Check for collision with target (simple distance check) if (dist < 60) { // Hit! if (typeof self.target.health === "number") { self.target.health -= self.damage; // If the target is a soldier and its health drops to 0 or below, destroy it and remove from array if (self.target.health <= 0 && typeof self.target.team === "string") { self.target.destroyed = true; self.target.destroy(); // Remove from correct array if (self.target.team === "enemy" && typeof enemySoldiers !== "undefined") { for (var i = 0; i < enemySoldiers.length; i++) { if (enemySoldiers[i] === self.target) { enemySoldiers.splice(i, 1); break; } } } else if (self.target.team === "player" && typeof playerSoldiers !== "undefined") { for (var i = 0; i < playerSoldiers.length; i++) { if (playerSoldiers[i] === self.target) { playerSoldiers.splice(i, 1); break; } } } } } self.destroyed = true; self.destroy(); } // Remove if out of bounds (double the distance for longer arrows) if (self.x < -600 || self.x > 3200 || self.y < -600 || self.y > 4000) { self.destroyed = true; self.destroy(); } }; return self; }); // Castle class for player and enemy castles var Castle = Container.expand(function () { var self = Container.call(this); // Properties self.team = null; self.health = CASTLE_HEALTH_INIT; self.hasUpgrade = false; self.attack = 0; self.arrowCooldown = 0; self.arrowCooldownMax = 60; // frames between shots self.attackRadius = 500; // px, for arrow shooting self.asset = null; // Initialize castle self.init = function (team, x, y) { self.team = team; self.x = x; self.y = y; self.health = CASTLE_HEALTH_INIT; self.hasUpgrade = false; self.attack = 0; self.arrowCooldown = 0; // Remove previous asset if any if (self.asset) { self.removeChild(self.asset); } // Add correct asset if (team === 'player') { self.asset = self.attachAsset('castle_player', { anchorX: 0.5, anchorY: 0.5 }); } else { self.asset = self.attachAsset('castle_enemy', { anchorX: 0.5, anchorY: 0.5 }); } }; // Upgrade castle self.upgrade = function () { self.hasUpgrade = true; self.health = 1500; self.attack = 1 + Math.floor(Math.random() * 3); // 1-3 self.arrowCooldown = 0; // Second upgrade handled externally (see gamecode.js) }; // Update castle (shoot arrows if upgraded) self.update = function () { if (self.hasUpgrade && self.health > 0) { // Only shoot at enemy soldiers in range var targets = []; if (self.team === 'player') { for (var i = 0; i < enemySoldiers.length; i++) { var e = enemySoldiers[i]; var dx = e.x - self.x; var dy = e.y - self.y; if (dx * dx + dy * dy <= self.attackRadius * self.attackRadius) { targets.push(e); } } } else { for (var i = 0; i < playerSoldiers.length; i++) { var e = playerSoldiers[i]; var dx = e.x - self.x; var dy = e.y - self.y; if (dx * dx + dy * dy <= self.attackRadius * self.attackRadius) { targets.push(e); } } } // Shoot at first target if cooldown is ready if (targets.length > 0 && self.arrowCooldown <= 0) { var target = targets[0]; var arrow = new Arrow(); arrow.init(self.x, self.y, target, self.attack, self.team); arrows.push(arrow); game.addChild(arrow); self.arrowCooldown = self.arrowCooldownMax; } if (self.arrowCooldown > 0) self.arrowCooldown--; } }; return self; }); // Soldier class for both player and enemy soldiers var Soldier = Container.expand(function () { var self = Container.call(this); self.team = null; self.health = 100; self.attack = 10; self.speed = SOLDIER_SPEED; self.inCombat = false; self.asset = null; self.lastX = 0; self.lastY = 0; // Initialize soldier self.init = function (team, health, attack) { self.team = team; self.health = health; self.attack = attack; self.inCombat = false; // Remove previous asset if any if (self.asset) { self.removeChild(self.asset); } // Add correct asset if (team === 'player') { self.asset = self.attachAsset('soldier_player', { anchorX: 0.5, anchorY: 0.5 }); } else { self.asset = self.attachAsset('soldier_enemy', { anchorX: 0.5, anchorY: 0.5 }); } }; // Update soldier position (move forward if not in combat) self.update = function () { self.lastX = self.x; self.lastY = self.y; if (!self.inCombat) { if (self.team === 'player') { self.x += self.speed; } else { self.x -= self.speed; } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xBFFF00 }); /**** * Game Code ****/ // --- Add background image --- // Ottoman-inspired church music with kanun, tambourine, zurna melodies var background = LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: GAME_W, height: GAME_H }); game.addChild(background); // Play Ottoman-inspired church music with kanun, tambourine, zurna melodies LK.playMusic('ottoman_church_bg'); // --- Game constants --- // Player castle (blue) // Enemy castle (red) // Player soldier (green) // Enemy soldier (orange) var GAME_W = 2048, GAME_H = 2732; var CASTLE_OFFSET_X = 120, CASTLE_OFFSET_Y = GAME_H - 520; // moved up by 200px var CASTLE_HEALTH_INIT = 1000; var GOLD_INIT = 200; var GOLD_PER_TICK = 5; // After enemy 800 gold upgrade, this will be increased for mine-like feature var GOLD_TICK_MS = 200; var SOLDIER_COST = 100; var SOLDIER_HEALTH_MIN = 80, SOLDIER_HEALTH_MAX = 160; var SOLDIER_ATTACK_MIN = 20, SOLDIER_ATTACK_MAX = 50; var SOLDIER_SPEED = 14 / 5 * 0.1 * 4.0; // px per frame (now 100% faster than previous) // --- Difficulty selection --- var difficulty = null; var ENEMY_SOLDIER_COST = 100; // default, will be set by difficulty // Show difficulty selection overlay var diffOverlay = new Container(); diffOverlay.zIndex = 10000; // ensure on top diffOverlay.width = GAME_W; diffOverlay.height = GAME_H; // Use a Text2 as a fake overlay background (solid block) since centerCircle is removed var overlayBg = new Text2(" ", { size: 10, fill: 0x000000 }); overlayBg.width = GAME_W; overlayBg.height = GAME_H; overlayBg.alpha = 0.7; overlayBg.x = 0; overlayBg.y = 0; diffOverlay.addChild(overlayBg); // Difficulty title with black background var diffTitleBg = new Text2(" ", { size: 10, fill: 0x000000 }); diffTitleBg.width = 900; diffTitleBg.height = 160; diffTitleBg.alpha = 0.95; diffTitleBg.anchor.set(0.5, 0.5); diffTitleBg.x = GAME_W / 2; diffTitleBg.y = GAME_H / 2 - 300; diffOverlay.addChild(diffTitleBg); var diffTitle = new Text2("Select Difficulty", { size: 120, fill: "#fff", font: "PressStart2P,Pixel,monospace" }); diffTitle.anchor.set(0.5, 0.5); diffTitle.x = GAME_W / 2; diffTitle.y = GAME_H / 2 - 300; diffOverlay.addChild(diffTitle); // Easy button with black background var btnEasyBg = new Text2(" ", { size: 10, fill: 0x000000 }); btnEasyBg.width = 600; btnEasyBg.height = 120; btnEasyBg.alpha = 0.95; btnEasyBg.anchor.set(0.5, 0.5); btnEasyBg.x = GAME_W / 2; btnEasyBg.y = GAME_H / 2 - 80; diffOverlay.addChild(btnEasyBg); var btnEasy = new Text2("Easy", { size: 100, fill: 0x00FF00, font: "PressStart2P,Pixel,monospace" }); btnEasy.anchor.set(0.5, 0.5); btnEasy.x = GAME_W / 2; btnEasy.y = GAME_H / 2 - 80; diffOverlay.addChild(btnEasy); // Normal button with black background var btnNormalBg = new Text2(" ", { size: 10, fill: 0x000000 }); btnNormalBg.width = 600; btnNormalBg.height = 120; btnNormalBg.alpha = 0.95; btnNormalBg.anchor.set(0.5, 0.5); btnNormalBg.x = GAME_W / 2; btnNormalBg.y = GAME_H / 2 + 80; diffOverlay.addChild(btnNormalBg); var btnNormal = new Text2("Normal", { size: 100, fill: 0xFFFF00, font: "PressStart2P,Pixel,monospace" }); btnNormal.anchor.set(0.5, 0.5); btnNormal.x = GAME_W / 2; btnNormal.y = GAME_H / 2 + 80; diffOverlay.addChild(btnNormal); // Hard button with black background var btnHardBg = new Text2(" ", { size: 10, fill: 0x000000 }); btnHardBg.width = 600; btnHardBg.height = 120; btnHardBg.alpha = 0.95; btnHardBg.anchor.set(0.5, 0.5); btnHardBg.x = GAME_W / 2; btnHardBg.y = GAME_H / 2 + 240; diffOverlay.addChild(btnHardBg); var btnHard = new Text2("Hard", { size: 100, fill: 0xFF0000, font: "PressStart2P,Pixel,monospace" }); btnHard.anchor.set(0.5, 0.5); btnHard.x = GAME_W / 2; btnHard.y = GAME_H / 2 + 240; diffOverlay.addChild(btnHard); game.addChild(diffOverlay); // Disable game input until difficulty is chosen var gameInputEnabled = false; // Helper to start game with selected difficulty function selectDifficulty(level) { difficulty = level; if (difficulty === "easy") { ENEMY_SOLDIER_COST = SOLDIER_COST; // same as player } else if (difficulty === "normal") { ENEMY_SOLDIER_COST = Math.floor(SOLDIER_COST * 0.9); // 0.9 of player } else if (difficulty === "hard") { ENEMY_SOLDIER_COST = Math.floor(SOLDIER_COST * 0.7); // 0.7 of player } gameInputEnabled = true; diffOverlay.destroy(); // Show solder button in the middle section (archerBtn will appear after upgrade) archerBtn.visible = false; solderBtn.visible = true; solderBtnLabel.visible = true; wallBtn.visible = true; wallBtnLabel.visible = true; } // Add touch/click handlers for buttons btnEasy.down = function (x, y, obj) { selectDifficulty("easy"); }; btnNormal.down = function (x, y, obj) { selectDifficulty("normal"); }; btnHard.down = function (x, y, obj) { selectDifficulty("hard"); }; // --- State variables --- // (removed, handled by castlePlayer.health and castleEnemy.health) var playerGold = GOLD_INIT; var enemyGold = GOLD_INIT; var playerSoldiers = []; var enemySoldiers = []; // --- Castles --- var castlePlayer = new Castle(); castlePlayer.init('player', CASTLE_OFFSET_X, CASTLE_OFFSET_Y); game.addChild(castlePlayer); var castleEnemy = new Castle(); castleEnemy.init('enemy', GAME_W - CASTLE_OFFSET_X, CASTLE_OFFSET_Y); game.addChild(castleEnemy); // --- Enemy castle health display above castle --- var castleEnemyHealthTxt = new Text2(castleEnemy.health + '', { size: 60, fill: '#fff', font: "PressStart2P,Pixel,monospace" }); castleEnemyHealthTxt.anchor.set(0.5, 1.2); castleEnemyHealthTxt.x = castleEnemy.x; castleEnemyHealthTxt.y = castleEnemy.y - 260; // above the castle game.addChild(castleEnemyHealthTxt); // --- Player castle unit cost labels above castle --- // --- Player castle upgrade cost label under player castle --- var playerCastleUpgradeTxt = new Text2("YOU NEED 350 GOLD\nTO UPGRADE.", { size: 54, fill: "#000", font: "PressStart2P,Pixel,monospace" }); playerCastleUpgradeTxt.anchor.set(0.5, -0.2); // anchor above the text baseline playerCastleUpgradeTxt.x = castlePlayer.x + 180; playerCastleUpgradeTxt.y = castlePlayer.y + 320; // under the castle game.addChild(playerCastleUpgradeTxt); // --- Player castle second upgrade cost label (800 gold, only after first upgrade) --- var playerCastleUpgrade2Txt = new Text2("YOU NEED 800 GOLD\nTO UPGRADE AGAIN.", { size: 54, fill: "#000", font: "PressStart2P,Pixel,monospace" }); playerCastleUpgrade2Txt.anchor.set(0.5, -0.2); playerCastleUpgrade2Txt.x = castlePlayer.x + 180; playerCastleUpgrade2Txt.y = castlePlayer.y + 320; playerCastleUpgrade2Txt.visible = false; game.addChild(playerCastleUpgrade2Txt); // --- Track second upgrade state for both castles --- castlePlayer.hasUpgrade2 = false; castleEnemy.hasUpgrade2 = false; // After 800 gold upgrade, mine-like feature is enabled for enemy // --- Left side: archer_button and solder_button side by side, visible on screen --- var leftButtonsY = GAME_H / 2 + 100; var leftButtonSpacing = 40; var leftButtonStartX = 120 + 75; // 120px margin + half button width (150/2) // Archer button (leftmost) var archerBtn = LK.getAsset('archer_button', { anchorX: 0.5, anchorY: 0.5, x: leftButtonStartX, y: leftButtonsY }); archerBtn.visible = false; game.addChild(archerBtn); // Add "150 G" label under archerBtn, but only show after castle is upgraded var archerBtnLabel = new Text2("150 G", { size: 48, fill: "#000", font: "PressStart2P,Pixel,monospace" }); archerBtnLabel.anchor.set(0.5, 0); archerBtnLabel.x = archerBtn.x; archerBtnLabel.y = archerBtn.y + 90; // 90px below center of button (button is 150px tall) archerBtnLabel.visible = false; game.addChild(archerBtnLabel); // Solder button (right of archer) var solderBtn = LK.getAsset('solder_button', { anchorX: 0.5, anchorY: 0.5, x: leftButtonStartX + 150 + leftButtonSpacing, // 150 is button width y: leftButtonsY }); solderBtn.visible = false; game.addChild(solderBtn); // Add "100 G" label under solderBtn var solderBtnLabel = new Text2("100 G", { size: 48, fill: "#000", font: "PressStart2P,Pixel,monospace" }); solderBtnLabel.anchor.set(0.5, 0); solderBtnLabel.x = solderBtn.x; solderBtnLabel.y = solderBtn.y + 90; // 90px below center of button (button is 150px tall) solderBtnLabel.visible = false; game.addChild(solderBtnLabel); // Wall button (right of solderBtn) var wallBtn = LK.getAsset('wall_button', { anchorX: 0.5, anchorY: 0.5, x: solderBtn.x + 150 + leftButtonSpacing, y: leftButtonsY }); wallBtn.visible = false; game.addChild(wallBtn); // Add "50 G" label under wallBtn var wallBtnLabel = new Text2("50 G", { size: 48, fill: "#000", font: "PressStart2P,Pixel,monospace" }); wallBtnLabel.anchor.set(0.5, 0); wallBtnLabel.x = wallBtn.x; wallBtnLabel.y = wallBtn.y + 90; wallBtnLabel.visible = false; game.addChild(wallBtnLabel); // --- Walls array to track all wall_player objects --- var wallPlayers = []; // --- Walls array to track all wall_enemy objects --- var wallEnemies = []; // Deduct 50 gold from player when wallBtn is pressed wallBtn.down = function (x, y, obj) { if (!gameInputEnabled) return; if (playerGold >= 50) { playerGold -= 50; updateGui(); // Place wall_player in the middle of the two castles var wallX = (castlePlayer.x + castleEnemy.x) / 2; var wallY = CASTLE_OFFSET_Y + 120; // align with soldiers' path var wallPlayer = LK.getAsset('wall_player', { anchorX: 0.5, anchorY: 0.5, x: wallX, y: wallY }); wallPlayer.health = 40; wallPlayer.maxHealth = 40; wallPlayer.destroyed = false; game.addChild(wallPlayer); wallPlayers.push(wallPlayer); } }; // --- Enemy AI: Deploy wall_enemy if enough gold and no wall exists --- // Track total wall_enemy deployed since game start for enemy 2nd upgrade var enemyWallEnemyDeployCount = 0; function deployWallEnemy() { // Only one wall_enemy at a time if (wallEnemies.length > 0) return false; if (enemyGold < 50) return false; enemyGold -= 50; updateGui(); var wallX = (castlePlayer.x + castleEnemy.x) / 2; var wallY = CASTLE_OFFSET_Y + 120; var wallEnemy = LK.getAsset('wall_enemy', { anchorX: 0.5, anchorY: 0.5, x: wallX, y: wallY }); wallEnemy.health = 40; wallEnemy.maxHealth = 40; wallEnemy.destroyed = false; game.addChild(wallEnemy); wallEnemies.push(wallEnemy); // Increment wall_enemy deploy count for enemy enemyWallEnemyDeployCount++; // If enemy has first upgrade, not yet second, has 800 gold, and deployed 15 wall_enemy, do 2nd upgrade automatically if (castleEnemy.hasUpgrade && !castleEnemy.hasUpgrade2 && enemyGold >= 800 && enemyWallEnemyDeployCount >= 15) { enemyGold -= 800; castleEnemy.hasUpgrade2 = true; // Apply second upgrade effects (same as player) castleEnemy.health = 2500; castleEnemy.attack = 4 + Math.floor(Math.random() * 3); // 4-6 castleEnemy.arrowCooldownMax = 40; // faster arrows // --- MINE-LIKE FEATURE: Increase enemy gold income after 2nd upgrade --- GOLD_PER_TICK = GOLD_PER_TICK + 5; // Increase gold income for both, or you can use a separate enemyGoldPerTick if needed updateGui(); } return true; } // --- GUI: Health and Gold displays --- var playerHealthTxt = new Text2('1000', { size: 70, fill: '#fff', font: "PressStart2P,Pixel,monospace" }); playerHealthTxt.anchor.set(0, 0); LK.gui.top.addChild(playerHealthTxt); // Player LIFE label var playerLifeLabel = new Text2('LIFE', { size: 38, fill: "#000", font: "PressStart2P,Pixel,monospace" }); playerLifeLabel.anchor.set(0, 0); LK.gui.top.addChild(playerLifeLabel); var playerGoldTxt = new Text2('200', { size: 60, fill: '#ffe600', font: "PressStart2P,Pixel,monospace" }); playerGoldTxt.anchor.set(0, 0); LK.gui.top.addChild(playerGoldTxt); // Add soldier/cavalry cost labels to the left of the tan score // Removed soldier/cavalry cost labels from GUI // Player GOLD label var playerGoldLabel = new Text2('GOLD', { size: 38, fill: "#000", font: "PressStart2P,Pixel,monospace" }); playerGoldLabel.anchor.set(0, 0); LK.gui.top.addChild(playerGoldLabel); var enemyHealthTxt = new Text2('1000', { size: 70, fill: '#fff', font: "PressStart2P,Pixel,monospace" }); enemyHealthTxt.anchor.set(1, 0); LK.gui.top.addChild(enemyHealthTxt); // Enemy LIFE label var enemyLifeLabel = new Text2('LIFE', { size: 38, fill: "#000", font: "PressStart2P,Pixel,monospace" }); enemyLifeLabel.anchor.set(1, 0); LK.gui.top.addChild(enemyLifeLabel); var enemyGoldTxt = new Text2('200', { size: 60, fill: '#ffe600', font: "PressStart2P,Pixel,monospace" }); enemyGoldTxt.anchor.set(1, 0); LK.gui.top.addChild(enemyGoldTxt); // Enemy GOLD label var enemyGoldLabel = new Text2('GOLD', { size: 38, fill: "#000", font: "PressStart2P,Pixel,monospace" }); enemyGoldLabel.anchor.set(1, 0); LK.gui.top.addChild(enemyGoldLabel); // --- Win Counter (Rounds Won) --- // Use storage plugin for persistence var roundsWon = storage.roundsWon || 0; var roundsWonTxt = new Text2('Rounds Won: ' + roundsWon, { size: 60, fill: '#00ff00', font: "PressStart2P,Pixel,monospace" }); roundsWonTxt.anchor.set(1, 0); LK.gui.top.addChild(roundsWonTxt); roundsWonTxt.x = LK.gui.width - 40; roundsWonTxt.y = 220; // Position GUI elements playerHealthTxt.x = 120; playerHealthTxt.y = 40; playerLifeLabel.x = playerHealthTxt.x + playerHealthTxt.width + 18; playerLifeLabel.y = playerHealthTxt.y + 10; playerGoldTxt.x = 120; playerGoldTxt.y = 120; playerGoldLabel.x = playerGoldTxt.x + playerGoldTxt.width + 18; playerGoldLabel.y = playerGoldTxt.y + 8; // (Removed: Position soldier/cavalry cost labels to the left of the tan score) enemyHealthTxt.x = LK.gui.width - 120; enemyHealthTxt.y = 40; enemyLifeLabel.x = enemyHealthTxt.x - enemyHealthTxt.width - 18; enemyLifeLabel.y = enemyHealthTxt.y + 10; enemyGoldTxt.x = LK.gui.width - 120; enemyGoldTxt.y = 120; enemyGoldLabel.x = enemyGoldTxt.x - enemyGoldTxt.width - 18; enemyGoldLabel.y = enemyGoldTxt.y + 8; // --- Gold income timer --- var goldTimer = LK.setInterval(function () { // Only increase gold if difficulty has been chosen if (!gameInputEnabled) return; playerGold += GOLD_PER_TICK; enemyGold += GOLD_PER_TICK; // If enemy has mine upgrade, GOLD_PER_TICK is increased for both updateGui(); }, GOLD_TICK_MS); // --- GUI update function --- function updateGui() { playerHealthTxt.setText(castlePlayer.health); playerLifeLabel.x = playerHealthTxt.x + playerHealthTxt.width + 18; playerLifeLabel.y = playerHealthTxt.y + 10; playerGoldTxt.setText(playerGold); playerGoldLabel.x = playerGoldTxt.x + playerGoldTxt.width + 18; playerGoldLabel.y = playerGoldTxt.y + 8; enemyHealthTxt.setText(castleEnemy.health); enemyLifeLabel.x = enemyHealthTxt.x - enemyHealthTxt.width - 18; enemyLifeLabel.y = enemyHealthTxt.y + 10; enemyGoldTxt.setText(enemyGold); enemyGoldLabel.x = enemyGoldTxt.x - enemyGoldTxt.width - 18; enemyGoldLabel.y = enemyGoldTxt.y + 8; // Update enemy castle health text and position castleEnemyHealthTxt.setText(castleEnemy.health); castleEnemyHealthTxt.x = castleEnemy.x; castleEnemyHealthTxt.y = castleEnemy.y - 260; } // --- Deploy soldier function --- function deploySoldier(team) { // Block deployment if difficulty not chosen if (!gameInputEnabled) return false; var gold = team === 'player' ? playerGold : enemyGold; var cost = team === 'player' ? SOLDIER_COST : ENEMY_SOLDIER_COST; if (gold < cost) return false; // Deduct gold if (team === 'player') playerGold -= SOLDIER_COST;else enemyGold -= ENEMY_SOLDIER_COST; // Fixed health and random attack var health = 25; var attack = 5 + Math.floor(Math.random() * 6); // 5-10 inclusive // Create soldier var s = new Soldier(); s.init(team, health, attack); s.speed = SOLDIER_SPEED; s.inCombat = false; // Position s.y = CASTLE_OFFSET_Y + 120; // Move soldiers' exit position down by 120px if (team === 'player') { s.x = castlePlayer.x + 120; playerSoldiers.push(s); } else { s.x = castleEnemy.x - 120; enemySoldiers.push(s); } game.addChild(s); // Play sound when unit leaves the castle LK.getSound('unit_leave_castle').play(); return true; } // --- Player tap to upgrade castle only (soldier deploy moved to solderBtn.down) --- game.down = function (x, y, obj) { // Block input until difficulty is chosen if (!gameInputEnabled) return; // Check if player clicked on their castle for upgrade var dx = x - castlePlayer.x; var dy = y - castlePlayer.y; if (dx * dx + dy * dy < 250 * 250 && !castlePlayer.hasUpgrade && playerGold >= 350) { playerGold -= 350; castlePlayer.upgrade(); updateGui(); // Show archerBtn after upgrade archerBtn.visible = true; // Show '150 G' label under archerBtn after upgrade archerBtnLabel.visible = true; // Hide the first upgrade text, show the second upgrade text if (playerCastleUpgradeTxt && typeof playerCastleUpgradeTxt.destroy === "function") { playerCastleUpgradeTxt.visible = false; } playerCastleUpgrade2Txt.visible = true; return; } // Second upgrade: only if first upgrade is done, not already done, and enough gold if (dx * dx + dy * dy < 250 * 250 && castlePlayer.hasUpgrade && !castlePlayer.hasUpgrade2 && playerGold >= 800) { playerGold -= 800; castlePlayer.hasUpgrade2 = true; // Apply second upgrade effects castlePlayer.health = 2500; castlePlayer.attack = 4 + Math.floor(Math.random() * 3); // 4-6 castlePlayer.arrowCooldownMax = 40; // faster arrows updateGui(); // Hide the second upgrade text playerCastleUpgrade2Txt.visible = false; // Optionally, show a message or effect for max upgrade return; } // (Removed: deploySoldier on screen tap) }; // --- Deploy soldier when solderBtn is pressed --- solderBtn.down = function (x, y, obj) { if (!gameInputEnabled) return; if (playerGold >= SOLDIER_COST) { deploySoldier('player'); updateGui(); } }; // --- Deploy archer_player when archerBtn is pressed (uses archer_player asset, cost 10 gold) --- archerBtn.down = function (x, y, obj) { if (!gameInputEnabled) return; if (playerGold >= 150) { playerGold -= 150; // Create a new Soldier but use archer_player asset var s = new Soldier(); // Set archer_player health to be twice that of soldier_player var baseSoldierHealth = 25; s.init('player', baseSoldierHealth * 2, 5 + Math.floor(Math.random() * 6)); // health 50, attack 5-10 s.speed = SOLDIER_SPEED * 0.8; // Already uses updated SOLDIER_SPEED, now 75% faster s.inCombat = false; // Remove previous asset and attach archer_player asset if (s.asset) { s.removeChild(s.asset); } s.asset = s.attachAsset('archer_player', { anchorX: 0.5, anchorY: 0.5 }); // Position s.y = CASTLE_OFFSET_Y + 120; s.x = castlePlayer.x + 120; playerSoldiers.push(s); game.addChild(s); // Play sound when archer leaves the castle LK.getSound('unit_leave_castle').play(); updateGui(); } }; // --- Enemy AI: deploy soldier automatically if enough gold --- // Track enemy soldier deployments for 2:1 soldier:archer ratio var enemySoldierDeployCount = 0; var enemyNextUnit = 'soldier'; // always start with soldier var enemyTotalSoldierDeployed = 0; var enemyUpgradeGoldReserved = 200; // Track total enemy soldier_enemys since last wall_enemy var enemySoldierSinceLastWall = 0; // --- Track if enemy castle has done second upgrade --- castleEnemy.hasUpgrade2 = false; var enemyDeployTimer = LK.setInterval(function () { // Block AI until difficulty is chosen if (!gameInputEnabled) return; // If not upgraded, reserve 200 gold for upgrade after 10 soldiers if (!castleEnemy.hasUpgrade) { // In hard mode, upgrade for free after 10 soldiers if (difficulty === "hard") { if (enemyTotalSoldierDeployed >= 10) { castleEnemy.upgrade(); updateGui(); return; } } else { // Only upgrade after 10 soldier_enemy deployed and enough gold (350 - 200 = 150 must be available for upgrade) if (enemyTotalSoldierDeployed >= 10 && enemyGold >= 350 - enemyUpgradeGoldReserved) { enemyGold -= 350 - enemyUpgradeGoldReserved; castleEnemy.upgrade(); updateGui(); return; } } } // If not upgraded and haven't deployed 10 soldiers, only deploy if enough gold and after reserving 200 for upgrade if (!castleEnemy.hasUpgrade && enemyTotalSoldierDeployed < 10) { if (enemyNextUnit === 'soldier' && enemyGold >= ENEMY_SOLDIER_COST + enemyUpgradeGoldReserved) { if (deploySoldier('enemy')) { updateGui(); enemySoldierDeployCount++; enemyTotalSoldierDeployed++; enemySoldierSinceLastWall++; // Track for wall_enemy spawn // For every 5 soldier_enemys, spawn a wall_enemy if (enemySoldierSinceLastWall >= 5) { if (enemyGold >= 50 && wallEnemies.length === 0) { deployWallEnemy(); } enemySoldierSinceLastWall = 0; } // After 2 soldiers, next must be archer (if possible) if (enemySoldierDeployCount >= 2) { enemyNextUnit = 'soldier'; // No archers before upgrade enemySoldierDeployCount = 0; } } } // Do not deploy archers before upgrade return; } // --- Enemy castle second upgrade logic (AI) --- // If enemy castle has first upgrade, not yet second, and enough gold, do second upgrade if (castleEnemy.hasUpgrade && !castleEnemy.hasUpgrade2 && enemyGold >= 800) { enemyGold -= 800; castleEnemy.hasUpgrade2 = true; // Apply second upgrade effects (same as player) castleEnemy.health = 2500; castleEnemy.attack = 4 + Math.floor(Math.random() * 3); // 4-6 castleEnemy.arrowCooldownMax = 40; // faster arrows // --- MINE-LIKE FEATURE: Increase enemy gold income after 2nd upgrade --- GOLD_PER_TICK = GOLD_PER_TICK + 5; // Increase gold income for both, or you can use a separate enemyGoldPerTick if needed updateGui(); } // After upgrade, normal logic: after every 2 soldiers, deploy 1 archer if (enemyNextUnit === 'soldier' && enemyGold >= ENEMY_SOLDIER_COST) { if (deploySoldier('enemy')) { updateGui(); enemySoldierDeployCount++; enemyTotalSoldierDeployed++; enemySoldierSinceLastWall++; // Track for wall_enemy spawn // For every 5 soldier_enemys, spawn a wall_enemy if (enemySoldierSinceLastWall >= 5) { if (enemyGold >= 50 && wallEnemies.length === 0) { deployWallEnemy(); } enemySoldierSinceLastWall = 0; } // After 2 soldiers, next must be archer (if possible) if (enemySoldierDeployCount >= 2 && castleEnemy.hasUpgrade) { enemyNextUnit = 'archer'; } else { enemyNextUnit = 'soldier'; } } // Try to deploy wall_enemy if enough gold and no wall exists (after soldier deploy) if (castleEnemy.hasUpgrade && enemyGold >= 50 && wallEnemies.length === 0) { deployWallEnemy(); } } else if (enemyNextUnit === 'archer' && castleEnemy.hasUpgrade && enemyGold >= 150) { // Deploy archer_enemy enemyGold -= 150; var s = new Soldier(); var baseSoldierHealth = 25; s.init('enemy', baseSoldierHealth * 2, 5 + Math.floor(Math.random() * 6)); // health 50, attack 5-10 s.speed = SOLDIER_SPEED * 0.8; // Already uses updated SOLDIER_SPEED, now 75% faster s.inCombat = false; if (s.asset) { s.removeChild(s.asset); } s.asset = s.attachAsset('archer_enemy', { anchorX: 0.5, anchorY: 0.5 }); s.y = CASTLE_OFFSET_Y + 120; s.x = castleEnemy.x - 120; enemySoldiers.push(s); game.addChild(s); // Play sound when archer_enemy leaves the castle LK.getSound('unit_leave_castle').play(); updateGui(); // Reset soldier deploy count and go back to soldier enemySoldierDeployCount = 0; enemyNextUnit = 'soldier'; // Try to deploy wall_enemy if enough gold and no wall exists (after archer deploy) if (castleEnemy.hasUpgrade && enemyGold >= 50 && wallEnemies.length === 0) { deployWallEnemy(); } } }, 600); // --- Arrows array --- var arrows = []; // --- Main update loop --- game.update = function () { // --- Update castles (health, arrows) --- castlePlayer.update(); castleEnemy.update(); // --- Update all soldiers --- // 1. Move soldiers forward if not in combat // 2. Check for combat with opposing soldiers // 3. If at enemy castle, attack castle // --- Player soldiers --- for (var i = playerSoldiers.length - 1; i >= 0; i--) { var s = playerSoldiers[i]; s.inCombat = false; // --- Check for wall_enemy collision --- var wallEngaged = false; for (var w = 0; w < wallEnemies.length; w++) { var wall = wallEnemies[w]; if (wall.destroyed) continue; // Simple collision: check if close enough horizontally and vertically if (Math.abs(s.x - wall.x) < 80 && Math.abs(s.y - wall.y) < 180) { s.inCombat = true; wallEngaged = true; // Player attacks wall every 12 frames if (LK.ticks % 12 === 0) { wall.health -= s.attack; } // If wall destroyed, remove from game and array if (wall.health <= 0) { wall.destroyed = true; if (typeof wall.destroy === "function") wall.destroy(); wallEnemies.splice(w, 1); w--; } break; // Only engage one wall at a time } } if (wallEngaged) continue; // Check for enemy soldier in range var engaged = false; for (var j = 0; j < enemySoldiers.length; j++) { var e = enemySoldiers[j]; // If close enough (overlap) if (Math.abs(s.x - e.x) < 80) { // Engage in combat s.inCombat = true; e.inCombat = true; // Both attack each other if (LK.ticks % 12 === 0) { // Attack every 12 frames (~5 times/sec) e.health -= s.attack; s.health -= e.attack; } // Remove dead soldiers if (e.health <= 0) { e.destroy(); enemySoldiers.splice(j, 1); j--; // Award 10 gold to player for killing enemy soldier playerGold += 10; updateGui(); } if (s.health <= 0) { s.destroy(); playerSoldiers.splice(i, 1); i--; // Award 10 gold to enemy for killing player soldier enemyGold += 10; updateGui(); engaged = true; break; } engaged = true; break; } } if (engaged) continue; // If not in combat, check if at enemy castle if (Math.abs(s.x - castleEnemy.x) < 120) { s.inCombat = true; if (LK.ticks % 12 === 0) { castleEnemy.health -= s.attack; if (castleEnemy.health < 0) castleEnemy.health = 0; updateGui(); } // Remove soldier if castle destroyed if (castleEnemy.health <= 0) { s.destroy(); playerSoldiers.splice(i, 1); i--; } } } // --- Enemy soldiers --- for (var i = enemySoldiers.length - 1; i >= 0; i--) { var s = enemySoldiers[i]; s.inCombat = false; // --- Check for wall_player collision --- var wallEngaged = false; for (var w = 0; w < wallPlayers.length; w++) { var wall = wallPlayers[w]; if (wall.destroyed) continue; // Simple collision: check if close enough horizontally and vertically if (Math.abs(s.x - wall.x) < 80 && Math.abs(s.y - wall.y) < 180) { s.inCombat = true; wallEngaged = true; // Enemy attacks wall every 12 frames if (LK.ticks % 12 === 0) { wall.health -= s.attack; } // If wall destroyed, remove from game and array if (wall.health <= 0) { wall.destroyed = true; if (typeof wall.destroy === "function") wall.destroy(); wallPlayers.splice(w, 1); w--; } break; // Only engage one wall at a time } } if (wallEngaged) continue; // Check for player soldier in range var engaged = false; for (var j = 0; j < playerSoldiers.length; j++) { var e = playerSoldiers[j]; if (Math.abs(s.x - e.x) < 80) { s.inCombat = true; e.inCombat = true; if (LK.ticks % 12 === 0) { e.health -= s.attack; s.health -= e.attack; } if (e.health <= 0) { e.destroy(); playerSoldiers.splice(j, 1); j--; // Award 10 gold to enemy for killing player soldier enemyGold += 10; updateGui(); } if (s.health <= 0) { s.destroy(); enemySoldiers.splice(i, 1); i--; // Award 10 gold to player for killing enemy soldier playerGold += 10; updateGui(); engaged = true; break; } engaged = true; break; } } if (engaged) continue; // If not in combat, check if at player castle if (Math.abs(s.x - castlePlayer.x) < 120) { s.inCombat = true; if (LK.ticks % 12 === 0) { castlePlayer.health -= s.attack; if (castlePlayer.health < 0) castlePlayer.health = 0; updateGui(); } if (castlePlayer.health <= 0) { s.destroy(); enemySoldiers.splice(i, 1); i--; } } } // --- Move soldiers --- for (var i = 0; i < playerSoldiers.length; i++) { playerSoldiers[i].update(); } for (var i = 0; i < enemySoldiers.length; i++) { enemySoldiers[i].update(); } // --- Update arrows --- for (var i = arrows.length - 1; i >= 0; i--) { var a = arrows[i]; a.update(); if (a.destroyed) { arrows.splice(i, 1); } } // --- Check win/lose --- if (castlePlayer.health <= 0) { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); return; } if (castleEnemy.health <= 0) { // Increase and persist rounds won roundsWon = (storage.roundsWon || 0) + 1; storage.roundsWon = roundsWon; roundsWonTxt.setText('Rounds Won: ' + roundsWon); LK.effects.flashScreen(0x00ff00, 1000); LK.showYouWin(); return; } }; // --- Clean up timers on game over --- game.destroy = function () { LK.clearInterval(goldTimer); LK.clearInterval(enemyDeployTimer); };
/****
* Plugins
****/
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Arrow class for castle arrow shooting
var Arrow = Container.expand(function () {
var self = Container.call(this);
// Increase arrow speed for longer distance
self.speed = 40; // px per frame (was 32)
self.damage = 2 + Math.floor(Math.random() * 3); // 2-4 damage
self.target = null;
self.team = null;
self.destroyed = false;
self.asset = null;
self.lastX = 0;
self.lastY = 0;
// Initialize arrow
self.init = function (x, y, target, damage, team) {
self.x = x;
self.y = y;
self.target = target;
// Arrow damage is randomized 2-4, ignore passed damage param for castle arrows
self.damage = 2 + Math.floor(Math.random() * 3);
self.team = team;
// Remove previous asset if any
if (self.asset) {
self.removeChild(self.asset);
}
// Use a visible arrow using a rectangle shape since centerCircle is removed
self.asset = self.attachAsset('arrow_shape', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 0.3,
tint: team === 'player' ? 0x00aaff : 0xffaa00 // more visible blue/orange
});
};
// Update arrow position and check for hit
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
if (!self.target || self.target.destroyed || self.destroyed) {
self.destroyed = true;
self.destroy();
return;
}
// Move towards target
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 1) dist = 1;
var vx = dx / dist * self.speed;
var vy = dy / dist * self.speed;
self.x += vx;
self.y += vy;
// Rotate arrow to face target
self.asset.rotation = Math.atan2(dy, dx);
// Check for collision with target (simple distance check)
if (dist < 60) {
// Hit!
if (typeof self.target.health === "number") {
self.target.health -= self.damage;
// If the target is a soldier and its health drops to 0 or below, destroy it and remove from array
if (self.target.health <= 0 && typeof self.target.team === "string") {
self.target.destroyed = true;
self.target.destroy();
// Remove from correct array
if (self.target.team === "enemy" && typeof enemySoldiers !== "undefined") {
for (var i = 0; i < enemySoldiers.length; i++) {
if (enemySoldiers[i] === self.target) {
enemySoldiers.splice(i, 1);
break;
}
}
} else if (self.target.team === "player" && typeof playerSoldiers !== "undefined") {
for (var i = 0; i < playerSoldiers.length; i++) {
if (playerSoldiers[i] === self.target) {
playerSoldiers.splice(i, 1);
break;
}
}
}
}
}
self.destroyed = true;
self.destroy();
}
// Remove if out of bounds (double the distance for longer arrows)
if (self.x < -600 || self.x > 3200 || self.y < -600 || self.y > 4000) {
self.destroyed = true;
self.destroy();
}
};
return self;
});
// Castle class for player and enemy castles
var Castle = Container.expand(function () {
var self = Container.call(this);
// Properties
self.team = null;
self.health = CASTLE_HEALTH_INIT;
self.hasUpgrade = false;
self.attack = 0;
self.arrowCooldown = 0;
self.arrowCooldownMax = 60; // frames between shots
self.attackRadius = 500; // px, for arrow shooting
self.asset = null;
// Initialize castle
self.init = function (team, x, y) {
self.team = team;
self.x = x;
self.y = y;
self.health = CASTLE_HEALTH_INIT;
self.hasUpgrade = false;
self.attack = 0;
self.arrowCooldown = 0;
// Remove previous asset if any
if (self.asset) {
self.removeChild(self.asset);
}
// Add correct asset
if (team === 'player') {
self.asset = self.attachAsset('castle_player', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
self.asset = self.attachAsset('castle_enemy', {
anchorX: 0.5,
anchorY: 0.5
});
}
};
// Upgrade castle
self.upgrade = function () {
self.hasUpgrade = true;
self.health = 1500;
self.attack = 1 + Math.floor(Math.random() * 3); // 1-3
self.arrowCooldown = 0;
// Second upgrade handled externally (see gamecode.js)
};
// Update castle (shoot arrows if upgraded)
self.update = function () {
if (self.hasUpgrade && self.health > 0) {
// Only shoot at enemy soldiers in range
var targets = [];
if (self.team === 'player') {
for (var i = 0; i < enemySoldiers.length; i++) {
var e = enemySoldiers[i];
var dx = e.x - self.x;
var dy = e.y - self.y;
if (dx * dx + dy * dy <= self.attackRadius * self.attackRadius) {
targets.push(e);
}
}
} else {
for (var i = 0; i < playerSoldiers.length; i++) {
var e = playerSoldiers[i];
var dx = e.x - self.x;
var dy = e.y - self.y;
if (dx * dx + dy * dy <= self.attackRadius * self.attackRadius) {
targets.push(e);
}
}
}
// Shoot at first target if cooldown is ready
if (targets.length > 0 && self.arrowCooldown <= 0) {
var target = targets[0];
var arrow = new Arrow();
arrow.init(self.x, self.y, target, self.attack, self.team);
arrows.push(arrow);
game.addChild(arrow);
self.arrowCooldown = self.arrowCooldownMax;
}
if (self.arrowCooldown > 0) self.arrowCooldown--;
}
};
return self;
});
// Soldier class for both player and enemy soldiers
var Soldier = Container.expand(function () {
var self = Container.call(this);
self.team = null;
self.health = 100;
self.attack = 10;
self.speed = SOLDIER_SPEED;
self.inCombat = false;
self.asset = null;
self.lastX = 0;
self.lastY = 0;
// Initialize soldier
self.init = function (team, health, attack) {
self.team = team;
self.health = health;
self.attack = attack;
self.inCombat = false;
// Remove previous asset if any
if (self.asset) {
self.removeChild(self.asset);
}
// Add correct asset
if (team === 'player') {
self.asset = self.attachAsset('soldier_player', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
self.asset = self.attachAsset('soldier_enemy', {
anchorX: 0.5,
anchorY: 0.5
});
}
};
// Update soldier position (move forward if not in combat)
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
if (!self.inCombat) {
if (self.team === 'player') {
self.x += self.speed;
} else {
self.x -= self.speed;
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xBFFF00
});
/****
* Game Code
****/
// --- Add background image ---
// Ottoman-inspired church music with kanun, tambourine, zurna melodies
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: GAME_W,
height: GAME_H
});
game.addChild(background);
// Play Ottoman-inspired church music with kanun, tambourine, zurna melodies
LK.playMusic('ottoman_church_bg');
// --- Game constants ---
// Player castle (blue)
// Enemy castle (red)
// Player soldier (green)
// Enemy soldier (orange)
var GAME_W = 2048,
GAME_H = 2732;
var CASTLE_OFFSET_X = 120,
CASTLE_OFFSET_Y = GAME_H - 520; // moved up by 200px
var CASTLE_HEALTH_INIT = 1000;
var GOLD_INIT = 200;
var GOLD_PER_TICK = 5;
// After enemy 800 gold upgrade, this will be increased for mine-like feature
var GOLD_TICK_MS = 200;
var SOLDIER_COST = 100;
var SOLDIER_HEALTH_MIN = 80,
SOLDIER_HEALTH_MAX = 160;
var SOLDIER_ATTACK_MIN = 20,
SOLDIER_ATTACK_MAX = 50;
var SOLDIER_SPEED = 14 / 5 * 0.1 * 4.0; // px per frame (now 100% faster than previous)
// --- Difficulty selection ---
var difficulty = null;
var ENEMY_SOLDIER_COST = 100; // default, will be set by difficulty
// Show difficulty selection overlay
var diffOverlay = new Container();
diffOverlay.zIndex = 10000; // ensure on top
diffOverlay.width = GAME_W;
diffOverlay.height = GAME_H;
// Use a Text2 as a fake overlay background (solid block) since centerCircle is removed
var overlayBg = new Text2(" ", {
size: 10,
fill: 0x000000
});
overlayBg.width = GAME_W;
overlayBg.height = GAME_H;
overlayBg.alpha = 0.7;
overlayBg.x = 0;
overlayBg.y = 0;
diffOverlay.addChild(overlayBg);
// Difficulty title with black background
var diffTitleBg = new Text2(" ", {
size: 10,
fill: 0x000000
});
diffTitleBg.width = 900;
diffTitleBg.height = 160;
diffTitleBg.alpha = 0.95;
diffTitleBg.anchor.set(0.5, 0.5);
diffTitleBg.x = GAME_W / 2;
diffTitleBg.y = GAME_H / 2 - 300;
diffOverlay.addChild(diffTitleBg);
var diffTitle = new Text2("Select Difficulty", {
size: 120,
fill: "#fff",
font: "PressStart2P,Pixel,monospace"
});
diffTitle.anchor.set(0.5, 0.5);
diffTitle.x = GAME_W / 2;
diffTitle.y = GAME_H / 2 - 300;
diffOverlay.addChild(diffTitle);
// Easy button with black background
var btnEasyBg = new Text2(" ", {
size: 10,
fill: 0x000000
});
btnEasyBg.width = 600;
btnEasyBg.height = 120;
btnEasyBg.alpha = 0.95;
btnEasyBg.anchor.set(0.5, 0.5);
btnEasyBg.x = GAME_W / 2;
btnEasyBg.y = GAME_H / 2 - 80;
diffOverlay.addChild(btnEasyBg);
var btnEasy = new Text2("Easy", {
size: 100,
fill: 0x00FF00,
font: "PressStart2P,Pixel,monospace"
});
btnEasy.anchor.set(0.5, 0.5);
btnEasy.x = GAME_W / 2;
btnEasy.y = GAME_H / 2 - 80;
diffOverlay.addChild(btnEasy);
// Normal button with black background
var btnNormalBg = new Text2(" ", {
size: 10,
fill: 0x000000
});
btnNormalBg.width = 600;
btnNormalBg.height = 120;
btnNormalBg.alpha = 0.95;
btnNormalBg.anchor.set(0.5, 0.5);
btnNormalBg.x = GAME_W / 2;
btnNormalBg.y = GAME_H / 2 + 80;
diffOverlay.addChild(btnNormalBg);
var btnNormal = new Text2("Normal", {
size: 100,
fill: 0xFFFF00,
font: "PressStart2P,Pixel,monospace"
});
btnNormal.anchor.set(0.5, 0.5);
btnNormal.x = GAME_W / 2;
btnNormal.y = GAME_H / 2 + 80;
diffOverlay.addChild(btnNormal);
// Hard button with black background
var btnHardBg = new Text2(" ", {
size: 10,
fill: 0x000000
});
btnHardBg.width = 600;
btnHardBg.height = 120;
btnHardBg.alpha = 0.95;
btnHardBg.anchor.set(0.5, 0.5);
btnHardBg.x = GAME_W / 2;
btnHardBg.y = GAME_H / 2 + 240;
diffOverlay.addChild(btnHardBg);
var btnHard = new Text2("Hard", {
size: 100,
fill: 0xFF0000,
font: "PressStart2P,Pixel,monospace"
});
btnHard.anchor.set(0.5, 0.5);
btnHard.x = GAME_W / 2;
btnHard.y = GAME_H / 2 + 240;
diffOverlay.addChild(btnHard);
game.addChild(diffOverlay);
// Disable game input until difficulty is chosen
var gameInputEnabled = false;
// Helper to start game with selected difficulty
function selectDifficulty(level) {
difficulty = level;
if (difficulty === "easy") {
ENEMY_SOLDIER_COST = SOLDIER_COST; // same as player
} else if (difficulty === "normal") {
ENEMY_SOLDIER_COST = Math.floor(SOLDIER_COST * 0.9); // 0.9 of player
} else if (difficulty === "hard") {
ENEMY_SOLDIER_COST = Math.floor(SOLDIER_COST * 0.7); // 0.7 of player
}
gameInputEnabled = true;
diffOverlay.destroy();
// Show solder button in the middle section (archerBtn will appear after upgrade)
archerBtn.visible = false;
solderBtn.visible = true;
solderBtnLabel.visible = true;
wallBtn.visible = true;
wallBtnLabel.visible = true;
}
// Add touch/click handlers for buttons
btnEasy.down = function (x, y, obj) {
selectDifficulty("easy");
};
btnNormal.down = function (x, y, obj) {
selectDifficulty("normal");
};
btnHard.down = function (x, y, obj) {
selectDifficulty("hard");
};
// --- State variables ---
// (removed, handled by castlePlayer.health and castleEnemy.health)
var playerGold = GOLD_INIT;
var enemyGold = GOLD_INIT;
var playerSoldiers = [];
var enemySoldiers = [];
// --- Castles ---
var castlePlayer = new Castle();
castlePlayer.init('player', CASTLE_OFFSET_X, CASTLE_OFFSET_Y);
game.addChild(castlePlayer);
var castleEnemy = new Castle();
castleEnemy.init('enemy', GAME_W - CASTLE_OFFSET_X, CASTLE_OFFSET_Y);
game.addChild(castleEnemy);
// --- Enemy castle health display above castle ---
var castleEnemyHealthTxt = new Text2(castleEnemy.health + '', {
size: 60,
fill: '#fff',
font: "PressStart2P,Pixel,monospace"
});
castleEnemyHealthTxt.anchor.set(0.5, 1.2);
castleEnemyHealthTxt.x = castleEnemy.x;
castleEnemyHealthTxt.y = castleEnemy.y - 260; // above the castle
game.addChild(castleEnemyHealthTxt);
// --- Player castle unit cost labels above castle ---
// --- Player castle upgrade cost label under player castle ---
var playerCastleUpgradeTxt = new Text2("YOU NEED 350 GOLD\nTO UPGRADE.", {
size: 54,
fill: "#000",
font: "PressStart2P,Pixel,monospace"
});
playerCastleUpgradeTxt.anchor.set(0.5, -0.2); // anchor above the text baseline
playerCastleUpgradeTxt.x = castlePlayer.x + 180;
playerCastleUpgradeTxt.y = castlePlayer.y + 320; // under the castle
game.addChild(playerCastleUpgradeTxt);
// --- Player castle second upgrade cost label (800 gold, only after first upgrade) ---
var playerCastleUpgrade2Txt = new Text2("YOU NEED 800 GOLD\nTO UPGRADE AGAIN.", {
size: 54,
fill: "#000",
font: "PressStart2P,Pixel,monospace"
});
playerCastleUpgrade2Txt.anchor.set(0.5, -0.2);
playerCastleUpgrade2Txt.x = castlePlayer.x + 180;
playerCastleUpgrade2Txt.y = castlePlayer.y + 320;
playerCastleUpgrade2Txt.visible = false;
game.addChild(playerCastleUpgrade2Txt);
// --- Track second upgrade state for both castles ---
castlePlayer.hasUpgrade2 = false;
castleEnemy.hasUpgrade2 = false; // After 800 gold upgrade, mine-like feature is enabled for enemy
// --- Left side: archer_button and solder_button side by side, visible on screen ---
var leftButtonsY = GAME_H / 2 + 100;
var leftButtonSpacing = 40;
var leftButtonStartX = 120 + 75; // 120px margin + half button width (150/2)
// Archer button (leftmost)
var archerBtn = LK.getAsset('archer_button', {
anchorX: 0.5,
anchorY: 0.5,
x: leftButtonStartX,
y: leftButtonsY
});
archerBtn.visible = false;
game.addChild(archerBtn);
// Add "150 G" label under archerBtn, but only show after castle is upgraded
var archerBtnLabel = new Text2("150 G", {
size: 48,
fill: "#000",
font: "PressStart2P,Pixel,monospace"
});
archerBtnLabel.anchor.set(0.5, 0);
archerBtnLabel.x = archerBtn.x;
archerBtnLabel.y = archerBtn.y + 90; // 90px below center of button (button is 150px tall)
archerBtnLabel.visible = false;
game.addChild(archerBtnLabel);
// Solder button (right of archer)
var solderBtn = LK.getAsset('solder_button', {
anchorX: 0.5,
anchorY: 0.5,
x: leftButtonStartX + 150 + leftButtonSpacing,
// 150 is button width
y: leftButtonsY
});
solderBtn.visible = false;
game.addChild(solderBtn);
// Add "100 G" label under solderBtn
var solderBtnLabel = new Text2("100 G", {
size: 48,
fill: "#000",
font: "PressStart2P,Pixel,monospace"
});
solderBtnLabel.anchor.set(0.5, 0);
solderBtnLabel.x = solderBtn.x;
solderBtnLabel.y = solderBtn.y + 90; // 90px below center of button (button is 150px tall)
solderBtnLabel.visible = false;
game.addChild(solderBtnLabel);
// Wall button (right of solderBtn)
var wallBtn = LK.getAsset('wall_button', {
anchorX: 0.5,
anchorY: 0.5,
x: solderBtn.x + 150 + leftButtonSpacing,
y: leftButtonsY
});
wallBtn.visible = false;
game.addChild(wallBtn);
// Add "50 G" label under wallBtn
var wallBtnLabel = new Text2("50 G", {
size: 48,
fill: "#000",
font: "PressStart2P,Pixel,monospace"
});
wallBtnLabel.anchor.set(0.5, 0);
wallBtnLabel.x = wallBtn.x;
wallBtnLabel.y = wallBtn.y + 90;
wallBtnLabel.visible = false;
game.addChild(wallBtnLabel);
// --- Walls array to track all wall_player objects ---
var wallPlayers = [];
// --- Walls array to track all wall_enemy objects ---
var wallEnemies = [];
// Deduct 50 gold from player when wallBtn is pressed
wallBtn.down = function (x, y, obj) {
if (!gameInputEnabled) return;
if (playerGold >= 50) {
playerGold -= 50;
updateGui();
// Place wall_player in the middle of the two castles
var wallX = (castlePlayer.x + castleEnemy.x) / 2;
var wallY = CASTLE_OFFSET_Y + 120; // align with soldiers' path
var wallPlayer = LK.getAsset('wall_player', {
anchorX: 0.5,
anchorY: 0.5,
x: wallX,
y: wallY
});
wallPlayer.health = 40;
wallPlayer.maxHealth = 40;
wallPlayer.destroyed = false;
game.addChild(wallPlayer);
wallPlayers.push(wallPlayer);
}
};
// --- Enemy AI: Deploy wall_enemy if enough gold and no wall exists ---
// Track total wall_enemy deployed since game start for enemy 2nd upgrade
var enemyWallEnemyDeployCount = 0;
function deployWallEnemy() {
// Only one wall_enemy at a time
if (wallEnemies.length > 0) return false;
if (enemyGold < 50) return false;
enemyGold -= 50;
updateGui();
var wallX = (castlePlayer.x + castleEnemy.x) / 2;
var wallY = CASTLE_OFFSET_Y + 120;
var wallEnemy = LK.getAsset('wall_enemy', {
anchorX: 0.5,
anchorY: 0.5,
x: wallX,
y: wallY
});
wallEnemy.health = 40;
wallEnemy.maxHealth = 40;
wallEnemy.destroyed = false;
game.addChild(wallEnemy);
wallEnemies.push(wallEnemy);
// Increment wall_enemy deploy count for enemy
enemyWallEnemyDeployCount++;
// If enemy has first upgrade, not yet second, has 800 gold, and deployed 15 wall_enemy, do 2nd upgrade automatically
if (castleEnemy.hasUpgrade && !castleEnemy.hasUpgrade2 && enemyGold >= 800 && enemyWallEnemyDeployCount >= 15) {
enemyGold -= 800;
castleEnemy.hasUpgrade2 = true;
// Apply second upgrade effects (same as player)
castleEnemy.health = 2500;
castleEnemy.attack = 4 + Math.floor(Math.random() * 3); // 4-6
castleEnemy.arrowCooldownMax = 40; // faster arrows
// --- MINE-LIKE FEATURE: Increase enemy gold income after 2nd upgrade ---
GOLD_PER_TICK = GOLD_PER_TICK + 5; // Increase gold income for both, or you can use a separate enemyGoldPerTick if needed
updateGui();
}
return true;
}
// --- GUI: Health and Gold displays ---
var playerHealthTxt = new Text2('1000', {
size: 70,
fill: '#fff',
font: "PressStart2P,Pixel,monospace"
});
playerHealthTxt.anchor.set(0, 0);
LK.gui.top.addChild(playerHealthTxt);
// Player LIFE label
var playerLifeLabel = new Text2('LIFE', {
size: 38,
fill: "#000",
font: "PressStart2P,Pixel,monospace"
});
playerLifeLabel.anchor.set(0, 0);
LK.gui.top.addChild(playerLifeLabel);
var playerGoldTxt = new Text2('200', {
size: 60,
fill: '#ffe600',
font: "PressStart2P,Pixel,monospace"
});
playerGoldTxt.anchor.set(0, 0);
LK.gui.top.addChild(playerGoldTxt);
// Add soldier/cavalry cost labels to the left of the tan score
// Removed soldier/cavalry cost labels from GUI
// Player GOLD label
var playerGoldLabel = new Text2('GOLD', {
size: 38,
fill: "#000",
font: "PressStart2P,Pixel,monospace"
});
playerGoldLabel.anchor.set(0, 0);
LK.gui.top.addChild(playerGoldLabel);
var enemyHealthTxt = new Text2('1000', {
size: 70,
fill: '#fff',
font: "PressStart2P,Pixel,monospace"
});
enemyHealthTxt.anchor.set(1, 0);
LK.gui.top.addChild(enemyHealthTxt);
// Enemy LIFE label
var enemyLifeLabel = new Text2('LIFE', {
size: 38,
fill: "#000",
font: "PressStart2P,Pixel,monospace"
});
enemyLifeLabel.anchor.set(1, 0);
LK.gui.top.addChild(enemyLifeLabel);
var enemyGoldTxt = new Text2('200', {
size: 60,
fill: '#ffe600',
font: "PressStart2P,Pixel,monospace"
});
enemyGoldTxt.anchor.set(1, 0);
LK.gui.top.addChild(enemyGoldTxt);
// Enemy GOLD label
var enemyGoldLabel = new Text2('GOLD', {
size: 38,
fill: "#000",
font: "PressStart2P,Pixel,monospace"
});
enemyGoldLabel.anchor.set(1, 0);
LK.gui.top.addChild(enemyGoldLabel);
// --- Win Counter (Rounds Won) ---
// Use storage plugin for persistence
var roundsWon = storage.roundsWon || 0;
var roundsWonTxt = new Text2('Rounds Won: ' + roundsWon, {
size: 60,
fill: '#00ff00',
font: "PressStart2P,Pixel,monospace"
});
roundsWonTxt.anchor.set(1, 0);
LK.gui.top.addChild(roundsWonTxt);
roundsWonTxt.x = LK.gui.width - 40;
roundsWonTxt.y = 220;
// Position GUI elements
playerHealthTxt.x = 120;
playerHealthTxt.y = 40;
playerLifeLabel.x = playerHealthTxt.x + playerHealthTxt.width + 18;
playerLifeLabel.y = playerHealthTxt.y + 10;
playerGoldTxt.x = 120;
playerGoldTxt.y = 120;
playerGoldLabel.x = playerGoldTxt.x + playerGoldTxt.width + 18;
playerGoldLabel.y = playerGoldTxt.y + 8;
// (Removed: Position soldier/cavalry cost labels to the left of the tan score)
enemyHealthTxt.x = LK.gui.width - 120;
enemyHealthTxt.y = 40;
enemyLifeLabel.x = enemyHealthTxt.x - enemyHealthTxt.width - 18;
enemyLifeLabel.y = enemyHealthTxt.y + 10;
enemyGoldTxt.x = LK.gui.width - 120;
enemyGoldTxt.y = 120;
enemyGoldLabel.x = enemyGoldTxt.x - enemyGoldTxt.width - 18;
enemyGoldLabel.y = enemyGoldTxt.y + 8;
// --- Gold income timer ---
var goldTimer = LK.setInterval(function () {
// Only increase gold if difficulty has been chosen
if (!gameInputEnabled) return;
playerGold += GOLD_PER_TICK;
enemyGold += GOLD_PER_TICK; // If enemy has mine upgrade, GOLD_PER_TICK is increased for both
updateGui();
}, GOLD_TICK_MS);
// --- GUI update function ---
function updateGui() {
playerHealthTxt.setText(castlePlayer.health);
playerLifeLabel.x = playerHealthTxt.x + playerHealthTxt.width + 18;
playerLifeLabel.y = playerHealthTxt.y + 10;
playerGoldTxt.setText(playerGold);
playerGoldLabel.x = playerGoldTxt.x + playerGoldTxt.width + 18;
playerGoldLabel.y = playerGoldTxt.y + 8;
enemyHealthTxt.setText(castleEnemy.health);
enemyLifeLabel.x = enemyHealthTxt.x - enemyHealthTxt.width - 18;
enemyLifeLabel.y = enemyHealthTxt.y + 10;
enemyGoldTxt.setText(enemyGold);
enemyGoldLabel.x = enemyGoldTxt.x - enemyGoldTxt.width - 18;
enemyGoldLabel.y = enemyGoldTxt.y + 8;
// Update enemy castle health text and position
castleEnemyHealthTxt.setText(castleEnemy.health);
castleEnemyHealthTxt.x = castleEnemy.x;
castleEnemyHealthTxt.y = castleEnemy.y - 260;
}
// --- Deploy soldier function ---
function deploySoldier(team) {
// Block deployment if difficulty not chosen
if (!gameInputEnabled) return false;
var gold = team === 'player' ? playerGold : enemyGold;
var cost = team === 'player' ? SOLDIER_COST : ENEMY_SOLDIER_COST;
if (gold < cost) return false;
// Deduct gold
if (team === 'player') playerGold -= SOLDIER_COST;else enemyGold -= ENEMY_SOLDIER_COST;
// Fixed health and random attack
var health = 25;
var attack = 5 + Math.floor(Math.random() * 6); // 5-10 inclusive
// Create soldier
var s = new Soldier();
s.init(team, health, attack);
s.speed = SOLDIER_SPEED;
s.inCombat = false;
// Position
s.y = CASTLE_OFFSET_Y + 120; // Move soldiers' exit position down by 120px
if (team === 'player') {
s.x = castlePlayer.x + 120;
playerSoldiers.push(s);
} else {
s.x = castleEnemy.x - 120;
enemySoldiers.push(s);
}
game.addChild(s);
// Play sound when unit leaves the castle
LK.getSound('unit_leave_castle').play();
return true;
}
// --- Player tap to upgrade castle only (soldier deploy moved to solderBtn.down) ---
game.down = function (x, y, obj) {
// Block input until difficulty is chosen
if (!gameInputEnabled) return;
// Check if player clicked on their castle for upgrade
var dx = x - castlePlayer.x;
var dy = y - castlePlayer.y;
if (dx * dx + dy * dy < 250 * 250 && !castlePlayer.hasUpgrade && playerGold >= 350) {
playerGold -= 350;
castlePlayer.upgrade();
updateGui();
// Show archerBtn after upgrade
archerBtn.visible = true;
// Show '150 G' label under archerBtn after upgrade
archerBtnLabel.visible = true;
// Hide the first upgrade text, show the second upgrade text
if (playerCastleUpgradeTxt && typeof playerCastleUpgradeTxt.destroy === "function") {
playerCastleUpgradeTxt.visible = false;
}
playerCastleUpgrade2Txt.visible = true;
return;
}
// Second upgrade: only if first upgrade is done, not already done, and enough gold
if (dx * dx + dy * dy < 250 * 250 && castlePlayer.hasUpgrade && !castlePlayer.hasUpgrade2 && playerGold >= 800) {
playerGold -= 800;
castlePlayer.hasUpgrade2 = true;
// Apply second upgrade effects
castlePlayer.health = 2500;
castlePlayer.attack = 4 + Math.floor(Math.random() * 3); // 4-6
castlePlayer.arrowCooldownMax = 40; // faster arrows
updateGui();
// Hide the second upgrade text
playerCastleUpgrade2Txt.visible = false;
// Optionally, show a message or effect for max upgrade
return;
}
// (Removed: deploySoldier on screen tap)
};
// --- Deploy soldier when solderBtn is pressed ---
solderBtn.down = function (x, y, obj) {
if (!gameInputEnabled) return;
if (playerGold >= SOLDIER_COST) {
deploySoldier('player');
updateGui();
}
};
// --- Deploy archer_player when archerBtn is pressed (uses archer_player asset, cost 10 gold) ---
archerBtn.down = function (x, y, obj) {
if (!gameInputEnabled) return;
if (playerGold >= 150) {
playerGold -= 150;
// Create a new Soldier but use archer_player asset
var s = new Soldier();
// Set archer_player health to be twice that of soldier_player
var baseSoldierHealth = 25;
s.init('player', baseSoldierHealth * 2, 5 + Math.floor(Math.random() * 6)); // health 50, attack 5-10
s.speed = SOLDIER_SPEED * 0.8; // Already uses updated SOLDIER_SPEED, now 75% faster
s.inCombat = false;
// Remove previous asset and attach archer_player asset
if (s.asset) {
s.removeChild(s.asset);
}
s.asset = s.attachAsset('archer_player', {
anchorX: 0.5,
anchorY: 0.5
});
// Position
s.y = CASTLE_OFFSET_Y + 120;
s.x = castlePlayer.x + 120;
playerSoldiers.push(s);
game.addChild(s);
// Play sound when archer leaves the castle
LK.getSound('unit_leave_castle').play();
updateGui();
}
};
// --- Enemy AI: deploy soldier automatically if enough gold ---
// Track enemy soldier deployments for 2:1 soldier:archer ratio
var enemySoldierDeployCount = 0;
var enemyNextUnit = 'soldier'; // always start with soldier
var enemyTotalSoldierDeployed = 0;
var enemyUpgradeGoldReserved = 200;
// Track total enemy soldier_enemys since last wall_enemy
var enemySoldierSinceLastWall = 0;
// --- Track if enemy castle has done second upgrade ---
castleEnemy.hasUpgrade2 = false;
var enemyDeployTimer = LK.setInterval(function () {
// Block AI until difficulty is chosen
if (!gameInputEnabled) return;
// If not upgraded, reserve 200 gold for upgrade after 10 soldiers
if (!castleEnemy.hasUpgrade) {
// In hard mode, upgrade for free after 10 soldiers
if (difficulty === "hard") {
if (enemyTotalSoldierDeployed >= 10) {
castleEnemy.upgrade();
updateGui();
return;
}
} else {
// Only upgrade after 10 soldier_enemy deployed and enough gold (350 - 200 = 150 must be available for upgrade)
if (enemyTotalSoldierDeployed >= 10 && enemyGold >= 350 - enemyUpgradeGoldReserved) {
enemyGold -= 350 - enemyUpgradeGoldReserved;
castleEnemy.upgrade();
updateGui();
return;
}
}
}
// If not upgraded and haven't deployed 10 soldiers, only deploy if enough gold and after reserving 200 for upgrade
if (!castleEnemy.hasUpgrade && enemyTotalSoldierDeployed < 10) {
if (enemyNextUnit === 'soldier' && enemyGold >= ENEMY_SOLDIER_COST + enemyUpgradeGoldReserved) {
if (deploySoldier('enemy')) {
updateGui();
enemySoldierDeployCount++;
enemyTotalSoldierDeployed++;
enemySoldierSinceLastWall++; // Track for wall_enemy spawn
// For every 5 soldier_enemys, spawn a wall_enemy
if (enemySoldierSinceLastWall >= 5) {
if (enemyGold >= 50 && wallEnemies.length === 0) {
deployWallEnemy();
}
enemySoldierSinceLastWall = 0;
}
// After 2 soldiers, next must be archer (if possible)
if (enemySoldierDeployCount >= 2) {
enemyNextUnit = 'soldier'; // No archers before upgrade
enemySoldierDeployCount = 0;
}
}
}
// Do not deploy archers before upgrade
return;
}
// --- Enemy castle second upgrade logic (AI) ---
// If enemy castle has first upgrade, not yet second, and enough gold, do second upgrade
if (castleEnemy.hasUpgrade && !castleEnemy.hasUpgrade2 && enemyGold >= 800) {
enemyGold -= 800;
castleEnemy.hasUpgrade2 = true;
// Apply second upgrade effects (same as player)
castleEnemy.health = 2500;
castleEnemy.attack = 4 + Math.floor(Math.random() * 3); // 4-6
castleEnemy.arrowCooldownMax = 40; // faster arrows
// --- MINE-LIKE FEATURE: Increase enemy gold income after 2nd upgrade ---
GOLD_PER_TICK = GOLD_PER_TICK + 5; // Increase gold income for both, or you can use a separate enemyGoldPerTick if needed
updateGui();
}
// After upgrade, normal logic: after every 2 soldiers, deploy 1 archer
if (enemyNextUnit === 'soldier' && enemyGold >= ENEMY_SOLDIER_COST) {
if (deploySoldier('enemy')) {
updateGui();
enemySoldierDeployCount++;
enemyTotalSoldierDeployed++;
enemySoldierSinceLastWall++; // Track for wall_enemy spawn
// For every 5 soldier_enemys, spawn a wall_enemy
if (enemySoldierSinceLastWall >= 5) {
if (enemyGold >= 50 && wallEnemies.length === 0) {
deployWallEnemy();
}
enemySoldierSinceLastWall = 0;
}
// After 2 soldiers, next must be archer (if possible)
if (enemySoldierDeployCount >= 2 && castleEnemy.hasUpgrade) {
enemyNextUnit = 'archer';
} else {
enemyNextUnit = 'soldier';
}
}
// Try to deploy wall_enemy if enough gold and no wall exists (after soldier deploy)
if (castleEnemy.hasUpgrade && enemyGold >= 50 && wallEnemies.length === 0) {
deployWallEnemy();
}
} else if (enemyNextUnit === 'archer' && castleEnemy.hasUpgrade && enemyGold >= 150) {
// Deploy archer_enemy
enemyGold -= 150;
var s = new Soldier();
var baseSoldierHealth = 25;
s.init('enemy', baseSoldierHealth * 2, 5 + Math.floor(Math.random() * 6)); // health 50, attack 5-10
s.speed = SOLDIER_SPEED * 0.8; // Already uses updated SOLDIER_SPEED, now 75% faster
s.inCombat = false;
if (s.asset) {
s.removeChild(s.asset);
}
s.asset = s.attachAsset('archer_enemy', {
anchorX: 0.5,
anchorY: 0.5
});
s.y = CASTLE_OFFSET_Y + 120;
s.x = castleEnemy.x - 120;
enemySoldiers.push(s);
game.addChild(s);
// Play sound when archer_enemy leaves the castle
LK.getSound('unit_leave_castle').play();
updateGui();
// Reset soldier deploy count and go back to soldier
enemySoldierDeployCount = 0;
enemyNextUnit = 'soldier';
// Try to deploy wall_enemy if enough gold and no wall exists (after archer deploy)
if (castleEnemy.hasUpgrade && enemyGold >= 50 && wallEnemies.length === 0) {
deployWallEnemy();
}
}
}, 600);
// --- Arrows array ---
var arrows = [];
// --- Main update loop ---
game.update = function () {
// --- Update castles (health, arrows) ---
castlePlayer.update();
castleEnemy.update();
// --- Update all soldiers ---
// 1. Move soldiers forward if not in combat
// 2. Check for combat with opposing soldiers
// 3. If at enemy castle, attack castle
// --- Player soldiers ---
for (var i = playerSoldiers.length - 1; i >= 0; i--) {
var s = playerSoldiers[i];
s.inCombat = false;
// --- Check for wall_enemy collision ---
var wallEngaged = false;
for (var w = 0; w < wallEnemies.length; w++) {
var wall = wallEnemies[w];
if (wall.destroyed) continue;
// Simple collision: check if close enough horizontally and vertically
if (Math.abs(s.x - wall.x) < 80 && Math.abs(s.y - wall.y) < 180) {
s.inCombat = true;
wallEngaged = true;
// Player attacks wall every 12 frames
if (LK.ticks % 12 === 0) {
wall.health -= s.attack;
}
// If wall destroyed, remove from game and array
if (wall.health <= 0) {
wall.destroyed = true;
if (typeof wall.destroy === "function") wall.destroy();
wallEnemies.splice(w, 1);
w--;
}
break; // Only engage one wall at a time
}
}
if (wallEngaged) continue;
// Check for enemy soldier in range
var engaged = false;
for (var j = 0; j < enemySoldiers.length; j++) {
var e = enemySoldiers[j];
// If close enough (overlap)
if (Math.abs(s.x - e.x) < 80) {
// Engage in combat
s.inCombat = true;
e.inCombat = true;
// Both attack each other
if (LK.ticks % 12 === 0) {
// Attack every 12 frames (~5 times/sec)
e.health -= s.attack;
s.health -= e.attack;
}
// Remove dead soldiers
if (e.health <= 0) {
e.destroy();
enemySoldiers.splice(j, 1);
j--;
// Award 10 gold to player for killing enemy soldier
playerGold += 10;
updateGui();
}
if (s.health <= 0) {
s.destroy();
playerSoldiers.splice(i, 1);
i--;
// Award 10 gold to enemy for killing player soldier
enemyGold += 10;
updateGui();
engaged = true;
break;
}
engaged = true;
break;
}
}
if (engaged) continue;
// If not in combat, check if at enemy castle
if (Math.abs(s.x - castleEnemy.x) < 120) {
s.inCombat = true;
if (LK.ticks % 12 === 0) {
castleEnemy.health -= s.attack;
if (castleEnemy.health < 0) castleEnemy.health = 0;
updateGui();
}
// Remove soldier if castle destroyed
if (castleEnemy.health <= 0) {
s.destroy();
playerSoldiers.splice(i, 1);
i--;
}
}
}
// --- Enemy soldiers ---
for (var i = enemySoldiers.length - 1; i >= 0; i--) {
var s = enemySoldiers[i];
s.inCombat = false;
// --- Check for wall_player collision ---
var wallEngaged = false;
for (var w = 0; w < wallPlayers.length; w++) {
var wall = wallPlayers[w];
if (wall.destroyed) continue;
// Simple collision: check if close enough horizontally and vertically
if (Math.abs(s.x - wall.x) < 80 && Math.abs(s.y - wall.y) < 180) {
s.inCombat = true;
wallEngaged = true;
// Enemy attacks wall every 12 frames
if (LK.ticks % 12 === 0) {
wall.health -= s.attack;
}
// If wall destroyed, remove from game and array
if (wall.health <= 0) {
wall.destroyed = true;
if (typeof wall.destroy === "function") wall.destroy();
wallPlayers.splice(w, 1);
w--;
}
break; // Only engage one wall at a time
}
}
if (wallEngaged) continue;
// Check for player soldier in range
var engaged = false;
for (var j = 0; j < playerSoldiers.length; j++) {
var e = playerSoldiers[j];
if (Math.abs(s.x - e.x) < 80) {
s.inCombat = true;
e.inCombat = true;
if (LK.ticks % 12 === 0) {
e.health -= s.attack;
s.health -= e.attack;
}
if (e.health <= 0) {
e.destroy();
playerSoldiers.splice(j, 1);
j--;
// Award 10 gold to enemy for killing player soldier
enemyGold += 10;
updateGui();
}
if (s.health <= 0) {
s.destroy();
enemySoldiers.splice(i, 1);
i--;
// Award 10 gold to player for killing enemy soldier
playerGold += 10;
updateGui();
engaged = true;
break;
}
engaged = true;
break;
}
}
if (engaged) continue;
// If not in combat, check if at player castle
if (Math.abs(s.x - castlePlayer.x) < 120) {
s.inCombat = true;
if (LK.ticks % 12 === 0) {
castlePlayer.health -= s.attack;
if (castlePlayer.health < 0) castlePlayer.health = 0;
updateGui();
}
if (castlePlayer.health <= 0) {
s.destroy();
enemySoldiers.splice(i, 1);
i--;
}
}
}
// --- Move soldiers ---
for (var i = 0; i < playerSoldiers.length; i++) {
playerSoldiers[i].update();
}
for (var i = 0; i < enemySoldiers.length; i++) {
enemySoldiers[i].update();
}
// --- Update arrows ---
for (var i = arrows.length - 1; i >= 0; i--) {
var a = arrows[i];
a.update();
if (a.destroyed) {
arrows.splice(i, 1);
}
}
// --- Check win/lose ---
if (castlePlayer.health <= 0) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
if (castleEnemy.health <= 0) {
// Increase and persist rounds won
roundsWon = (storage.roundsWon || 0) + 1;
storage.roundsWon = roundsWon;
roundsWonTxt.setText('Rounds Won: ' + roundsWon);
LK.effects.flashScreen(0x00ff00, 1000);
LK.showYouWin();
return;
}
};
// --- Clean up timers on game over ---
game.destroy = function () {
LK.clearInterval(goldTimer);
LK.clearInterval(enemyDeployTimer);
};
ottoman castle. In-Game asset. 2d. High contrast. No shadows. pixel art
roma knight. In-Game asset. 2d. High contrast. No shadows
Ottoman Janissary. In-Game asset. 2d. High contrast. No shadows
sword. In-Game asset. 2d. High contrast. No shadows
Ottoman camel warrior. In-Game asset. 2d. High contrast. No shadows. pixel art
Roman cavalry. In-Game asset. 2d. High contrast. No shadows
camel face. In-Game asset. 2d. High contrast. No shadows. pixel art
wall icon game. In-Game asset. 2d. High contrast. No shadows
RED CRESCENT ICON. In-Game asset. 2d. High contrast. No shadows
HEALING TENT OTTOMAN. In-Game asset. 2d. High contrast. No shadows