User prompt
far from each other remove
User prompt
Put worker, warrior, wizard and dragon recruitment at the bottom of the screen
User prompt
Each team should have only one tower
User prompt
Both teams start with 0 coins and each team has 1 worker at the beginning and each team has a tower
Code edit (1 edits merged)
Please save this source code
User prompt
Tower Defense: Coin Wars
Initial prompt
Make a tower defense game. In this game, there is a us and an AI. There are towers at the bottom, middle and top middle of the screen. Coins appear randomly around and the worker character tries to collect these coins. For each coin collected, 1 coin goes to that team. Let's try to summon new workers, warriors, wizards and dragons with those coins. The wizard, warrior and dragon try to attack the towers. Workers have 5 coins, warriors have 7 coins, wizards have 10 coins and the dragon has 20 coins.
/**** * Plugins ****/ var storage = LK.import("@upit/storage.v1"); var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Arrow = Container.expand(function (team, startX, startY, targetX, targetY, damage) { var self = Container.call(this); var graphics = self.attachAsset('Arrow', { anchorX: 0.5, anchorY: 0.5 }); self.team = team; self.damage = damage; self.speed = 8; self.target = null; // Calculate direction var dx = targetX - startX; var dy = targetY - startY; var distance = Math.sqrt(dx * dx + dy * dy); self.directionX = dx / distance; self.directionY = dy / distance; // Set rotation to point toward target self.rotation = Math.atan2(dy, dx); // Position arrow at start self.x = startX; self.y = startY; self.update = function () { // Move arrow self.x += self.directionX * self.speed; self.y += self.directionY * self.speed; // Check if arrow is off screen if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) { self.destroy(); // Remove from arrows array for (var i = arrows.length - 1; i >= 0; i--) { if (arrows[i] === self) { arrows.splice(i, 1); break; } } } // Check collision with enemy units var enemyUnits = self.team === 'player' ? aiUnits : playerUnits; for (var i = 0; i < enemyUnits.length; i++) { var enemy = enemyUnits[i]; if (self.intersects(enemy)) { enemy.health -= self.damage; if (enemy.health <= 0) { enemyUnits.splice(i, 1); enemy.destroy(); } self.destroy(); // Remove from arrows array for (var j = arrows.length - 1; j >= 0; j--) { if (arrows[j] === self) { arrows.splice(j, 1); break; } } return; } } // Check collision with enemy towers var enemyTowers = self.team === 'player' ? aiTowers : playerTowers; for (var i = 0; i < enemyTowers.length; i++) { var tower = enemyTowers[i]; if (self.intersects(tower)) { tower.health -= self.damage; if (tower.health <= 0) { enemyTowers.splice(i, 1); tower.destroy(); LK.getSound('towerDestroyed').play(); } self.destroy(); // Remove from arrows array for (var j = arrows.length - 1; j >= 0; j--) { if (arrows[j] === self) { arrows.splice(j, 1); break; } } return; } } }; return self; }); var Coin = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('coin', { anchorX: 0.5, anchorY: 0.5 }); self.value = 1; self.collected = false; return self; }); var CombatUnit = Container.expand(function (team, unitType) { var self = Container.call(this); var assetName = team === 'player' ? unitType : 'ai' + unitType.charAt(0).toUpperCase() + unitType.slice(1); var graphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); self.team = team; self.unitType = unitType; self.speed = unitType === 'dragon' ? 3 : unitType === 'wizard' ? 1.5 : unitType === 'archer' ? 2.5 : 2; self.damage = unitType === 'dragon' ? 40 : unitType === 'wizard' ? 30 : unitType === 'archer' ? 20 : 10; self.health = unitType === 'dragon' ? 50 : unitType === 'wizard' ? 40 : unitType === 'archer' ? 30 : unitType === 'warrior' ? 20 : 10; self.maxHealth = self.health; self.attackRange = unitType === 'wizard' ? 100 : unitType === 'archer' ? 200 : 50; self.attackCooldown = 0; self.target = null; self.isWaiting = !zombieMode && team === 'player'; // Only player units wait in normal mode // Create health bar background (black) self.healthBarBg = LK.getAsset('blackBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 0.75 }); self.healthBarBg.x = 0; self.healthBarBg.y = -120; self.healthBarBg.tint = team === 'player' ? 0x0000ff : 0x000000; self.addChild(self.healthBarBg); // Create health bar (green for player, red for AI) self.healthBar = LK.getAsset('blackBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 0.75 }); self.healthBar.x = 0; self.healthBar.y = -120; self.healthBar.tint = team === 'player' ? 0x00ff00 : 0xff0000; self.addChild(self.healthBar); self.healthText = new Text2(self.health.toString(), { size: 50, fill: '#ffffff' }); self.healthText.anchor.set(0.5, 0.5); self.addChild(self.healthText); self.healthText.x = 0; self.healthText.y = -120; self.update = function () { if (self.attackCooldown > 0) { self.attackCooldown--; } // In zombie mode, player units prioritize attacking zombies if (zombieMode && self.team === 'player' && self.unitType !== 'worker') { self.attackZombies(); } else if (!zombieMode && self.team === 'player' && self.isWaiting) { // In normal mode, player units wait unless attack is triggered // Stay in position and don't move } else { if (!self.target) { self.findNearestEnemyTower(); } else { self.moveToTarget(); } // Combat with nearby enemy units self.combatNearbyEnemies(); } self.healthText.setText(self.health.toString()); // Update health bar scale based on health percentage var healthPercentage = self.health / self.maxHealth; tween(self.healthBar, { scaleX: 1.5 * healthPercentage }, { duration: 200 }); }; self.findNearestEnemyTower = function () { // In zombie mode, player units should prioritize attacking zombies if (zombieMode && self.team === 'player' && self.unitType !== 'worker') { var nearestZombie = null; var nearestDistance = Infinity; for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var distance = Math.sqrt(Math.pow(zombie.x - self.x, 2) + Math.pow(zombie.y - self.y, 2)); if (distance < nearestDistance) { nearestDistance = distance; nearestZombie = zombie; } } // If we found a zombie, target it if (nearestZombie) { self.target = nearestZombie; return; } } // Find nearest target - prioritize ALL enemy units first (including workers), then towers var nearestTarget = null; var nearestDistance = Infinity; // First check all enemy units (including workers) var enemyUnits = self.team === 'player' ? aiUnits : playerUnits; for (var i = 0; i < enemyUnits.length; i++) { var enemy = enemyUnits[i]; // Target all enemy units now, including workers if (self.unitType !== 'worker') { var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2)); if (distance < nearestDistance) { nearestDistance = distance; nearestTarget = enemy; } } } // Only target towers if no enemy units remain if (!nearestTarget) { var enemyTowers = self.team === 'player' ? aiTowers : playerTowers; for (var i = 0; i < enemyTowers.length; i++) { var tower = enemyTowers[i]; var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2)); if (distance < nearestDistance) { nearestDistance = distance; nearestTarget = tower; } } } self.target = nearestTarget; }; 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 < self.attackRange) { self.attackTarget(); } else { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } }; self.attackTarget = function () { if (self.attackCooldown <= 0 && self.target) { if (self.unitType === 'archer') { // Archer shoots arrow var arrow = new Arrow(self.team, self.x, self.y, self.target.x, self.target.y, self.damage); arrows.push(arrow); game.addChild(arrow); } else if (self.unitType === 'wizard') { // Wizard shoots magic var magic = new Magic(self.team, self.x, self.y, self.target.x, self.target.y, self.damage); magics.push(magic); game.addChild(magic); } else if (self.unitType === 'dragon') { // Change to attack asset for all dragon attacks self.removeChild(graphics); var assetName = 'DragonAttack'; graphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); // Dragon shoots fire with 1 damage var fire = new Fire(self.team, self.x, self.y, self.target.x, self.target.y, 1); fires.push(fire); game.addChild(fire); } else { // Melee attack self.target.health -= self.damage; if (self.target.health <= 0) { // Check if target is a unit or tower if (self.target.unitType) { // Target is a unit self.destroyUnit(); } else { // Target is a tower self.destroyTower(); } } } // Set dragon attack cooldown to 3 frames (0.05 seconds) if (self.unitType === 'dragon') { self.attackCooldown = 3; } else { self.attackCooldown = 60; } LK.getSound('attack').play(); } }; self.destroyUnit = function () { var units = self.team === 'player' ? aiUnits : playerUnits; var index = units.indexOf(self.target); if (index > -1) { units.splice(index, 1); self.target.destroy(); self.target = null; } }; self.destroyTower = function () { var towers = self.team === 'player' ? aiTowers : playerTowers; var index = towers.indexOf(self.target); if (index > -1) { towers.splice(index, 1); self.target.destroy(); LK.getSound('towerDestroyed').play(); self.target = null; } }; self.attackZombies = function () { // Find nearest zombie to target var nearestZombie = null; var nearestDistance = Infinity; for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var dx = zombie.x - self.x; var dy = zombie.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < nearestDistance) { nearestDistance = distance; nearestZombie = zombie; } } // Move toward and attack nearest zombie if (nearestZombie) { var dx = nearestZombie.x - self.x; var dy = nearestZombie.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Move toward zombie if not in attack range if (distance > self.attackRange) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } else if (self.attackCooldown <= 0) { // Attack zombie based on unit type if (self.unitType === 'archer') { // Archer shoots arrow at zombie var arrow = new Arrow(self.team, self.x, self.y, nearestZombie.x, nearestZombie.y, self.damage); arrows.push(arrow); game.addChild(arrow); } else if (self.unitType === 'wizard') { // Wizard shoots magic at zombie var magic = new Magic(self.team, self.x, self.y, nearestZombie.x, nearestZombie.y, self.damage); magics.push(magic); game.addChild(magic); } else if (self.unitType === 'dragon') { // Change to attack asset for dragon zombie attacks self.removeChild(graphics); var assetName = 'DragonAttack'; graphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); // Dragon shoots fire at zombie var fire = new Fire(self.team, self.x, self.y, nearestZombie.x, nearestZombie.y, self.damage); fires.push(fire); game.addChild(fire); } else { // Melee attack (warrior) nearestZombie.health -= self.damage; if (nearestZombie.health <= 0) { for (var j = 0; j < zombies.length; j++) { if (zombies[j] === nearestZombie) { zombies.splice(j, 1); nearestZombie.destroy(); break; } } } } self.attackCooldown = 60; // 1 second cooldown LK.getSound('attack').play(); } } }; self.combatNearbyEnemies = function () { if (self.attackCooldown <= 0) { var enemyUnits = self.team === 'player' ? aiUnits : playerUnits; var meleeRange = 60; // Close combat range for (var i = 0; i < enemyUnits.length; i++) { var enemy = enemyUnits[i]; // Skip workers in combat if (enemy.unitType === 'worker') { continue; } var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // If enemy unit is within melee range, attack it if (distance <= meleeRange) { enemy.health -= self.damage; self.attackCooldown = 60; // 1 second cooldown LK.getSound('attack').play(); // Check if enemy is destroyed if (enemy.health <= 0) { enemyUnits.splice(i, 1); enemy.destroy(); } break; // Only attack one enemy per cycle } } } }; return self; }); var Fire = Container.expand(function (team, startX, startY, targetX, targetY, damage) { var self = Container.call(this); var graphics = self.attachAsset('Magic', { anchorX: 0.5, anchorY: 0.5 }); self.team = team; self.damage = damage; self.speed = 4; self.target = null; // Calculate direction var dx = targetX - startX; var dy = targetY - startY; var distance = Math.sqrt(dx * dx + dy * dy); self.directionX = dx / distance; self.directionY = dy / distance; // Position fire at start self.x = startX; self.y = startY; // Add fire effect - orange/red tint and scaling graphics.tint = 0xff4500; // Orange-red fire color tween(graphics, { scaleX: 1.5, scaleY: 1.5 }, { duration: 200, easing: tween.easeOut }); self.update = function () { // Move fire self.x += self.directionX * self.speed; self.y += self.directionY * self.speed; // Add fire flicker rotation graphics.rotation += 0.15; // Check if fire is off screen if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) { self.destroy(); // Remove from fires array for (var i = fires.length - 1; i >= 0; i--) { if (fires[i] === self) { fires.splice(i, 1); break; } } } // Check collision with enemy units var enemyUnits = self.team === 'player' ? aiUnits : playerUnits; for (var i = 0; i < enemyUnits.length; i++) { var enemy = enemyUnits[i]; if (self.intersects(enemy)) { enemy.health -= self.damage; if (enemy.health <= 0) { enemyUnits.splice(i, 1); enemy.destroy(); } self.destroy(); // Remove from fires array for (var j = fires.length - 1; j >= 0; j--) { if (fires[j] === self) { fires.splice(j, 1); break; } } return; } } // Check collision with enemy towers var enemyTowers = self.team === 'player' ? aiTowers : playerTowers; for (var i = 0; i < enemyTowers.length; i++) { var tower = enemyTowers[i]; if (self.intersects(tower)) { tower.health -= self.damage; if (tower.health <= 0) { enemyTowers.splice(i, 1); tower.destroy(); LK.getSound('towerDestroyed').play(); } self.destroy(); // Remove from fires array for (var j = fires.length - 1; j >= 0; j--) { if (fires[j] === self) { fires.splice(j, 1); break; } } return; } } }; return self; }); var Magic = Container.expand(function (team, startX, startY, targetX, targetY, damage) { var self = Container.call(this); var graphics = self.attachAsset('Magic', { anchorX: 0.5, anchorY: 0.5 }); self.team = team; self.damage = damage; self.speed = 6; self.target = null; // Calculate direction var dx = targetX - startX; var dy = targetY - startY; var distance = Math.sqrt(dx * dx + dy * dy); self.directionX = dx / distance; self.directionY = dy / distance; // Position magic at start self.x = startX; self.y = startY; // Add magical glow effect tween(graphics, { tint: 0x9966ff }, { duration: 500, easing: tween.easeInOut }); tween(graphics, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.easeOut }); self.update = function () { // Move magic self.x += self.directionX * self.speed; self.y += self.directionY * self.speed; // Add sparkle rotation graphics.rotation += 0.1; // Check if magic is off screen if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) { self.destroy(); // Remove from magics array for (var i = magics.length - 1; i >= 0; i--) { if (magics[i] === self) { magics.splice(i, 1); break; } } } // Check collision with enemy units var enemyUnits = self.team === 'player' ? aiUnits : playerUnits; for (var i = 0; i < enemyUnits.length; i++) { var enemy = enemyUnits[i]; if (self.intersects(enemy)) { enemy.health -= self.damage; if (enemy.health <= 0) { enemyUnits.splice(i, 1); enemy.destroy(); } self.destroy(); // Remove from magics array for (var j = magics.length - 1; j >= 0; j--) { if (magics[j] === self) { magics.splice(j, 1); break; } } return; } } // Check collision with enemy towers var enemyTowers = self.team === 'player' ? aiTowers : playerTowers; for (var i = 0; i < enemyTowers.length; i++) { var tower = enemyTowers[i]; if (self.intersects(tower)) { tower.health -= self.damage; if (tower.health <= 0) { enemyTowers.splice(i, 1); tower.destroy(); LK.getSound('towerDestroyed').play(); } self.destroy(); // Remove from magics array for (var j = magics.length - 1; j >= 0; j--) { if (magics[j] === self) { magics.splice(j, 1); break; } } return; } } }; return self; }); var Tower = Container.expand(function (team) { var self = Container.call(this); var assetName = team === 'player' ? 'playerTower' : 'aiTower'; var graphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 1.0 }); self.team = team; self.health = 500; self.maxHealth = 500; // Create health bar background (black) self.healthBarBg = LK.getAsset('blackBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.08, scaleY: 0.625 }); self.healthBarBg.x = 0; self.healthBarBg.y = -262.5; self.healthBarBg.tint = 0x0000ff; self.addChild(self.healthBarBg); // Create health bar (green for player, red for AI) self.healthBar = LK.getAsset('blackBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.08, scaleY: 0.625 }); self.healthBar.x = 0; self.healthBar.y = -262.5; self.healthBar.tint = team === 'player' ? 0x00ff00 : 0xff0000; self.addChild(self.healthBar); self.healthText = new Text2(self.health.toString(), { size: 42, fill: '#ffffff' }); self.healthText.anchor.set(0.5, 0.5); self.addChild(self.healthText); self.healthText.x = 0; self.healthText.y = -262.5; self.attackCooldown = 0; self.attackRange = 400; // Damage area outline removed as requested self.update = function () { if (self.attackCooldown > 0) { self.attackCooldown--; } self.attackNearbyEnemies(); self.healthText.setText(self.health.toString()); // Update health bar scale based on health percentage var healthPercentage = self.health / self.maxHealth; tween(self.healthBar, { scaleX: 2.08 * healthPercentage }, { duration: 200 }); }; self.attackNearbyEnemies = function () { if (self.attackCooldown <= 0) { var enemyUnits = self.team === 'player' ? aiUnits : playerUnits; var nearestEnemy = null; var nearestDistance = Infinity; // Find nearest enemy unit within attack range for (var i = 0; i < enemyUnits.length; i++) { var enemy = enemyUnits[i]; var dx = enemy.x - self.x; var dy = enemy.y - (self.y - 131.25); // Adjust for tower center position var distance = Math.sqrt(dx * dx + dy * dy); // Only consider units within the attack range if (distance <= self.attackRange && distance < nearestDistance) { nearestDistance = distance; nearestEnemy = enemy; } } // Attack only the nearest enemy if found if (nearestEnemy) { nearestEnemy.health -= 1; // Deal 1 damage as specified self.attackCooldown = 3; // 0.05 seconds at 60 FPS LK.getSound('attack').play(); if (nearestEnemy.health <= 0) { var index = enemyUnits.indexOf(nearestEnemy); if (index > -1) { self.destroyUnit(nearestEnemy, enemyUnits, index); } } } // In zombie mode, player towers also attack zombies (find nearest zombie) if (zombieMode && self.team === 'player') { var nearestZombie = null; var nearestZombieDistance = Infinity; for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var dx = zombie.x - self.x; var dy = zombie.y - (self.y - 131.25); var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= self.attackRange && distance < nearestZombieDistance) { nearestZombieDistance = distance; nearestZombie = zombie; } } // Attack only the nearest zombie if found if (nearestZombie && !nearestEnemy) { // Only attack zombie if no enemy unit was attacked nearestZombie.health -= 1; self.attackCooldown = 3; LK.getSound('attack').play(); if (nearestZombie.health <= 0) { var index = zombies.indexOf(nearestZombie); if (index > -1) { zombies.splice(index, 1); nearestZombie.destroy(); } } } } } }; self.showDamageArea = function () { // No visual effects when towers attack }; self.destroyUnit = function (unit, unitsArray, index) { unitsArray.splice(index, 1); unit.destroy(); }; return self; }); var Worker = Container.expand(function (team) { var self = Container.call(this); var assetName = team === 'player' ? 'worker' : 'aiWorker'; var graphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); self.team = team; self.speed = 4; self.target = null; self.collectRange = 40; self.health = 10; self.maxHealth = 10; // Create health bar background (black) self.healthBarBg = LK.getAsset('blackBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 0.75 }); self.healthBarBg.x = 0; self.healthBarBg.y = -120; self.healthBarBg.tint = 0x000000; self.addChild(self.healthBarBg); // Create health bar (green for player, red for AI) self.healthBar = LK.getAsset('blackBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 0.75 }); self.healthBar.x = 0; self.healthBar.y = -120; self.healthBar.tint = team === 'player' ? 0x00ff00 : 0xff0000; self.addChild(self.healthBar); self.healthText = new Text2(self.health.toString(), { size: 50, fill: '#ffffff' }); self.healthText.anchor.set(0.5, 0.5); self.addChild(self.healthText); self.healthText.x = 0; self.healthText.y = -120; self.update = function () { if (!self.target) { self.findNearestCoin(); } else { self.moveToTarget(); } // Workers can defend themselves self.defendSelf(); self.healthText.setText(self.health.toString()); // Update health bar scale based on health percentage var healthPercentage = self.health / self.maxHealth; tween(self.healthBar, { scaleX: 1.5 * healthPercentage }, { duration: 200 }); }; self.findNearestCoin = function () { var targetCoins = self.team === 'player' ? playerCoins_array : aiCoins_array; var teamWorkers = self.team === 'player' ? playerUnits : aiUnits; var availableCoins = []; // First, collect all untargeted coins for (var i = 0; i < targetCoins.length; i++) { var coin = targetCoins[i]; if (!coin.collected) { // Check if another worker is already targeting this coin var isTargeted = false; for (var j = 0; j < teamWorkers.length; j++) { var worker = teamWorkers[j]; if (worker.unitType === 'worker' && worker !== self && worker.target === coin) { isTargeted = true; break; } } if (!isTargeted) { availableCoins.push(coin); } } } // If no available coins, clear target if (availableCoins.length === 0) { self.target = null; return; } // Get worker index to create unique targeting preference var workerIndex = -1; for (var i = 0; i < teamWorkers.length; i++) { if (teamWorkers[i] === self && teamWorkers[i].unitType === 'worker') { workerIndex = i; break; } } // Use worker index to create different targeting preferences var targetCoin = null; if (workerIndex >= 0 && workerIndex < availableCoins.length) { // Each worker gets a different coin based on their index targetCoin = availableCoins[workerIndex]; } else { // If more workers than coins, use modulo to cycle through available coins var coinIndex = workerIndex % availableCoins.length; targetCoin = availableCoins[coinIndex]; } // If targeted approach fails, fall back to nearest coin if (!targetCoin) { var nearestCoin = null; var nearestDistance = Infinity; for (var i = 0; i < availableCoins.length; i++) { var coin = availableCoins[i]; var distance = Math.sqrt(Math.pow(coin.x - self.x, 2) + Math.pow(coin.y - self.y, 2)); if (distance < nearestDistance) { nearestDistance = distance; nearestCoin = coin; } } targetCoin = nearestCoin; } self.target = targetCoin; }; self.moveToTarget = function () { if (!self.target || self.target.collected) { self.target = null; 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 < self.collectRange) { self.collectCoin(); } else { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } }; self.collectCoin = function () { if (self.target && !self.target.collected) { self.target.collected = true; if (self.team === 'player') { playerCoins += self.target.value; } else { aiCoins += self.target.value; } LK.getSound('coinCollect').play(); self.target = null; } }; self.defendSelf = function () { var enemyUnits = self.team === 'player' ? aiUnits : playerUnits; var defenseRange = 50; for (var i = 0; i < enemyUnits.length; i++) { var enemy = enemyUnits[i]; // Workers only defend against other workers if (enemy.unitType === 'worker') { var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= defenseRange) { enemy.health -= 5; // Workers do less damage LK.getSound('attack').play(); if (enemy.health <= 0) { enemyUnits.splice(i, 1); enemy.destroy(); } break; } } } }; return self; }); var Zombie = Container.expand(function (waveNumber) { var self = Container.call(this); var graphics = self.attachAsset('zombie', { anchorX: 0.5, anchorY: 0.5 }); // Stats increase with wave number var waveMultiplier = waveNumber || 1; self.speed = 1.5 + (waveMultiplier - 1) * 0.2; // Speed increases slightly each wave self.health = 30 + (waveMultiplier - 1) * 15; // Health increases by 15 each wave self.maxHealth = self.health; self.damage = 10 + (waveMultiplier - 1) * 5; // Damage increases by 5 each wave self.waveNumber = waveNumber; self.target = null; self.attackCooldown = 0; // Create health bar background (black) self.healthBarBg = LK.getAsset('blackBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 0.75 }); self.healthBarBg.x = 0; self.healthBarBg.y = -120; self.healthBarBg.tint = 0x000000; self.addChild(self.healthBarBg); // Create health bar (red for zombies) self.healthBar = LK.getAsset('blackBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 0.75 }); self.healthBar.x = 0; self.healthBar.y = -120; self.healthBar.tint = 0xff0000; self.addChild(self.healthBar); self.healthText = new Text2(self.health.toString(), { size: 50, fill: '#ffffff' }); self.healthText.anchor.set(0.5, 0.5); self.addChild(self.healthText); self.healthText.x = 0; self.healthText.y = -120; self.update = function () { if (self.attackCooldown > 0) { self.attackCooldown--; } if (!self.target) { self.findNearestPlayerTower(); } else { self.moveToTarget(); } // Attack nearby player units self.attackNearbyPlayerUnits(); self.healthText.setText(self.health.toString()); // Update health bar scale based on health percentage var healthPercentage = self.health / self.maxHealth; tween(self.healthBar, { scaleX: 1.5 * healthPercentage }, { duration: 200 }); }; self.findNearestPlayerTower = function () { // Target player tower if (playerTowers.length > 0) { self.target = playerTowers[0]; } }; 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 < 60) { self.attackTarget(); } else { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } }; self.attackTarget = function () { if (self.attackCooldown <= 0 && self.target) { self.target.health -= self.damage; self.attackCooldown = 60; LK.getSound('attack').play(); if (self.target.health <= 0) { // Tower destroyed var index = playerTowers.indexOf(self.target); if (index > -1) { playerTowers.splice(index, 1); self.target.destroy(); LK.getSound('towerDestroyed').play(); self.target = null; } } } }; self.attackNearbyPlayerUnits = function () { if (self.attackCooldown <= 0) { var meleeRange = 60; for (var i = 0; i < playerUnits.length; i++) { var unit = playerUnits[i]; var dx = unit.x - self.x; var dy = unit.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= meleeRange) { unit.health -= self.damage; self.attackCooldown = 60; LK.getSound('attack').play(); if (unit.health <= 0) { playerUnits.splice(i, 1); unit.destroy(); } break; } } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x228B22 }); /**** * Game Code ****/ // Game state management var gameState = 'menu'; // 'menu' or 'playing' // Add menu background var menuBackground = LK.getAsset('menuBg', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChild(menuBackground); // Create menu overlay var menuOverlay = new Container(); game.addChild(menuOverlay); // Add towers asset at the top of the menu var towersAsset = LK.getAsset('Towers', { anchorX: 0.5, anchorY: 0, scaleX: 4.0, scaleY: 4.0 }); towersAsset.x = 1024; towersAsset.y = 50; menuOverlay.addChild(towersAsset); // Add app banner at top of menu var appBanner = LK.getAsset('Appbanner', { anchorX: 0.5, anchorY: 0, x: 1024, y: 20 }); menuOverlay.addChild(appBanner); // Start button background var startButtonBg = LK.getAsset('blackBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 4.0, scaleY: 1.5 }); startButtonBg.x = 1024; startButtonBg.y = 1000; startButtonBg.tint = 0x228b22; menuOverlay.addChild(startButtonBg); // Start button image var startButton = LK.getAsset('StartGameButton', { anchorX: 0.5, anchorY: 0.5, scaleX: 4.0, scaleY: 2.0 }); startButton.x = 1024; startButton.y = 1000; menuOverlay.addChild(startButton); // How to Play button var howToPlayButton = LK.getAsset('Howtoplay22', { anchorX: 0.5, anchorY: 0.5, scaleX: 4.0, scaleY: 4.0 }); howToPlayButton.x = 1024; howToPlayButton.y = 1615; // 1.3x higher (2100 - 485 = 1615) menuOverlay.addChild(howToPlayButton); // Play menu music when game starts LK.playMusic('Menusong'); // How to Play instructions overlay var howToPlayOverlay = new Container(); howToPlayOverlay.visible = false; game.addChild(howToPlayOverlay); // How to Play background var howToPlayBg = LK.getAsset('blackBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 18.0, scaleY: 24.0 }); howToPlayBg.x = 1024; howToPlayBg.y = 1366; howToPlayBg.tint = 0x000000; howToPlayBg.alpha = 0.9; howToPlayOverlay.addChild(howToPlayBg); // How to Play title var howToPlayTitle = new Text2('HOW TO PLAY', { size: 100, fill: '#ffffff' }); howToPlayTitle.anchor.set(0.5, 0.5); howToPlayTitle.x = 1024; howToPlayTitle.y = 400; howToPlayOverlay.addChild(howToPlayTitle); // How to Play instructions var instructionsText = new Text2('NORMAL MODE:\n• Collect gold coins with workers\n• Spend gold to recruit units\n• Press Attack to command units to advance\n• Destroy enemy tower to win\n\nUNIT TYPES & STATS:\n• Worker: 5 gold, 10 HP, 5 damage, collects coins\n• Warrior: 10 gold, 20 HP, 10 damage, melee combat\n• Archer: 15 gold, 30 HP, 20 damage, ranged arrows\n• Wizard: 20 gold, 40 HP, 30 damage, magic attacks\n• Dragon: 25 gold, 50 HP, 40 damage, fire breath\n\nTOWERS:\n• Player Tower: 500 HP, 5 damage per second, attacks nearest enemy\n• AI Tower: 500 HP, 5 damage per second, attacks nearest enemy\n• Attack range: 400 pixels\n\nZOMBIE MODE:\n• Survive endless zombie waves\n• Units automatically attack zombies first\n• Zombies spawn 30 seconds after start\n• Wave 1: 5 zombies (30 HP, 10 damage)\n• Each wave: +2 zombies, +15 HP, +5 damage\n• Defend your tower at all costs!', { size: 42, fill: '#ffffff' }); instructionsText.anchor.set(0.5, 0.5); instructionsText.x = 1024; instructionsText.y = 1300; howToPlayOverlay.addChild(instructionsText); // Back button var backButton = new Text2('BACK TO MENU', { size: 80, fill: '#ffff00' }); backButton.anchor.set(0.5, 0.5); backButton.x = 1024; backButton.y = 2100; howToPlayOverlay.addChild(backButton); // Mode selection overlay var modeSelectionOverlay = new Container(); modeSelectionOverlay.visible = false; game.addChild(modeSelectionOverlay); // Mode selection background var modeSelectionBg = LK.getAsset('blackBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 18.0, scaleY: 24.0 }); modeSelectionBg.x = 1024; modeSelectionBg.y = 1366; modeSelectionBg.tint = 0x000000; modeSelectionBg.alpha = 0.9; modeSelectionOverlay.addChild(modeSelectionBg); // Mode selection title var modeSelectionTitle = new Text2('SELECT MODE', { size: 100, fill: '#ffffff' }); modeSelectionTitle.anchor.set(0.5, 0.5); modeSelectionTitle.x = 1024; modeSelectionTitle.y = 600; modeSelectionOverlay.addChild(modeSelectionTitle); // Normal mode button var normalModeButton = LK.getAsset('Normalmode', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.0, scaleY: 2.0 }); normalModeButton.x = 1024; normalModeButton.y = 1000; modeSelectionOverlay.addChild(normalModeButton); // Zombie mode button in mode selection var zombieModeSelectButton = LK.getAsset('Zombiemode', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.0, scaleY: 2.0 }); zombieModeSelectButton.x = 1024; zombieModeSelectButton.y = 1400; modeSelectionOverlay.addChild(zombieModeSelectButton); // Back to menu button from mode selection var backToMenuButton = new Text2('BACK TO MENU', { size: 80, fill: '#ffff00' }); backToMenuButton.anchor.set(0.5, 0.5); backToMenuButton.x = 1024; backToMenuButton.y = 2000; modeSelectionOverlay.addChild(backToMenuButton); // Start button event handler - now shows mode selection startButton.down = function () { menuOverlay.visible = false; modeSelectionOverlay.visible = true; }; startButtonBg.down = function () { menuOverlay.visible = false; modeSelectionOverlay.visible = true; }; // Normal mode button event handler normalModeButton.down = function () { zombieMode = false; gameState = 'playing'; modeSelectionOverlay.visible = false; LK.stopMusic(); // Stop menu music when starting game initializeGame(); }; // Zombie mode button event handler in mode selection zombieModeSelectButton.down = function () { zombieMode = true; gameState = 'playing'; modeSelectionOverlay.visible = false; LK.stopMusic(); initializeGame(); }; // Back to menu button event handler backToMenuButton.down = function () { modeSelectionOverlay.visible = false; menuOverlay.visible = true; }; // How to Play button event handler howToPlayButton.down = function () { menuOverlay.visible = false; howToPlayOverlay.visible = true; }; // Back button event handler backButton.down = function () { howToPlayOverlay.visible = false; menuOverlay.visible = true; }; // Add game background (initially hidden) var background = LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); background.visible = false; game.addChild(background); var playerCoins = 0; var aiCoins = 0; var playerTowers = []; var aiTowers = []; var coins = []; var playerCoins_array = []; var aiCoins_array = []; var playerUnits = []; var aiUnits = []; var arrows = []; var magics = []; var fires = []; var coinSpawnTimer = 0; var aiActionTimer = 0; var zombies = []; var zombieSpawnTimer = 0; var zombieMode = false; var zombieWaveNumber = 1; var zombieWaveSpawnTimer = 0; var zombieWaveInProgress = false; var zombiesInCurrentWave = 0; var zombiesToSpawnInWave = 0; // UI Elements var coinDisplay = new Text2('Golds: 0', { size: 80, fill: '#ffff00' }); coinDisplay.anchor.set(0.5, 0.5); coinDisplay.x = 200; coinDisplay.y = -273.75; coinDisplay.visible = false; LK.gui.bottom.addChild(coinDisplay); // Attack button var attackButton = new Text2('Attack', { size: 80, fill: '#ff0000' }); attackButton.anchor.set(0.5, 0.5); attackButton.x = -200; attackButton.y = -273.75; attackButton.visible = false; LK.gui.bottom.addChild(attackButton); var workerButton = new Text2('5', { size: 32, fill: '#ffffff' }); workerButton.anchor.set(0.5, 0.5); workerButton.x = -400; workerButton.y = -45; var warriorButton = new Text2('10', { size: 32, fill: '#ffffff' }); warriorButton.anchor.set(0.5, 0.5); warriorButton.x = -200; warriorButton.y = -45; var wizardButton = new Text2('20', { size: 32, fill: '#ffffff' }); wizardButton.anchor.set(0.5, 0.5); wizardButton.x = 200; wizardButton.y = -45; var archerButton = new Text2('15', { size: 32, fill: '#ffffff' }); archerButton.anchor.set(0.5, 0.5); archerButton.x = 0; archerButton.y = -45; var dragonButton = new Text2('25', { size: 32, fill: '#ffffff' }); dragonButton.anchor.set(0.5, 0.5); dragonButton.x = 400; dragonButton.y = -45; // Add unified black background behind all recruitment buttons and unit images var unifiedBackground = LK.getAsset('blackBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 22.0, scaleY: 2.8 }); unifiedBackground.x = 0; unifiedBackground.y = -78; unifiedBackground.tint = 0x000000; unifiedBackground.alpha = 0.5; unifiedBackground.visible = false; LK.gui.bottom.addChild(unifiedBackground); // Add unit images above recruitment buttons var workerImage = LK.getAsset('worker', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0 }); workerImage.x = -400; workerImage.y = -110; LK.gui.bottom.addChild(workerImage); var warriorImage = LK.getAsset('warrior', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0 }); warriorImage.x = -200; warriorImage.y = -110; LK.gui.bottom.addChild(warriorImage); var wizardImage = LK.getAsset('wizard', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0 }); wizardImage.x = 200; wizardImage.y = -110; LK.gui.bottom.addChild(wizardImage); var archerImage = LK.getAsset('archer', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0 }); archerImage.x = 0; archerImage.y = -110; LK.gui.bottom.addChild(archerImage); var dragonImage = LK.getAsset('dragon', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0 }); dragonImage.x = 400; dragonImage.y = -110; LK.gui.bottom.addChild(dragonImage); // Wave display for zombie mode var waveDisplay = new Text2('Wave: 1', { size: 80, fill: '#ff0000' }); waveDisplay.anchor.set(0.5, 0.5); waveDisplay.x = 0; waveDisplay.y = 100; waveDisplay.visible = false; LK.gui.top.addChild(waveDisplay); // Hide all UI elements initially (menu state) workerButton.visible = false; warriorButton.visible = false; wizardButton.visible = false; archerButton.visible = false; dragonButton.visible = false; workerImage.visible = false; warriorImage.visible = false; wizardImage.visible = false; archerImage.visible = false; dragonImage.visible = false; // Add texts after backgrounds so they appear on top LK.gui.bottom.addChild(workerButton); LK.gui.bottom.addChild(warriorButton); LK.gui.bottom.addChild(wizardButton); LK.gui.bottom.addChild(archerButton); LK.gui.bottom.addChild(dragonButton); // Initialize towers function initializeTowers() { // Player tower var playerTower = new Tower('player'); playerTower.x = 1024; playerTower.y = 2300; playerTowers.push(playerTower); game.addChild(playerTower); // AI tower (only in normal mode) if (!zombieMode) { var aiTower = new Tower('ai'); aiTower.x = 1024; aiTower.y = 400; aiTowers.push(aiTower); game.addChild(aiTower); } } ; function spawnCoin() { // Spawn player coin var playerCoin = new Coin(); playerCoin.x = Math.random() * 1800 + 124; playerCoin.y = Math.random() * 1000 + 1200; // Lower half for player playerCoins_array.push(playerCoin); coins.push(playerCoin); game.addChild(playerCoin); // Spawn AI coin var aiCoin = new Coin(); aiCoin.x = Math.random() * 1800 + 124; aiCoin.y = Math.random() * 1000 + 400; // Upper half for AI aiCoins_array.push(aiCoin); coins.push(aiCoin); game.addChild(aiCoin); } function spawnUnit(team, unitType) { var unit; var cost = unitType === 'worker' ? 5 : unitType === 'warrior' ? 10 : unitType === 'wizard' ? 20 : unitType === 'archer' ? 15 : 25; if (team === 'player' && playerCoins >= cost) { playerCoins -= cost; if (unitType === 'worker') { unit = new Worker('player'); } else { unit = new CombatUnit('player', unitType); } if (!zombieMode && unitType !== 'worker') { // Position combat units in front of tower in normal mode with better spacing var unitCount = playerUnits.length; var spreadRadius = 300; var angle = unitCount * 0.5 % (Math.PI * 2); unit.x = playerTowers[0].x + Math.cos(angle) * spreadRadius; unit.y = playerTowers[0].y - 507 + Math.sin(angle) * 100; } else { var unitCount = playerUnits.length; var spreadRadius = 200; var angle = unitCount * 0.8 % (Math.PI * 2); unit.x = playerTowers[0].x + Math.cos(angle) * spreadRadius; unit.y = playerTowers[0].y - 100 + Math.sin(angle) * 50; } playerUnits.push(unit); game.addChild(unit); LK.getSound('unitSpawn').play(); } else if (team === 'ai' && aiCoins >= cost) { aiCoins -= cost; if (unitType === 'worker') { unit = new Worker('ai'); } else { unit = new CombatUnit('ai', unitType); } var unitCount = aiUnits.length; var spreadRadius = 200; var angle = unitCount * 0.8 % (Math.PI * 2); unit.x = aiTowers[0].x + Math.cos(angle) * spreadRadius; unit.y = aiTowers[0].y + 100 + Math.sin(angle) * 50; aiUnits.push(unit); game.addChild(unit); } } function spawnZombie() { var zombie = new Zombie(zombieWaveNumber); zombie.x = Math.random() * 1800 + 124; // Random X position across screen width zombie.y = 50; // Spawn at top of screen zombies.push(zombie); game.addChild(zombie); } function startZombieWave() { zombieWaveInProgress = true; zombiesToSpawnInWave = 3 + zombieWaveNumber * 2; // Wave 1: 5 zombies, Wave 2: 7 zombies, etc. zombiesInCurrentWave = 0; zombieWaveSpawnTimer = 0; // Update wave display if (zombieMode) { waveDisplay.setText('Wave: ' + zombieWaveNumber); } } function checkWaveComplete() { // Check if all zombies in current wave are spawned and destroyed if (zombieWaveInProgress && zombiesInCurrentWave >= zombiesToSpawnInWave && zombies.length === 0) { zombieWaveInProgress = false; zombieWaveNumber++; // Start next wave after 10 seconds (doubled from 5 seconds) zombieWaveSpawnTimer = -600; // 10 seconds delay before next wave } } function aiLogic() { if (aiActionTimer <= 0) { if (aiCoins >= 25 && Math.random() < 0.1) { spawnUnit('ai', 'dragon'); } else if (aiCoins >= 15 && Math.random() < 0.15) { spawnUnit('ai', 'archer'); } else if (aiCoins >= 20 && Math.random() < 0.2) { spawnUnit('ai', 'wizard'); } else if (aiCoins >= 10 && Math.random() < 0.3) { spawnUnit('ai', 'warrior'); } else if (aiCoins >= 5 && Math.random() < 0.4) { spawnUnit('ai', 'worker'); } aiActionTimer = 120; } aiActionTimer--; } function checkGameEnd() { if (playerTowers.length === 0) { LK.showGameOver(); } else if (!zombieMode && aiTowers.length === 0) { LK.showYouWin(); } } function cleanupCollectedCoins() { // Clean up from main coins array for (var i = coins.length - 1; i >= 0; i--) { if (coins[i].collected) { coins[i].destroy(); coins.splice(i, 1); } } // Clean up from player coins array for (var i = playerCoins_array.length - 1; i >= 0; i--) { if (playerCoins_array[i].collected) { playerCoins_array.splice(i, 1); } } // Clean up from AI coins array for (var i = aiCoins_array.length - 1; i >= 0; i--) { if (aiCoins_array[i].collected) { aiCoins_array.splice(i, 1); } } } // Button event handlers workerButton.down = function () { spawnUnit('player', 'worker'); }; warriorButton.down = function () { spawnUnit('player', 'warrior'); }; wizardButton.down = function () { spawnUnit('player', 'wizard'); }; archerButton.down = function () { spawnUnit('player', 'archer'); }; dragonButton.down = function () { spawnUnit('player', 'dragon'); }; // Character image event handlers workerImage.down = function () { spawnUnit('player', 'worker'); }; warriorImage.down = function () { spawnUnit('player', 'warrior'); }; wizardImage.down = function () { spawnUnit('player', 'wizard'); }; archerImage.down = function () { spawnUnit('player', 'archer'); }; dragonImage.down = function () { spawnUnit('player', 'dragon'); }; // Attack button event handler attackButton.down = function () { if (!zombieMode) { // Make all waiting player units start attacking for (var i = 0; i < playerUnits.length; i++) { var unit = playerUnits[i]; if (unit.unitType !== 'worker') { unit.isWaiting = false; } } } }; // Initialize game function function initializeGame() { background.visible = true; // Show UI elements when game starts coinDisplay.visible = true; unifiedBackground.visible = true; workerButton.visible = true; warriorButton.visible = true; wizardButton.visible = true; archerButton.visible = true; dragonButton.visible = true; workerImage.visible = true; warriorImage.visible = true; wizardImage.visible = true; archerImage.visible = true; dragonImage.visible = true; // Show attack button only in normal mode if (!zombieMode) { attackButton.visible = true; } else { // Show wave display in zombie mode waveDisplay.visible = true; waveDisplay.setText('Wave: ' + zombieWaveNumber); } // Reset zombie wave system zombieWaveNumber = 1; zombieWaveSpawnTimer = -1800; // 30 second delay (30 * 60 FPS = 1800 frames) zombieWaveInProgress = false; zombiesInCurrentWave = 0; zombiesToSpawnInWave = 0; // Play game music when match starts LK.playMusic('Song'); initializeTowers(); // Spawn initial workers for both teams var playerWorker = new Worker('player'); playerWorker.x = playerTowers[0].x + 50; playerWorker.y = playerTowers[0].y - 100; playerUnits.push(playerWorker); game.addChild(playerWorker); // AI worker (only in normal mode) if (!zombieMode) { var aiWorker = new Worker('ai'); aiWorker.x = aiTowers[0].x + 50; aiWorker.y = aiTowers[0].y + 100; aiUnits.push(aiWorker); game.addChild(aiWorker); } } // Main game loop game.update = function () { if (gameState === 'playing') { // Update coin display coinDisplay.setText('Golds: ' + playerCoins); // Spawn coins coinSpawnTimer++; if (coinSpawnTimer >= 36) { // 5x more frequent (180/5 = 36) spawnCoin(); coinSpawnTimer = 0; } // AI logic (only in normal mode) if (!zombieMode) { aiLogic(); } // Clean up collected coins cleanupCollectedCoins(); // Zombie mode logic if (zombieMode) { zombieWaveSpawnTimer++; // Start first wave or next wave after delay if (!zombieWaveInProgress && zombieWaveSpawnTimer >= 0) { startZombieWave(); } // Spawn zombies in current wave if (zombieWaveInProgress && zombiesInCurrentWave < zombiesToSpawnInWave) { if (zombieWaveSpawnTimer % 60 === 0) { // Spawn one zombie every second during wave spawnZombie(); zombiesInCurrentWave++; } } // Check if current wave is complete checkWaveComplete(); // Clean up off-screen zombies for (var i = zombies.length - 1; i >= 0; i--) { if (zombies[i].y > 2800) { zombies[i].destroy(); zombies.splice(i, 1); } } } // Check game end conditions checkGameEnd(); } };
/****
* Plugins
****/
var storage = LK.import("@upit/storage.v1");
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Arrow = Container.expand(function (team, startX, startY, targetX, targetY, damage) {
var self = Container.call(this);
var graphics = self.attachAsset('Arrow', {
anchorX: 0.5,
anchorY: 0.5
});
self.team = team;
self.damage = damage;
self.speed = 8;
self.target = null;
// Calculate direction
var dx = targetX - startX;
var dy = targetY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
self.directionX = dx / distance;
self.directionY = dy / distance;
// Set rotation to point toward target
self.rotation = Math.atan2(dy, dx);
// Position arrow at start
self.x = startX;
self.y = startY;
self.update = function () {
// Move arrow
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
// Check if arrow is off screen
if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) {
self.destroy();
// Remove from arrows array
for (var i = arrows.length - 1; i >= 0; i--) {
if (arrows[i] === self) {
arrows.splice(i, 1);
break;
}
}
}
// Check collision with enemy units
var enemyUnits = self.team === 'player' ? aiUnits : playerUnits;
for (var i = 0; i < enemyUnits.length; i++) {
var enemy = enemyUnits[i];
if (self.intersects(enemy)) {
enemy.health -= self.damage;
if (enemy.health <= 0) {
enemyUnits.splice(i, 1);
enemy.destroy();
}
self.destroy();
// Remove from arrows array
for (var j = arrows.length - 1; j >= 0; j--) {
if (arrows[j] === self) {
arrows.splice(j, 1);
break;
}
}
return;
}
}
// Check collision with enemy towers
var enemyTowers = self.team === 'player' ? aiTowers : playerTowers;
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (self.intersects(tower)) {
tower.health -= self.damage;
if (tower.health <= 0) {
enemyTowers.splice(i, 1);
tower.destroy();
LK.getSound('towerDestroyed').play();
}
self.destroy();
// Remove from arrows array
for (var j = arrows.length - 1; j >= 0; j--) {
if (arrows[j] === self) {
arrows.splice(j, 1);
break;
}
}
return;
}
}
};
return self;
});
var Coin = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.value = 1;
self.collected = false;
return self;
});
var CombatUnit = Container.expand(function (team, unitType) {
var self = Container.call(this);
var assetName = team === 'player' ? unitType : 'ai' + unitType.charAt(0).toUpperCase() + unitType.slice(1);
var graphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.team = team;
self.unitType = unitType;
self.speed = unitType === 'dragon' ? 3 : unitType === 'wizard' ? 1.5 : unitType === 'archer' ? 2.5 : 2;
self.damage = unitType === 'dragon' ? 40 : unitType === 'wizard' ? 30 : unitType === 'archer' ? 20 : 10;
self.health = unitType === 'dragon' ? 50 : unitType === 'wizard' ? 40 : unitType === 'archer' ? 30 : unitType === 'warrior' ? 20 : 10;
self.maxHealth = self.health;
self.attackRange = unitType === 'wizard' ? 100 : unitType === 'archer' ? 200 : 50;
self.attackCooldown = 0;
self.target = null;
self.isWaiting = !zombieMode && team === 'player'; // Only player units wait in normal mode
// Create health bar background (black)
self.healthBarBg = LK.getAsset('blackBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.75
});
self.healthBarBg.x = 0;
self.healthBarBg.y = -120;
self.healthBarBg.tint = team === 'player' ? 0x0000ff : 0x000000;
self.addChild(self.healthBarBg);
// Create health bar (green for player, red for AI)
self.healthBar = LK.getAsset('blackBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.75
});
self.healthBar.x = 0;
self.healthBar.y = -120;
self.healthBar.tint = team === 'player' ? 0x00ff00 : 0xff0000;
self.addChild(self.healthBar);
self.healthText = new Text2(self.health.toString(), {
size: 50,
fill: '#ffffff'
});
self.healthText.anchor.set(0.5, 0.5);
self.addChild(self.healthText);
self.healthText.x = 0;
self.healthText.y = -120;
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
// In zombie mode, player units prioritize attacking zombies
if (zombieMode && self.team === 'player' && self.unitType !== 'worker') {
self.attackZombies();
} else if (!zombieMode && self.team === 'player' && self.isWaiting) {
// In normal mode, player units wait unless attack is triggered
// Stay in position and don't move
} else {
if (!self.target) {
self.findNearestEnemyTower();
} else {
self.moveToTarget();
}
// Combat with nearby enemy units
self.combatNearbyEnemies();
}
self.healthText.setText(self.health.toString());
// Update health bar scale based on health percentage
var healthPercentage = self.health / self.maxHealth;
tween(self.healthBar, {
scaleX: 1.5 * healthPercentage
}, {
duration: 200
});
};
self.findNearestEnemyTower = function () {
// In zombie mode, player units should prioritize attacking zombies
if (zombieMode && self.team === 'player' && self.unitType !== 'worker') {
var nearestZombie = null;
var nearestDistance = Infinity;
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var distance = Math.sqrt(Math.pow(zombie.x - self.x, 2) + Math.pow(zombie.y - self.y, 2));
if (distance < nearestDistance) {
nearestDistance = distance;
nearestZombie = zombie;
}
}
// If we found a zombie, target it
if (nearestZombie) {
self.target = nearestZombie;
return;
}
}
// Find nearest target - prioritize ALL enemy units first (including workers), then towers
var nearestTarget = null;
var nearestDistance = Infinity;
// First check all enemy units (including workers)
var enemyUnits = self.team === 'player' ? aiUnits : playerUnits;
for (var i = 0; i < enemyUnits.length; i++) {
var enemy = enemyUnits[i];
// Target all enemy units now, including workers
if (self.unitType !== 'worker') {
var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2));
if (distance < nearestDistance) {
nearestDistance = distance;
nearestTarget = enemy;
}
}
}
// Only target towers if no enemy units remain
if (!nearestTarget) {
var enemyTowers = self.team === 'player' ? aiTowers : playerTowers;
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < nearestDistance) {
nearestDistance = distance;
nearestTarget = tower;
}
}
}
self.target = nearestTarget;
};
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 < self.attackRange) {
self.attackTarget();
} else {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
self.attackTarget = function () {
if (self.attackCooldown <= 0 && self.target) {
if (self.unitType === 'archer') {
// Archer shoots arrow
var arrow = new Arrow(self.team, self.x, self.y, self.target.x, self.target.y, self.damage);
arrows.push(arrow);
game.addChild(arrow);
} else if (self.unitType === 'wizard') {
// Wizard shoots magic
var magic = new Magic(self.team, self.x, self.y, self.target.x, self.target.y, self.damage);
magics.push(magic);
game.addChild(magic);
} else if (self.unitType === 'dragon') {
// Change to attack asset for all dragon attacks
self.removeChild(graphics);
var assetName = 'DragonAttack';
graphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Dragon shoots fire with 1 damage
var fire = new Fire(self.team, self.x, self.y, self.target.x, self.target.y, 1);
fires.push(fire);
game.addChild(fire);
} else {
// Melee attack
self.target.health -= self.damage;
if (self.target.health <= 0) {
// Check if target is a unit or tower
if (self.target.unitType) {
// Target is a unit
self.destroyUnit();
} else {
// Target is a tower
self.destroyTower();
}
}
}
// Set dragon attack cooldown to 3 frames (0.05 seconds)
if (self.unitType === 'dragon') {
self.attackCooldown = 3;
} else {
self.attackCooldown = 60;
}
LK.getSound('attack').play();
}
};
self.destroyUnit = function () {
var units = self.team === 'player' ? aiUnits : playerUnits;
var index = units.indexOf(self.target);
if (index > -1) {
units.splice(index, 1);
self.target.destroy();
self.target = null;
}
};
self.destroyTower = function () {
var towers = self.team === 'player' ? aiTowers : playerTowers;
var index = towers.indexOf(self.target);
if (index > -1) {
towers.splice(index, 1);
self.target.destroy();
LK.getSound('towerDestroyed').play();
self.target = null;
}
};
self.attackZombies = function () {
// Find nearest zombie to target
var nearestZombie = null;
var nearestDistance = Infinity;
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var dx = zombie.x - self.x;
var dy = zombie.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestZombie = zombie;
}
}
// Move toward and attack nearest zombie
if (nearestZombie) {
var dx = nearestZombie.x - self.x;
var dy = nearestZombie.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Move toward zombie if not in attack range
if (distance > self.attackRange) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else if (self.attackCooldown <= 0) {
// Attack zombie based on unit type
if (self.unitType === 'archer') {
// Archer shoots arrow at zombie
var arrow = new Arrow(self.team, self.x, self.y, nearestZombie.x, nearestZombie.y, self.damage);
arrows.push(arrow);
game.addChild(arrow);
} else if (self.unitType === 'wizard') {
// Wizard shoots magic at zombie
var magic = new Magic(self.team, self.x, self.y, nearestZombie.x, nearestZombie.y, self.damage);
magics.push(magic);
game.addChild(magic);
} else if (self.unitType === 'dragon') {
// Change to attack asset for dragon zombie attacks
self.removeChild(graphics);
var assetName = 'DragonAttack';
graphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Dragon shoots fire at zombie
var fire = new Fire(self.team, self.x, self.y, nearestZombie.x, nearestZombie.y, self.damage);
fires.push(fire);
game.addChild(fire);
} else {
// Melee attack (warrior)
nearestZombie.health -= self.damage;
if (nearestZombie.health <= 0) {
for (var j = 0; j < zombies.length; j++) {
if (zombies[j] === nearestZombie) {
zombies.splice(j, 1);
nearestZombie.destroy();
break;
}
}
}
}
self.attackCooldown = 60; // 1 second cooldown
LK.getSound('attack').play();
}
}
};
self.combatNearbyEnemies = function () {
if (self.attackCooldown <= 0) {
var enemyUnits = self.team === 'player' ? aiUnits : playerUnits;
var meleeRange = 60; // Close combat range
for (var i = 0; i < enemyUnits.length; i++) {
var enemy = enemyUnits[i];
// Skip workers in combat
if (enemy.unitType === 'worker') {
continue;
}
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// If enemy unit is within melee range, attack it
if (distance <= meleeRange) {
enemy.health -= self.damage;
self.attackCooldown = 60; // 1 second cooldown
LK.getSound('attack').play();
// Check if enemy is destroyed
if (enemy.health <= 0) {
enemyUnits.splice(i, 1);
enemy.destroy();
}
break; // Only attack one enemy per cycle
}
}
}
};
return self;
});
var Fire = Container.expand(function (team, startX, startY, targetX, targetY, damage) {
var self = Container.call(this);
var graphics = self.attachAsset('Magic', {
anchorX: 0.5,
anchorY: 0.5
});
self.team = team;
self.damage = damage;
self.speed = 4;
self.target = null;
// Calculate direction
var dx = targetX - startX;
var dy = targetY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
self.directionX = dx / distance;
self.directionY = dy / distance;
// Position fire at start
self.x = startX;
self.y = startY;
// Add fire effect - orange/red tint and scaling
graphics.tint = 0xff4500; // Orange-red fire color
tween(graphics, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut
});
self.update = function () {
// Move fire
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
// Add fire flicker rotation
graphics.rotation += 0.15;
// Check if fire is off screen
if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) {
self.destroy();
// Remove from fires array
for (var i = fires.length - 1; i >= 0; i--) {
if (fires[i] === self) {
fires.splice(i, 1);
break;
}
}
}
// Check collision with enemy units
var enemyUnits = self.team === 'player' ? aiUnits : playerUnits;
for (var i = 0; i < enemyUnits.length; i++) {
var enemy = enemyUnits[i];
if (self.intersects(enemy)) {
enemy.health -= self.damage;
if (enemy.health <= 0) {
enemyUnits.splice(i, 1);
enemy.destroy();
}
self.destroy();
// Remove from fires array
for (var j = fires.length - 1; j >= 0; j--) {
if (fires[j] === self) {
fires.splice(j, 1);
break;
}
}
return;
}
}
// Check collision with enemy towers
var enemyTowers = self.team === 'player' ? aiTowers : playerTowers;
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (self.intersects(tower)) {
tower.health -= self.damage;
if (tower.health <= 0) {
enemyTowers.splice(i, 1);
tower.destroy();
LK.getSound('towerDestroyed').play();
}
self.destroy();
// Remove from fires array
for (var j = fires.length - 1; j >= 0; j--) {
if (fires[j] === self) {
fires.splice(j, 1);
break;
}
}
return;
}
}
};
return self;
});
var Magic = Container.expand(function (team, startX, startY, targetX, targetY, damage) {
var self = Container.call(this);
var graphics = self.attachAsset('Magic', {
anchorX: 0.5,
anchorY: 0.5
});
self.team = team;
self.damage = damage;
self.speed = 6;
self.target = null;
// Calculate direction
var dx = targetX - startX;
var dy = targetY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
self.directionX = dx / distance;
self.directionY = dy / distance;
// Position magic at start
self.x = startX;
self.y = startY;
// Add magical glow effect
tween(graphics, {
tint: 0x9966ff
}, {
duration: 500,
easing: tween.easeInOut
});
tween(graphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.easeOut
});
self.update = function () {
// Move magic
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
// Add sparkle rotation
graphics.rotation += 0.1;
// Check if magic is off screen
if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) {
self.destroy();
// Remove from magics array
for (var i = magics.length - 1; i >= 0; i--) {
if (magics[i] === self) {
magics.splice(i, 1);
break;
}
}
}
// Check collision with enemy units
var enemyUnits = self.team === 'player' ? aiUnits : playerUnits;
for (var i = 0; i < enemyUnits.length; i++) {
var enemy = enemyUnits[i];
if (self.intersects(enemy)) {
enemy.health -= self.damage;
if (enemy.health <= 0) {
enemyUnits.splice(i, 1);
enemy.destroy();
}
self.destroy();
// Remove from magics array
for (var j = magics.length - 1; j >= 0; j--) {
if (magics[j] === self) {
magics.splice(j, 1);
break;
}
}
return;
}
}
// Check collision with enemy towers
var enemyTowers = self.team === 'player' ? aiTowers : playerTowers;
for (var i = 0; i < enemyTowers.length; i++) {
var tower = enemyTowers[i];
if (self.intersects(tower)) {
tower.health -= self.damage;
if (tower.health <= 0) {
enemyTowers.splice(i, 1);
tower.destroy();
LK.getSound('towerDestroyed').play();
}
self.destroy();
// Remove from magics array
for (var j = magics.length - 1; j >= 0; j--) {
if (magics[j] === self) {
magics.splice(j, 1);
break;
}
}
return;
}
}
};
return self;
});
var Tower = Container.expand(function (team) {
var self = Container.call(this);
var assetName = team === 'player' ? 'playerTower' : 'aiTower';
var graphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 1.0
});
self.team = team;
self.health = 500;
self.maxHealth = 500;
// Create health bar background (black)
self.healthBarBg = LK.getAsset('blackBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.08,
scaleY: 0.625
});
self.healthBarBg.x = 0;
self.healthBarBg.y = -262.5;
self.healthBarBg.tint = 0x0000ff;
self.addChild(self.healthBarBg);
// Create health bar (green for player, red for AI)
self.healthBar = LK.getAsset('blackBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.08,
scaleY: 0.625
});
self.healthBar.x = 0;
self.healthBar.y = -262.5;
self.healthBar.tint = team === 'player' ? 0x00ff00 : 0xff0000;
self.addChild(self.healthBar);
self.healthText = new Text2(self.health.toString(), {
size: 42,
fill: '#ffffff'
});
self.healthText.anchor.set(0.5, 0.5);
self.addChild(self.healthText);
self.healthText.x = 0;
self.healthText.y = -262.5;
self.attackCooldown = 0;
self.attackRange = 400;
// Damage area outline removed as requested
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
self.attackNearbyEnemies();
self.healthText.setText(self.health.toString());
// Update health bar scale based on health percentage
var healthPercentage = self.health / self.maxHealth;
tween(self.healthBar, {
scaleX: 2.08 * healthPercentage
}, {
duration: 200
});
};
self.attackNearbyEnemies = function () {
if (self.attackCooldown <= 0) {
var enemyUnits = self.team === 'player' ? aiUnits : playerUnits;
var nearestEnemy = null;
var nearestDistance = Infinity;
// Find nearest enemy unit within attack range
for (var i = 0; i < enemyUnits.length; i++) {
var enemy = enemyUnits[i];
var dx = enemy.x - self.x;
var dy = enemy.y - (self.y - 131.25); // Adjust for tower center position
var distance = Math.sqrt(dx * dx + dy * dy);
// Only consider units within the attack range
if (distance <= self.attackRange && distance < nearestDistance) {
nearestDistance = distance;
nearestEnemy = enemy;
}
}
// Attack only the nearest enemy if found
if (nearestEnemy) {
nearestEnemy.health -= 1; // Deal 1 damage as specified
self.attackCooldown = 3; // 0.05 seconds at 60 FPS
LK.getSound('attack').play();
if (nearestEnemy.health <= 0) {
var index = enemyUnits.indexOf(nearestEnemy);
if (index > -1) {
self.destroyUnit(nearestEnemy, enemyUnits, index);
}
}
}
// In zombie mode, player towers also attack zombies (find nearest zombie)
if (zombieMode && self.team === 'player') {
var nearestZombie = null;
var nearestZombieDistance = Infinity;
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var dx = zombie.x - self.x;
var dy = zombie.y - (self.y - 131.25);
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.attackRange && distance < nearestZombieDistance) {
nearestZombieDistance = distance;
nearestZombie = zombie;
}
}
// Attack only the nearest zombie if found
if (nearestZombie && !nearestEnemy) {
// Only attack zombie if no enemy unit was attacked
nearestZombie.health -= 1;
self.attackCooldown = 3;
LK.getSound('attack').play();
if (nearestZombie.health <= 0) {
var index = zombies.indexOf(nearestZombie);
if (index > -1) {
zombies.splice(index, 1);
nearestZombie.destroy();
}
}
}
}
}
};
self.showDamageArea = function () {
// No visual effects when towers attack
};
self.destroyUnit = function (unit, unitsArray, index) {
unitsArray.splice(index, 1);
unit.destroy();
};
return self;
});
var Worker = Container.expand(function (team) {
var self = Container.call(this);
var assetName = team === 'player' ? 'worker' : 'aiWorker';
var graphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.team = team;
self.speed = 4;
self.target = null;
self.collectRange = 40;
self.health = 10;
self.maxHealth = 10;
// Create health bar background (black)
self.healthBarBg = LK.getAsset('blackBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.75
});
self.healthBarBg.x = 0;
self.healthBarBg.y = -120;
self.healthBarBg.tint = 0x000000;
self.addChild(self.healthBarBg);
// Create health bar (green for player, red for AI)
self.healthBar = LK.getAsset('blackBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.75
});
self.healthBar.x = 0;
self.healthBar.y = -120;
self.healthBar.tint = team === 'player' ? 0x00ff00 : 0xff0000;
self.addChild(self.healthBar);
self.healthText = new Text2(self.health.toString(), {
size: 50,
fill: '#ffffff'
});
self.healthText.anchor.set(0.5, 0.5);
self.addChild(self.healthText);
self.healthText.x = 0;
self.healthText.y = -120;
self.update = function () {
if (!self.target) {
self.findNearestCoin();
} else {
self.moveToTarget();
}
// Workers can defend themselves
self.defendSelf();
self.healthText.setText(self.health.toString());
// Update health bar scale based on health percentage
var healthPercentage = self.health / self.maxHealth;
tween(self.healthBar, {
scaleX: 1.5 * healthPercentage
}, {
duration: 200
});
};
self.findNearestCoin = function () {
var targetCoins = self.team === 'player' ? playerCoins_array : aiCoins_array;
var teamWorkers = self.team === 'player' ? playerUnits : aiUnits;
var availableCoins = [];
// First, collect all untargeted coins
for (var i = 0; i < targetCoins.length; i++) {
var coin = targetCoins[i];
if (!coin.collected) {
// Check if another worker is already targeting this coin
var isTargeted = false;
for (var j = 0; j < teamWorkers.length; j++) {
var worker = teamWorkers[j];
if (worker.unitType === 'worker' && worker !== self && worker.target === coin) {
isTargeted = true;
break;
}
}
if (!isTargeted) {
availableCoins.push(coin);
}
}
}
// If no available coins, clear target
if (availableCoins.length === 0) {
self.target = null;
return;
}
// Get worker index to create unique targeting preference
var workerIndex = -1;
for (var i = 0; i < teamWorkers.length; i++) {
if (teamWorkers[i] === self && teamWorkers[i].unitType === 'worker') {
workerIndex = i;
break;
}
}
// Use worker index to create different targeting preferences
var targetCoin = null;
if (workerIndex >= 0 && workerIndex < availableCoins.length) {
// Each worker gets a different coin based on their index
targetCoin = availableCoins[workerIndex];
} else {
// If more workers than coins, use modulo to cycle through available coins
var coinIndex = workerIndex % availableCoins.length;
targetCoin = availableCoins[coinIndex];
}
// If targeted approach fails, fall back to nearest coin
if (!targetCoin) {
var nearestCoin = null;
var nearestDistance = Infinity;
for (var i = 0; i < availableCoins.length; i++) {
var coin = availableCoins[i];
var distance = Math.sqrt(Math.pow(coin.x - self.x, 2) + Math.pow(coin.y - self.y, 2));
if (distance < nearestDistance) {
nearestDistance = distance;
nearestCoin = coin;
}
}
targetCoin = nearestCoin;
}
self.target = targetCoin;
};
self.moveToTarget = function () {
if (!self.target || self.target.collected) {
self.target = null;
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 < self.collectRange) {
self.collectCoin();
} else {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
self.collectCoin = function () {
if (self.target && !self.target.collected) {
self.target.collected = true;
if (self.team === 'player') {
playerCoins += self.target.value;
} else {
aiCoins += self.target.value;
}
LK.getSound('coinCollect').play();
self.target = null;
}
};
self.defendSelf = function () {
var enemyUnits = self.team === 'player' ? aiUnits : playerUnits;
var defenseRange = 50;
for (var i = 0; i < enemyUnits.length; i++) {
var enemy = enemyUnits[i];
// Workers only defend against other workers
if (enemy.unitType === 'worker') {
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= defenseRange) {
enemy.health -= 5; // Workers do less damage
LK.getSound('attack').play();
if (enemy.health <= 0) {
enemyUnits.splice(i, 1);
enemy.destroy();
}
break;
}
}
}
};
return self;
});
var Zombie = Container.expand(function (waveNumber) {
var self = Container.call(this);
var graphics = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
// Stats increase with wave number
var waveMultiplier = waveNumber || 1;
self.speed = 1.5 + (waveMultiplier - 1) * 0.2; // Speed increases slightly each wave
self.health = 30 + (waveMultiplier - 1) * 15; // Health increases by 15 each wave
self.maxHealth = self.health;
self.damage = 10 + (waveMultiplier - 1) * 5; // Damage increases by 5 each wave
self.waveNumber = waveNumber;
self.target = null;
self.attackCooldown = 0;
// Create health bar background (black)
self.healthBarBg = LK.getAsset('blackBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.75
});
self.healthBarBg.x = 0;
self.healthBarBg.y = -120;
self.healthBarBg.tint = 0x000000;
self.addChild(self.healthBarBg);
// Create health bar (red for zombies)
self.healthBar = LK.getAsset('blackBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.75
});
self.healthBar.x = 0;
self.healthBar.y = -120;
self.healthBar.tint = 0xff0000;
self.addChild(self.healthBar);
self.healthText = new Text2(self.health.toString(), {
size: 50,
fill: '#ffffff'
});
self.healthText.anchor.set(0.5, 0.5);
self.addChild(self.healthText);
self.healthText.x = 0;
self.healthText.y = -120;
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
if (!self.target) {
self.findNearestPlayerTower();
} else {
self.moveToTarget();
}
// Attack nearby player units
self.attackNearbyPlayerUnits();
self.healthText.setText(self.health.toString());
// Update health bar scale based on health percentage
var healthPercentage = self.health / self.maxHealth;
tween(self.healthBar, {
scaleX: 1.5 * healthPercentage
}, {
duration: 200
});
};
self.findNearestPlayerTower = function () {
// Target player tower
if (playerTowers.length > 0) {
self.target = playerTowers[0];
}
};
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 < 60) {
self.attackTarget();
} else {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
self.attackTarget = function () {
if (self.attackCooldown <= 0 && self.target) {
self.target.health -= self.damage;
self.attackCooldown = 60;
LK.getSound('attack').play();
if (self.target.health <= 0) {
// Tower destroyed
var index = playerTowers.indexOf(self.target);
if (index > -1) {
playerTowers.splice(index, 1);
self.target.destroy();
LK.getSound('towerDestroyed').play();
self.target = null;
}
}
}
};
self.attackNearbyPlayerUnits = function () {
if (self.attackCooldown <= 0) {
var meleeRange = 60;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var dx = unit.x - self.x;
var dy = unit.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= meleeRange) {
unit.health -= self.damage;
self.attackCooldown = 60;
LK.getSound('attack').play();
if (unit.health <= 0) {
playerUnits.splice(i, 1);
unit.destroy();
}
break;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228B22
});
/****
* Game Code
****/
// Game state management
var gameState = 'menu'; // 'menu' or 'playing'
// Add menu background
var menuBackground = LK.getAsset('menuBg', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(menuBackground);
// Create menu overlay
var menuOverlay = new Container();
game.addChild(menuOverlay);
// Add towers asset at the top of the menu
var towersAsset = LK.getAsset('Towers', {
anchorX: 0.5,
anchorY: 0,
scaleX: 4.0,
scaleY: 4.0
});
towersAsset.x = 1024;
towersAsset.y = 50;
menuOverlay.addChild(towersAsset);
// Add app banner at top of menu
var appBanner = LK.getAsset('Appbanner', {
anchorX: 0.5,
anchorY: 0,
x: 1024,
y: 20
});
menuOverlay.addChild(appBanner);
// Start button background
var startButtonBg = LK.getAsset('blackBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4.0,
scaleY: 1.5
});
startButtonBg.x = 1024;
startButtonBg.y = 1000;
startButtonBg.tint = 0x228b22;
menuOverlay.addChild(startButtonBg);
// Start button image
var startButton = LK.getAsset('StartGameButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4.0,
scaleY: 2.0
});
startButton.x = 1024;
startButton.y = 1000;
menuOverlay.addChild(startButton);
// How to Play button
var howToPlayButton = LK.getAsset('Howtoplay22', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4.0,
scaleY: 4.0
});
howToPlayButton.x = 1024;
howToPlayButton.y = 1615; // 1.3x higher (2100 - 485 = 1615)
menuOverlay.addChild(howToPlayButton);
// Play menu music when game starts
LK.playMusic('Menusong');
// How to Play instructions overlay
var howToPlayOverlay = new Container();
howToPlayOverlay.visible = false;
game.addChild(howToPlayOverlay);
// How to Play background
var howToPlayBg = LK.getAsset('blackBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 18.0,
scaleY: 24.0
});
howToPlayBg.x = 1024;
howToPlayBg.y = 1366;
howToPlayBg.tint = 0x000000;
howToPlayBg.alpha = 0.9;
howToPlayOverlay.addChild(howToPlayBg);
// How to Play title
var howToPlayTitle = new Text2('HOW TO PLAY', {
size: 100,
fill: '#ffffff'
});
howToPlayTitle.anchor.set(0.5, 0.5);
howToPlayTitle.x = 1024;
howToPlayTitle.y = 400;
howToPlayOverlay.addChild(howToPlayTitle);
// How to Play instructions
var instructionsText = new Text2('NORMAL MODE:\n• Collect gold coins with workers\n• Spend gold to recruit units\n• Press Attack to command units to advance\n• Destroy enemy tower to win\n\nUNIT TYPES & STATS:\n• Worker: 5 gold, 10 HP, 5 damage, collects coins\n• Warrior: 10 gold, 20 HP, 10 damage, melee combat\n• Archer: 15 gold, 30 HP, 20 damage, ranged arrows\n• Wizard: 20 gold, 40 HP, 30 damage, magic attacks\n• Dragon: 25 gold, 50 HP, 40 damage, fire breath\n\nTOWERS:\n• Player Tower: 500 HP, 5 damage per second, attacks nearest enemy\n• AI Tower: 500 HP, 5 damage per second, attacks nearest enemy\n• Attack range: 400 pixels\n\nZOMBIE MODE:\n• Survive endless zombie waves\n• Units automatically attack zombies first\n• Zombies spawn 30 seconds after start\n• Wave 1: 5 zombies (30 HP, 10 damage)\n• Each wave: +2 zombies, +15 HP, +5 damage\n• Defend your tower at all costs!', {
size: 42,
fill: '#ffffff'
});
instructionsText.anchor.set(0.5, 0.5);
instructionsText.x = 1024;
instructionsText.y = 1300;
howToPlayOverlay.addChild(instructionsText);
// Back button
var backButton = new Text2('BACK TO MENU', {
size: 80,
fill: '#ffff00'
});
backButton.anchor.set(0.5, 0.5);
backButton.x = 1024;
backButton.y = 2100;
howToPlayOverlay.addChild(backButton);
// Mode selection overlay
var modeSelectionOverlay = new Container();
modeSelectionOverlay.visible = false;
game.addChild(modeSelectionOverlay);
// Mode selection background
var modeSelectionBg = LK.getAsset('blackBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 18.0,
scaleY: 24.0
});
modeSelectionBg.x = 1024;
modeSelectionBg.y = 1366;
modeSelectionBg.tint = 0x000000;
modeSelectionBg.alpha = 0.9;
modeSelectionOverlay.addChild(modeSelectionBg);
// Mode selection title
var modeSelectionTitle = new Text2('SELECT MODE', {
size: 100,
fill: '#ffffff'
});
modeSelectionTitle.anchor.set(0.5, 0.5);
modeSelectionTitle.x = 1024;
modeSelectionTitle.y = 600;
modeSelectionOverlay.addChild(modeSelectionTitle);
// Normal mode button
var normalModeButton = LK.getAsset('Normalmode', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
normalModeButton.x = 1024;
normalModeButton.y = 1000;
modeSelectionOverlay.addChild(normalModeButton);
// Zombie mode button in mode selection
var zombieModeSelectButton = LK.getAsset('Zombiemode', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
zombieModeSelectButton.x = 1024;
zombieModeSelectButton.y = 1400;
modeSelectionOverlay.addChild(zombieModeSelectButton);
// Back to menu button from mode selection
var backToMenuButton = new Text2('BACK TO MENU', {
size: 80,
fill: '#ffff00'
});
backToMenuButton.anchor.set(0.5, 0.5);
backToMenuButton.x = 1024;
backToMenuButton.y = 2000;
modeSelectionOverlay.addChild(backToMenuButton);
// Start button event handler - now shows mode selection
startButton.down = function () {
menuOverlay.visible = false;
modeSelectionOverlay.visible = true;
};
startButtonBg.down = function () {
menuOverlay.visible = false;
modeSelectionOverlay.visible = true;
};
// Normal mode button event handler
normalModeButton.down = function () {
zombieMode = false;
gameState = 'playing';
modeSelectionOverlay.visible = false;
LK.stopMusic(); // Stop menu music when starting game
initializeGame();
};
// Zombie mode button event handler in mode selection
zombieModeSelectButton.down = function () {
zombieMode = true;
gameState = 'playing';
modeSelectionOverlay.visible = false;
LK.stopMusic();
initializeGame();
};
// Back to menu button event handler
backToMenuButton.down = function () {
modeSelectionOverlay.visible = false;
menuOverlay.visible = true;
};
// How to Play button event handler
howToPlayButton.down = function () {
menuOverlay.visible = false;
howToPlayOverlay.visible = true;
};
// Back button event handler
backButton.down = function () {
howToPlayOverlay.visible = false;
menuOverlay.visible = true;
};
// Add game background (initially hidden)
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
background.visible = false;
game.addChild(background);
var playerCoins = 0;
var aiCoins = 0;
var playerTowers = [];
var aiTowers = [];
var coins = [];
var playerCoins_array = [];
var aiCoins_array = [];
var playerUnits = [];
var aiUnits = [];
var arrows = [];
var magics = [];
var fires = [];
var coinSpawnTimer = 0;
var aiActionTimer = 0;
var zombies = [];
var zombieSpawnTimer = 0;
var zombieMode = false;
var zombieWaveNumber = 1;
var zombieWaveSpawnTimer = 0;
var zombieWaveInProgress = false;
var zombiesInCurrentWave = 0;
var zombiesToSpawnInWave = 0;
// UI Elements
var coinDisplay = new Text2('Golds: 0', {
size: 80,
fill: '#ffff00'
});
coinDisplay.anchor.set(0.5, 0.5);
coinDisplay.x = 200;
coinDisplay.y = -273.75;
coinDisplay.visible = false;
LK.gui.bottom.addChild(coinDisplay);
// Attack button
var attackButton = new Text2('Attack', {
size: 80,
fill: '#ff0000'
});
attackButton.anchor.set(0.5, 0.5);
attackButton.x = -200;
attackButton.y = -273.75;
attackButton.visible = false;
LK.gui.bottom.addChild(attackButton);
var workerButton = new Text2('5', {
size: 32,
fill: '#ffffff'
});
workerButton.anchor.set(0.5, 0.5);
workerButton.x = -400;
workerButton.y = -45;
var warriorButton = new Text2('10', {
size: 32,
fill: '#ffffff'
});
warriorButton.anchor.set(0.5, 0.5);
warriorButton.x = -200;
warriorButton.y = -45;
var wizardButton = new Text2('20', {
size: 32,
fill: '#ffffff'
});
wizardButton.anchor.set(0.5, 0.5);
wizardButton.x = 200;
wizardButton.y = -45;
var archerButton = new Text2('15', {
size: 32,
fill: '#ffffff'
});
archerButton.anchor.set(0.5, 0.5);
archerButton.x = 0;
archerButton.y = -45;
var dragonButton = new Text2('25', {
size: 32,
fill: '#ffffff'
});
dragonButton.anchor.set(0.5, 0.5);
dragonButton.x = 400;
dragonButton.y = -45;
// Add unified black background behind all recruitment buttons and unit images
var unifiedBackground = LK.getAsset('blackBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 22.0,
scaleY: 2.8
});
unifiedBackground.x = 0;
unifiedBackground.y = -78;
unifiedBackground.tint = 0x000000;
unifiedBackground.alpha = 0.5;
unifiedBackground.visible = false;
LK.gui.bottom.addChild(unifiedBackground);
// Add unit images above recruitment buttons
var workerImage = LK.getAsset('worker', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
workerImage.x = -400;
workerImage.y = -110;
LK.gui.bottom.addChild(workerImage);
var warriorImage = LK.getAsset('warrior', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
warriorImage.x = -200;
warriorImage.y = -110;
LK.gui.bottom.addChild(warriorImage);
var wizardImage = LK.getAsset('wizard', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
wizardImage.x = 200;
wizardImage.y = -110;
LK.gui.bottom.addChild(wizardImage);
var archerImage = LK.getAsset('archer', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
archerImage.x = 0;
archerImage.y = -110;
LK.gui.bottom.addChild(archerImage);
var dragonImage = LK.getAsset('dragon', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
dragonImage.x = 400;
dragonImage.y = -110;
LK.gui.bottom.addChild(dragonImage);
// Wave display for zombie mode
var waveDisplay = new Text2('Wave: 1', {
size: 80,
fill: '#ff0000'
});
waveDisplay.anchor.set(0.5, 0.5);
waveDisplay.x = 0;
waveDisplay.y = 100;
waveDisplay.visible = false;
LK.gui.top.addChild(waveDisplay);
// Hide all UI elements initially (menu state)
workerButton.visible = false;
warriorButton.visible = false;
wizardButton.visible = false;
archerButton.visible = false;
dragonButton.visible = false;
workerImage.visible = false;
warriorImage.visible = false;
wizardImage.visible = false;
archerImage.visible = false;
dragonImage.visible = false;
// Add texts after backgrounds so they appear on top
LK.gui.bottom.addChild(workerButton);
LK.gui.bottom.addChild(warriorButton);
LK.gui.bottom.addChild(wizardButton);
LK.gui.bottom.addChild(archerButton);
LK.gui.bottom.addChild(dragonButton);
// Initialize towers
function initializeTowers() {
// Player tower
var playerTower = new Tower('player');
playerTower.x = 1024;
playerTower.y = 2300;
playerTowers.push(playerTower);
game.addChild(playerTower);
// AI tower (only in normal mode)
if (!zombieMode) {
var aiTower = new Tower('ai');
aiTower.x = 1024;
aiTower.y = 400;
aiTowers.push(aiTower);
game.addChild(aiTower);
}
}
;
function spawnCoin() {
// Spawn player coin
var playerCoin = new Coin();
playerCoin.x = Math.random() * 1800 + 124;
playerCoin.y = Math.random() * 1000 + 1200; // Lower half for player
playerCoins_array.push(playerCoin);
coins.push(playerCoin);
game.addChild(playerCoin);
// Spawn AI coin
var aiCoin = new Coin();
aiCoin.x = Math.random() * 1800 + 124;
aiCoin.y = Math.random() * 1000 + 400; // Upper half for AI
aiCoins_array.push(aiCoin);
coins.push(aiCoin);
game.addChild(aiCoin);
}
function spawnUnit(team, unitType) {
var unit;
var cost = unitType === 'worker' ? 5 : unitType === 'warrior' ? 10 : unitType === 'wizard' ? 20 : unitType === 'archer' ? 15 : 25;
if (team === 'player' && playerCoins >= cost) {
playerCoins -= cost;
if (unitType === 'worker') {
unit = new Worker('player');
} else {
unit = new CombatUnit('player', unitType);
}
if (!zombieMode && unitType !== 'worker') {
// Position combat units in front of tower in normal mode with better spacing
var unitCount = playerUnits.length;
var spreadRadius = 300;
var angle = unitCount * 0.5 % (Math.PI * 2);
unit.x = playerTowers[0].x + Math.cos(angle) * spreadRadius;
unit.y = playerTowers[0].y - 507 + Math.sin(angle) * 100;
} else {
var unitCount = playerUnits.length;
var spreadRadius = 200;
var angle = unitCount * 0.8 % (Math.PI * 2);
unit.x = playerTowers[0].x + Math.cos(angle) * spreadRadius;
unit.y = playerTowers[0].y - 100 + Math.sin(angle) * 50;
}
playerUnits.push(unit);
game.addChild(unit);
LK.getSound('unitSpawn').play();
} else if (team === 'ai' && aiCoins >= cost) {
aiCoins -= cost;
if (unitType === 'worker') {
unit = new Worker('ai');
} else {
unit = new CombatUnit('ai', unitType);
}
var unitCount = aiUnits.length;
var spreadRadius = 200;
var angle = unitCount * 0.8 % (Math.PI * 2);
unit.x = aiTowers[0].x + Math.cos(angle) * spreadRadius;
unit.y = aiTowers[0].y + 100 + Math.sin(angle) * 50;
aiUnits.push(unit);
game.addChild(unit);
}
}
function spawnZombie() {
var zombie = new Zombie(zombieWaveNumber);
zombie.x = Math.random() * 1800 + 124; // Random X position across screen width
zombie.y = 50; // Spawn at top of screen
zombies.push(zombie);
game.addChild(zombie);
}
function startZombieWave() {
zombieWaveInProgress = true;
zombiesToSpawnInWave = 3 + zombieWaveNumber * 2; // Wave 1: 5 zombies, Wave 2: 7 zombies, etc.
zombiesInCurrentWave = 0;
zombieWaveSpawnTimer = 0;
// Update wave display
if (zombieMode) {
waveDisplay.setText('Wave: ' + zombieWaveNumber);
}
}
function checkWaveComplete() {
// Check if all zombies in current wave are spawned and destroyed
if (zombieWaveInProgress && zombiesInCurrentWave >= zombiesToSpawnInWave && zombies.length === 0) {
zombieWaveInProgress = false;
zombieWaveNumber++;
// Start next wave after 10 seconds (doubled from 5 seconds)
zombieWaveSpawnTimer = -600; // 10 seconds delay before next wave
}
}
function aiLogic() {
if (aiActionTimer <= 0) {
if (aiCoins >= 25 && Math.random() < 0.1) {
spawnUnit('ai', 'dragon');
} else if (aiCoins >= 15 && Math.random() < 0.15) {
spawnUnit('ai', 'archer');
} else if (aiCoins >= 20 && Math.random() < 0.2) {
spawnUnit('ai', 'wizard');
} else if (aiCoins >= 10 && Math.random() < 0.3) {
spawnUnit('ai', 'warrior');
} else if (aiCoins >= 5 && Math.random() < 0.4) {
spawnUnit('ai', 'worker');
}
aiActionTimer = 120;
}
aiActionTimer--;
}
function checkGameEnd() {
if (playerTowers.length === 0) {
LK.showGameOver();
} else if (!zombieMode && aiTowers.length === 0) {
LK.showYouWin();
}
}
function cleanupCollectedCoins() {
// Clean up from main coins array
for (var i = coins.length - 1; i >= 0; i--) {
if (coins[i].collected) {
coins[i].destroy();
coins.splice(i, 1);
}
}
// Clean up from player coins array
for (var i = playerCoins_array.length - 1; i >= 0; i--) {
if (playerCoins_array[i].collected) {
playerCoins_array.splice(i, 1);
}
}
// Clean up from AI coins array
for (var i = aiCoins_array.length - 1; i >= 0; i--) {
if (aiCoins_array[i].collected) {
aiCoins_array.splice(i, 1);
}
}
}
// Button event handlers
workerButton.down = function () {
spawnUnit('player', 'worker');
};
warriorButton.down = function () {
spawnUnit('player', 'warrior');
};
wizardButton.down = function () {
spawnUnit('player', 'wizard');
};
archerButton.down = function () {
spawnUnit('player', 'archer');
};
dragonButton.down = function () {
spawnUnit('player', 'dragon');
};
// Character image event handlers
workerImage.down = function () {
spawnUnit('player', 'worker');
};
warriorImage.down = function () {
spawnUnit('player', 'warrior');
};
wizardImage.down = function () {
spawnUnit('player', 'wizard');
};
archerImage.down = function () {
spawnUnit('player', 'archer');
};
dragonImage.down = function () {
spawnUnit('player', 'dragon');
};
// Attack button event handler
attackButton.down = function () {
if (!zombieMode) {
// Make all waiting player units start attacking
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
if (unit.unitType !== 'worker') {
unit.isWaiting = false;
}
}
}
};
// Initialize game function
function initializeGame() {
background.visible = true;
// Show UI elements when game starts
coinDisplay.visible = true;
unifiedBackground.visible = true;
workerButton.visible = true;
warriorButton.visible = true;
wizardButton.visible = true;
archerButton.visible = true;
dragonButton.visible = true;
workerImage.visible = true;
warriorImage.visible = true;
wizardImage.visible = true;
archerImage.visible = true;
dragonImage.visible = true;
// Show attack button only in normal mode
if (!zombieMode) {
attackButton.visible = true;
} else {
// Show wave display in zombie mode
waveDisplay.visible = true;
waveDisplay.setText('Wave: ' + zombieWaveNumber);
}
// Reset zombie wave system
zombieWaveNumber = 1;
zombieWaveSpawnTimer = -1800; // 30 second delay (30 * 60 FPS = 1800 frames)
zombieWaveInProgress = false;
zombiesInCurrentWave = 0;
zombiesToSpawnInWave = 0;
// Play game music when match starts
LK.playMusic('Song');
initializeTowers();
// Spawn initial workers for both teams
var playerWorker = new Worker('player');
playerWorker.x = playerTowers[0].x + 50;
playerWorker.y = playerTowers[0].y - 100;
playerUnits.push(playerWorker);
game.addChild(playerWorker);
// AI worker (only in normal mode)
if (!zombieMode) {
var aiWorker = new Worker('ai');
aiWorker.x = aiTowers[0].x + 50;
aiWorker.y = aiTowers[0].y + 100;
aiUnits.push(aiWorker);
game.addChild(aiWorker);
}
}
// Main game loop
game.update = function () {
if (gameState === 'playing') {
// Update coin display
coinDisplay.setText('Golds: ' + playerCoins);
// Spawn coins
coinSpawnTimer++;
if (coinSpawnTimer >= 36) {
// 5x more frequent (180/5 = 36)
spawnCoin();
coinSpawnTimer = 0;
}
// AI logic (only in normal mode)
if (!zombieMode) {
aiLogic();
}
// Clean up collected coins
cleanupCollectedCoins();
// Zombie mode logic
if (zombieMode) {
zombieWaveSpawnTimer++;
// Start first wave or next wave after delay
if (!zombieWaveInProgress && zombieWaveSpawnTimer >= 0) {
startZombieWave();
}
// Spawn zombies in current wave
if (zombieWaveInProgress && zombiesInCurrentWave < zombiesToSpawnInWave) {
if (zombieWaveSpawnTimer % 60 === 0) {
// Spawn one zombie every second during wave
spawnZombie();
zombiesInCurrentWave++;
}
}
// Check if current wave is complete
checkWaveComplete();
// Clean up off-screen zombies
for (var i = zombies.length - 1; i >= 0; i--) {
if (zombies[i].y > 2800) {
zombies[i].destroy();
zombies.splice(i, 1);
}
}
}
// Check game end conditions
checkGameEnd();
}
};
Worker. In-Game asset. 2d. High contrast. No shadows
Wizard. In-Game asset. 2d. High contrast. No shadows
Dragon. In-Game asset. 2d. High contrast. No shadows
Warrior. In-Game asset. 2d. High contrast. No shadows
etrafı kırmızı işçi. In-Game asset. 2d. High contrast. No shadows
Archer. In-Game asset. 2d. High contrast. No shadows
Magic. In-Game asset. 2d. High contrast. No shadows
Sadece 1 Altın parçacığı. In-Game asset. 2d. High contrast. No shadows
Köşelerdeki taş yerleri kaldır
Çözünürlüğü artır ve kırmızı şeyleri kaldır. In-Game asset. 2d. High contrast. No shadows
Ağzından ateş püskürtsün
Tam yukarıdan bakılan bir kule clash royaledeki gibi. In-Game asset. 2d. High contrast. No shadows
Mavi yerler kırmızı olsun
Sol tarafında kılıçlar olan start buton. In-Game asset. 2d. High contrast. No shadows
How to play button. In-Game asset. 2d. High contrast. No shadows
Tower defence menü background. In-Game asset. 2d. High contrast. No shadows
Towers On Attack yazısı. In-Game asset. 2d. High contrast. No shadows
Zombie. In-Game asset. 2d. High contrast. No shadows
Normal Mode button. In-Game asset. 2d. High contrast. No shadows
Zombie Mode buton. In-Game asset. 2d. High contrast. No shadows