/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var AircraftBullet = Container.expand(function (startX, startY, targetX, targetY, damage) { var self = Container.call(this); self.graphics = self.attachAsset('rocket', { anchorX: 0.5, anchorY: 0.5 }); self.x = startX; self.y = startY; self.damage = damage; self.speed = 12; var dx = targetX - startX; var dy = targetY - startY; var distance = Math.sqrt(dx * dx + dy * dy); self.velocityX = dx / distance * self.speed; self.velocityY = dy / distance * self.speed; // Calculate rotation angle to point towards target var angle = Math.atan2(dy, dx) + Math.PI / 2; self.graphics.rotation = angle; self.update = function () { self.x += self.velocityX; self.y += self.velocityY; }; return self; }); var ArtilleryBullet = Container.expand(function (startX, startY, targetX, targetY, damage) { var self = Container.call(this); self.graphics = self.attachAsset('artilleryShell', { anchorX: 0.5, anchorY: 0.5 }); self.x = startX; self.y = startY; self.damage = damage; self.speed = 4; var dx = targetX - startX; var dy = targetY - startY; var distance = Math.sqrt(dx * dx + dy * dy); self.velocityX = dx / distance * self.speed; self.velocityY = dy / distance * self.speed; self.update = function () { self.x += self.velocityX; self.y += self.velocityY; }; return self; }); var Boss = Container.expand(function () { var self = Container.call(this); self.enemyType = 'boss'; self.health = 500; self.maxHealth = 500; self.damage = 50; self.speed = 0.5; self.targetX = 1024; self.targetY = 1366; self.graphics = self.attachAsset('boss', { anchorX: 0.5, anchorY: 0.5 }); // Create health bar for boss self.healthBarBg = self.attachAsset('healthBarBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 1.5 }); self.healthBarFill = self.attachAsset('healthBarFill', { anchorX: 0, anchorY: 0.5, scaleX: 2.5, scaleY: 1.5 }); self.healthBarBg.x = 0; self.healthBarBg.y = -170; self.healthBarFill.x = -95; self.healthBarFill.y = -170; self.updateHealthBar = function () { var healthPercent = Math.max(0, self.health / self.maxHealth); var newWidth = 76 * healthPercent; self.healthBarFill.width = newWidth; if (healthPercent > 0.6) { self.healthBarFill.tint = 0x00ff00; // Green } else if (healthPercent > 0.3) { self.healthBarFill.tint = 0xffff00; // Yellow } else { self.healthBarFill.tint = 0xff0000; // Red } }; self.updateHealthBar(); self.takeDamage = function (damage) { self.health -= damage; self.updateHealthBar(); if (self.health <= 0) { self.health = 0; return true; } return false; }; self.update = function () { // Move towards base var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } }; return self; }); var Bullet = Container.expand(function (startX, startY, targetX, targetY, damage) { var self = Container.call(this); self.graphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.x = startX; self.y = startY; self.damage = damage; self.speed = 8; var dx = targetX - startX; var dy = targetY - startY; var distance = Math.sqrt(dx * dx + dy * dy); self.velocityX = dx / distance * self.speed; self.velocityY = dy / distance * self.speed; self.update = function () { self.x += self.velocityX; self.y += self.velocityY; }; return self; }); var Enemy = Container.expand(function (enemyType) { var self = Container.call(this); self.enemyType = enemyType; self.health = 60; self.maxHealth = 60; self.damage = 15; self.speed = 1; self.targetX = 1024; self.targetY = 1366; if (enemyType === 'tank') { self.health = 120; self.maxHealth = 120; self.damage = 30; self.speed = 0.8; self.graphics = self.attachAsset('enemyTank', { anchorX: 0.5, anchorY: 0.5 }); } else if (enemyType === 'soldier') { self.health = 50; self.maxHealth = 50; self.damage = 10; self.speed = 1.2; self.graphics = self.attachAsset('enemySoldier', { anchorX: 0.5, anchorY: 0.5 }); } // Create health bar self.healthBarBg = self.attachAsset('healthBarBackground', { anchorX: 0.5, anchorY: 0.5 }); self.healthBarFill = self.attachAsset('healthBarFill', { anchorX: 0, anchorY: 0.5 }); self.healthBarBg.x = 0; self.healthBarBg.y = -60; self.healthBarFill.x = -38; self.healthBarFill.y = -60; self.updateHealthBar = function () { var healthPercent = Math.max(0, self.health / self.maxHealth); var newWidth = 76 * healthPercent; self.healthBarFill.width = newWidth; if (healthPercent > 0.6) { self.healthBarFill.tint = 0x00ff00; // Green } else if (healthPercent > 0.3) { self.healthBarFill.tint = 0xffff00; // Yellow } else { self.healthBarFill.tint = 0xff0000; // Red } }; self.updateHealthBar(); self.takeDamage = function (damage) { self.health -= damage; self.updateHealthBar(); if (self.health <= 0) { self.health = 0; return true; } return false; }; self.update = function () { // Move towards base var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } }; return self; }); var GridCell = Container.expand(function (gridX, gridY) { var self = Container.call(this); self.gridX = gridX; self.gridY = gridY; self.occupied = false; self.unit = null; self.graphics = self.attachAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, alpha: 0.3 }); self.down = function (x, y, obj) { if (selectedUnitType === 'erase') { // Always allow erase action if there's a unit to erase if (self.occupied) { deployUnit(self.gridX, self.gridY); } } else { // Only deploy if cell is not occupied and player has enough money if (!self.occupied && playerMoney >= selectedUnitCost) { deployUnit(self.gridX, self.gridY); } } }; return self; }); var LaserBullet = Container.expand(function (startX, startY, targetX, targetY, damage) { var self = Container.call(this); self.graphics = self.attachAsset('laserBeam', { anchorX: 0.5, anchorY: 0.5 }); self.x = startX; self.y = startY; self.damage = damage; self.speed = 15; var dx = targetX - startX; var dy = targetY - startY; var distance = Math.sqrt(dx * dx + dy * dy); self.velocityX = dx / distance * self.speed; self.velocityY = dy / distance * self.speed; // Calculate rotation angle to point towards target var angle = Math.atan2(dy, dx) + Math.PI / 2; self.graphics.rotation = angle; self.update = function () { self.x += self.velocityX; self.y += self.velocityY; }; return self; }); var SniperBullet = Container.expand(function (startX, startY, targetX, targetY, damage) { var self = Container.call(this); self.graphics = self.attachAsset('sniperBullet', { anchorX: 0.5, anchorY: 0.5 }); self.x = startX; self.y = startY; self.damage = damage; self.speed = 20; var dx = targetX - startX; var dy = targetY - startY; var distance = Math.sqrt(dx * dx + dy * dy); self.velocityX = dx / distance * self.speed; self.velocityY = dy / distance * self.speed; // Calculate rotation angle to point towards target var angle = Math.atan2(dy, dx) + Math.PI / 2; self.graphics.rotation = angle; self.update = function () { self.x += self.velocityX; self.y += self.velocityY; }; return self; }); var TankBullet = Container.expand(function (startX, startY, targetX, targetY, damage) { var self = Container.call(this); self.graphics = self.attachAsset('tankShell', { anchorX: 0.5, anchorY: 0.5 }); self.x = startX; self.y = startY; self.damage = damage; self.speed = 6; var dx = targetX - startX; var dy = targetY - startY; var distance = Math.sqrt(dx * dx + dy * dy); self.velocityX = dx / distance * self.speed; self.velocityY = dy / distance * self.speed; self.update = function () { self.x += self.velocityX; self.y += self.velocityY; }; return self; }); var Unit = Container.expand(function (unitType) { var self = Container.call(this); self.unitType = unitType; self.health = 100; self.maxHealth = 100; self.attackDamage = 20; self.attackRange = 200; self.attackCooldown = 0; self.attackSpeed = 60; // frames between attacks self.cost = 50; // Set unit-specific properties if (unitType === 'tank') { self.health = 150; self.maxHealth = 150; self.attackDamage = 40; self.attackRange = 280; self.attackSpeed = 90; self.cost = 100; self.graphics = self.attachAsset('tank', { anchorX: 0.5, anchorY: 0.5 }); } else if (unitType === 'soldier') { self.health = 80; self.maxHealth = 80; self.attackDamage = 15; self.attackRange = 180; self.attackSpeed = 45; self.cost = 30; self.graphics = self.attachAsset('soldier', { anchorX: 0.5, anchorY: 0.5 }); } else if (unitType === 'aircraft') { self.health = 100; self.maxHealth = 100; self.attackDamage = 25; self.attackRange = 350; self.attackSpeed = 60; self.cost = 80; self.graphics = self.attachAsset('aircraft', { anchorX: 0.5, anchorY: 0.5 }); } else if (unitType === 'artillery') { self.health = 120; self.maxHealth = 120; self.attackDamage = 60; self.attackRange = 450; self.attackSpeed = 150; self.cost = 150; self.graphics = self.attachAsset('artillery', { anchorX: 0.5, anchorY: 0.5 }); } else if (unitType === 'sniper') { self.health = 60; self.maxHealth = 60; self.attackDamage = 80; self.attackRange = 500; self.attackSpeed = 180; self.cost = 120; self.graphics = self.attachAsset('sniper', { anchorX: 0.5, anchorY: 0.5 }); } else if (unitType === 'laser') { self.health = 90; self.maxHealth = 90; self.attackDamage = 35; self.attackRange = 250; self.attackSpeed = 30; self.cost = 200; self.graphics = self.attachAsset('laser', { anchorX: 0.5, anchorY: 0.5 }); } self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.health = 0; return true; // unit destroyed } return false; }; self.canAttack = function () { return self.attackCooldown <= 0; }; self.attack = function (target) { if (self.canAttack()) { self.attackCooldown = self.attackSpeed; return true; } return false; }; self.update = function () { if (self.attackCooldown > 0) { self.attackCooldown--; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2F4F2F }); /**** * Game Code ****/ var GRID_SIZE = 120; var GRID_COLS = 8; var GRID_ROWS = 6; var GRID_START_X = 400; var GRID_START_Y = 800; // Game state variables var playerMoney = 200; var currentWave = 1; var maxWaves = 10; var enemiesThisWave = 0; var maxEnemiesThisWave = 3; var enemySpawnTimer = 0; var enemySpawnDelay = 120; var selectedUnitType = 'soldier'; var selectedUnitCost = 30; var gameState = 'playing'; // 'playing', 'gameOver', 'victory' var baseHealth = 100; var maxBaseHealth = 100; // Game arrays var playerUnits = []; var enemies = []; var bullets = []; var tankBullets = []; var aircraftBullets = []; var artilleryBullets = []; var sniperBullets = []; var laserBullets = []; var gridCells = []; // Create base var base = game.addChild(LK.getAsset('base', { anchorX: 0.5, anchorY: 0.5 })); base.x = 1024; base.y = 1366; // Create grid for (var row = 0; row < GRID_ROWS; row++) { for (var col = 0; col < GRID_COLS; col++) { var cell = new GridCell(col, row); cell.x = GRID_START_X + col * GRID_SIZE; cell.y = GRID_START_Y + row * GRID_SIZE; gridCells.push(cell); game.addChild(cell); } } // UI Elements var moneyText = new Text2('Money: $' + playerMoney, { size: 60, fill: 0xFFFFFF }); moneyText.anchor.set(0, 0); moneyText.x = 120; moneyText.y = 100; LK.gui.topLeft.addChild(moneyText); var waveText = new Text2('Wave: ' + currentWave + '/' + maxWaves, { size: 60, fill: 0xFFFFFF }); waveText.anchor.set(0.5, 0); LK.gui.top.addChild(waveText); var baseHealthText = new Text2('Base Health: ' + baseHealth, { size: 50, fill: 0xFFFFFF }); baseHealthText.anchor.set(0.5, 0); baseHealthText.y = 80; LK.gui.top.addChild(baseHealthText); // Unit selection buttons var soldierButton = LK.getAsset('soldier', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); soldierButton.x = 200; soldierButton.y = 200; LK.gui.topLeft.addChild(soldierButton); var tankButton = LK.getAsset('tank', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); tankButton.x = 300; tankButton.y = 200; LK.gui.topLeft.addChild(tankButton); var aircraftButton = LK.getAsset('aircraft', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.3, scaleY: 1.3 }); aircraftButton.x = 400; aircraftButton.y = 200; LK.gui.topLeft.addChild(aircraftButton); // Cost labels for unit buttons var soldierCostText = new Text2('$30', { size: 30, fill: 0xFFD700 }); soldierCostText.anchor.set(0.5, 0); soldierCostText.x = 200; soldierCostText.y = 250; LK.gui.topLeft.addChild(soldierCostText); var tankCostText = new Text2('$100', { size: 30, fill: 0xFFD700 }); tankCostText.anchor.set(0.5, 0); tankCostText.x = 300; tankCostText.y = 250; LK.gui.topLeft.addChild(tankCostText); var aircraftCostText = new Text2('$80', { size: 30, fill: 0xFFD700 }); aircraftCostText.anchor.set(0.5, 0); aircraftCostText.x = 400; aircraftCostText.y = 250; LK.gui.topLeft.addChild(aircraftCostText); // Additional tower buttons var artilleryButton = LK.getAsset('artillery', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); artilleryButton.x = 500; artilleryButton.y = 200; LK.gui.topLeft.addChild(artilleryButton); var sniperButton = LK.getAsset('sniper', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.4, scaleY: 1.4 }); sniperButton.x = 600; sniperButton.y = 200; LK.gui.topLeft.addChild(sniperButton); var laserButton = LK.getAsset('laser', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.3, scaleY: 1.3 }); laserButton.x = 700; laserButton.y = 200; LK.gui.topLeft.addChild(laserButton); // Add erase button var eraseButton = LK.getAsset('eraseButton', { anchorX: 0.5, anchorY: 0.5 }); eraseButton.x = 800; eraseButton.y = 200; LK.gui.topLeft.addChild(eraseButton); // Add erase button label var eraseButtonText = new Text2('X', { size: 40, fill: 0xFFFFFF }); eraseButtonText.anchor.set(0.5, 0.5); eraseButtonText.x = 800; eraseButtonText.y = 200; LK.gui.topLeft.addChild(eraseButtonText); // Additional cost labels var artilleryCostText = new Text2('$150', { size: 30, fill: 0xFFD700 }); artilleryCostText.anchor.set(0.5, 0); artilleryCostText.x = 500; artilleryCostText.y = 250; LK.gui.topLeft.addChild(artilleryCostText); var sniperCostText = new Text2('$120', { size: 30, fill: 0xFFD700 }); sniperCostText.anchor.set(0.5, 0); sniperCostText.x = 600; sniperCostText.y = 250; LK.gui.topLeft.addChild(sniperCostText); var laserCostText = new Text2('$200', { size: 30, fill: 0xFFD700 }); laserCostText.anchor.set(0.5, 0); laserCostText.x = 700; laserCostText.y = 250; LK.gui.topLeft.addChild(laserCostText); var eraseCostText = new Text2('ERASE', { size: 25, fill: 0xFFD700 }); eraseCostText.anchor.set(0.5, 0); eraseCostText.x = 800; eraseCostText.y = 250; LK.gui.topLeft.addChild(eraseCostText); // Button event handlers soldierButton.down = function () { selectedUnitType = 'soldier'; selectedUnitCost = 30; updateButtonSelection(); }; tankButton.down = function () { selectedUnitType = 'tank'; selectedUnitCost = 100; updateButtonSelection(); }; aircraftButton.down = function () { selectedUnitType = 'aircraft'; selectedUnitCost = 80; updateButtonSelection(); }; artilleryButton.down = function () { selectedUnitType = 'artillery'; selectedUnitCost = 150; updateButtonSelection(); }; sniperButton.down = function () { selectedUnitType = 'sniper'; selectedUnitCost = 120; updateButtonSelection(); }; laserButton.down = function () { selectedUnitType = 'laser'; selectedUnitCost = 200; updateButtonSelection(); }; eraseButton.down = function () { selectedUnitType = 'erase'; selectedUnitCost = 0; updateButtonSelection(); }; function updateButtonSelection() { soldierButton.alpha = selectedUnitType === 'soldier' ? 1.0 : 0.6; tankButton.alpha = selectedUnitType === 'tank' ? 1.0 : 0.6; aircraftButton.alpha = selectedUnitType === 'aircraft' ? 1.0 : 0.6; artilleryButton.alpha = selectedUnitType === 'artillery' ? 1.0 : 0.6; sniperButton.alpha = selectedUnitType === 'sniper' ? 1.0 : 0.6; laserButton.alpha = selectedUnitType === 'laser' ? 1.0 : 0.6; eraseButton.alpha = selectedUnitType === 'erase' ? 1.0 : 0.6; // Update cost label highlighting soldierCostText.alpha = selectedUnitType === 'soldier' ? 1.0 : 0.6; tankCostText.alpha = selectedUnitType === 'tank' ? 1.0 : 0.6; aircraftCostText.alpha = selectedUnitType === 'aircraft' ? 1.0 : 0.6; artilleryCostText.alpha = selectedUnitType === 'artillery' ? 1.0 : 0.6; sniperCostText.alpha = selectedUnitType === 'sniper' ? 1.0 : 0.6; laserCostText.alpha = selectedUnitType === 'laser' ? 1.0 : 0.6; eraseCostText.alpha = selectedUnitType === 'erase' ? 1.0 : 0.6; } function deployUnit(gridX, gridY) { var cellIndex = gridY * GRID_COLS + gridX; var cell = gridCells[cellIndex]; if (selectedUnitType === 'erase') { // Erase existing tower if (cell.occupied && cell.unit) { // Remove unit from playerUnits array for (var i = playerUnits.length - 1; i >= 0; i--) { if (playerUnits[i] === cell.unit) { cell.unit.destroy(); playerUnits.splice(i, 1); break; } } // Mark cell as unoccupied cell.occupied = false; cell.unit = null; } } else { // Deploy new unit if (!cell.occupied && playerMoney >= selectedUnitCost) { var unit = new Unit(selectedUnitType); unit.x = GRID_START_X + gridX * GRID_SIZE; unit.y = GRID_START_Y + gridY * GRID_SIZE; playerUnits.push(unit); game.addChild(unit); // Mark grid cell as occupied cell.occupied = true; cell.unit = unit; playerMoney -= selectedUnitCost; moneyText.setText('Money: $' + playerMoney); LK.getSound('unitDeploy').play(); } } } function spawnEnemy() { var enemy; if (currentWave === 10 && enemiesThisWave === 0) { // Spawn boss on wave 10 enemy = new Boss(); } else { var enemyType = Math.random() < 0.6 ? 'soldier' : 'tank'; enemy = new Enemy(enemyType); } // Spawn from top of screen enemy.x = Math.random() * 1500 + 200; enemy.y = 100; enemies.push(enemy); game.addChild(enemy); enemiesThisWave++; } function findNearestEnemy(unit) { var nearestEnemy = null; var nearestDistance = unit.attackRange; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var dx = enemy.x - unit.x; var dy = enemy.y - unit.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= nearestDistance) { nearestEnemy = enemy; nearestDistance = distance; } } return nearestEnemy; } function createBullet(startX, startY, targetX, targetY, damage) { var bullet = new Bullet(startX, startY, targetX, targetY, damage); bullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); } function createTankBullet(startX, startY, targetX, targetY, damage) { var bullet = new TankBullet(startX, startY, targetX, targetY, damage); tankBullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); } function createAircraftBullet(startX, startY, targetX, targetY, damage) { var bullet = new AircraftBullet(startX, startY, targetX, targetY, damage); aircraftBullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); } function createArtilleryBullet(startX, startY, targetX, targetY, damage) { var bullet = new ArtilleryBullet(startX, startY, targetX, targetY, damage); artilleryBullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); } function createSniperBullet(startX, startY, targetX, targetY, damage) { var bullet = new SniperBullet(startX, startY, targetX, targetY, damage); sniperBullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); } function createLaserBullet(startX, startY, targetX, targetY, damage) { var bullet = new LaserBullet(startX, startY, targetX, targetY, damage); laserBullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); } function checkWaveComplete() { if (enemiesThisWave >= maxEnemiesThisWave && enemies.length === 0) { currentWave++; if (currentWave > maxWaves) { // After wave 10, loop back to wave 2 currentWave = 2; } // Start next wave enemiesThisWave = 0; if (currentWave === 10) { // Wave 10: only spawn the boss maxEnemiesThisWave = 1; } else { maxEnemiesThisWave = Math.min(3 + currentWave, 15); } enemySpawnDelay = Math.max(60, 120 - currentWave * 5); playerMoney += 50 + currentWave * 10; waveText.setText('Wave: ' + currentWave + '/' + maxWaves); moneyText.setText('Money: $' + playerMoney); showWaveSection(); } } // Wave section display for retreat scenarios var waveSectionText = new Text2('SECTION ' + currentWave, { size: 120, fill: 0xFFFF00 }); waveSectionText.anchor.set(0.5, 0.5); waveSectionText.x = 1024; waveSectionText.y = 400; waveSectionText.alpha = 0; game.addChild(waveSectionText); function showWaveSection() { waveSectionText.setText('SECTION ' + currentWave); waveSectionText.alpha = 1; tween(waveSectionText, { alpha: 0 }, { duration: 3000, easing: tween.easeOut }); } // Initialize selection updateButtonSelection(); game.update = function () { if (gameState !== 'playing') return; // Spawn enemies if (enemiesThisWave < maxEnemiesThisWave) { enemySpawnTimer++; if (enemySpawnTimer >= enemySpawnDelay) { spawnEnemy(); enemySpawnTimer = 0; } } // Update units and handle combat for (var i = 0; i < playerUnits.length; i++) { var unit = playerUnits[i]; var target = findNearestEnemy(unit); if (target && unit.canAttack()) { // Calculate angle to target for tower rotation var dx = target.x - unit.x; var dy = target.y - unit.y; var targetAngle = Math.atan2(dy, dx); // Smooth rotation using tween tween.stop(unit.graphics, { rotation: true }); tween(unit.graphics, { rotation: targetAngle }, { duration: 200, easing: tween.easeOut }); if (unit.attack(target)) { if (unit.unitType === 'tank') { createTankBullet(unit.x, unit.y, target.x, target.y, unit.attackDamage); } else if (unit.unitType === 'aircraft') { createAircraftBullet(unit.x, unit.y, target.x, target.y, unit.attackDamage); } else if (unit.unitType === 'artillery') { createArtilleryBullet(unit.x, unit.y, target.x, target.y, unit.attackDamage); } else if (unit.unitType === 'sniper') { createSniperBullet(unit.x, unit.y, target.x, target.y, unit.attackDamage); } else if (unit.unitType === 'laser') { createLaserBullet(unit.x, unit.y, target.x, target.y, unit.attackDamage); } else { createBullet(unit.x, unit.y, target.x, target.y, unit.attackDamage); } } } } // Update enemies for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; // Check if enemy reached base var distanceToBase = Math.sqrt(Math.pow(enemy.x - base.x, 2) + Math.pow(enemy.y - base.y, 2)); if (distanceToBase < 100) { baseHealth -= enemy.damage; baseHealthText.setText('Base Health: ' + Math.max(0, baseHealth)); showWaveSection(); // Show section when under attack if (baseHealth <= 0) { gameState = 'gameOver'; LK.showGameOver(); return; } enemy.destroy(); enemies.splice(i, 1); continue; } } // Update bullets and check collisions for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; // Check if bullet is out of bounds if (bullet.x < 0 || bullet.x > 2048 || bullet.y < 0 || bullet.y > 2732) { bullet.destroy(); bullets.splice(i, 1); continue; } // Check bullet-enemy collisions for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (bullet.intersects(enemy)) { if (enemy.takeDamage(bullet.damage)) { playerMoney += 10; moneyText.setText('Money: $' + playerMoney); LK.getSound('enemyHit').play(); enemy.destroy(); enemies.splice(j, 1); } bullet.destroy(); bullets.splice(i, 1); break; } } } // Update tank bullets and check collisions for (var i = tankBullets.length - 1; i >= 0; i--) { var bullet = tankBullets[i]; // Check if bullet is out of bounds if (bullet.x < 0 || bullet.x > 2048 || bullet.y < 0 || bullet.y > 2732) { bullet.destroy(); tankBullets.splice(i, 1); continue; } // Check bullet-enemy collisions for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (bullet.intersects(enemy)) { if (enemy.takeDamage(bullet.damage)) { playerMoney += 10; moneyText.setText('Money: $' + playerMoney); LK.getSound('enemyHit').play(); enemy.destroy(); enemies.splice(j, 1); } bullet.destroy(); tankBullets.splice(i, 1); break; } } } // Update aircraft bullets and check collisions for (var i = aircraftBullets.length - 1; i >= 0; i--) { var bullet = aircraftBullets[i]; // Check if bullet is out of bounds if (bullet.x < 0 || bullet.x > 2048 || bullet.y < 0 || bullet.y > 2732) { bullet.destroy(); aircraftBullets.splice(i, 1); continue; } // Check bullet-enemy collisions for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (bullet.intersects(enemy)) { if (enemy.takeDamage(bullet.damage)) { playerMoney += 10; moneyText.setText('Money: $' + playerMoney); LK.getSound('enemyHit').play(); enemy.destroy(); enemies.splice(j, 1); } bullet.destroy(); aircraftBullets.splice(i, 1); break; } } } // Update artillery bullets and check collisions for (var i = artilleryBullets.length - 1; i >= 0; i--) { var bullet = artilleryBullets[i]; // Check if bullet is out of bounds if (bullet.x < 0 || bullet.x > 2048 || bullet.y < 0 || bullet.y > 2732) { bullet.destroy(); artilleryBullets.splice(i, 1); continue; } // Check bullet-enemy collisions for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (bullet.intersects(enemy)) { if (enemy.takeDamage(bullet.damage)) { playerMoney += 10; moneyText.setText('Money: $' + playerMoney); LK.getSound('enemyHit').play(); enemy.destroy(); enemies.splice(j, 1); } bullet.destroy(); artilleryBullets.splice(i, 1); break; } } } // Update sniper bullets and check collisions for (var i = sniperBullets.length - 1; i >= 0; i--) { var bullet = sniperBullets[i]; // Check if bullet is out of bounds if (bullet.x < 0 || bullet.x > 2048 || bullet.y < 0 || bullet.y > 2732) { bullet.destroy(); sniperBullets.splice(i, 1); continue; } // Check bullet-enemy collisions for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (bullet.intersects(enemy)) { if (enemy.takeDamage(bullet.damage)) { playerMoney += 10; moneyText.setText('Money: $' + playerMoney); LK.getSound('enemyHit').play(); enemy.destroy(); enemies.splice(j, 1); } bullet.destroy(); sniperBullets.splice(i, 1); break; } } } // Update laser bullets and check collisions for (var i = laserBullets.length - 1; i >= 0; i--) { var bullet = laserBullets[i]; // Check if bullet is out of bounds if (bullet.x < 0 || bullet.x > 2048 || bullet.y < 0 || bullet.y > 2732) { bullet.destroy(); laserBullets.splice(i, 1); continue; } // Check bullet-enemy collisions for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (bullet.intersects(enemy)) { if (enemy.takeDamage(bullet.damage)) { playerMoney += 10; moneyText.setText('Money: $' + playerMoney); LK.getSound('enemyHit').play(); enemy.destroy(); enemies.splice(j, 1); } bullet.destroy(); laserBullets.splice(i, 1); break; } } } checkWaveComplete(); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var AircraftBullet = Container.expand(function (startX, startY, targetX, targetY, damage) {
var self = Container.call(this);
self.graphics = self.attachAsset('rocket', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = startX;
self.y = startY;
self.damage = damage;
self.speed = 12;
var dx = targetX - startX;
var dy = targetY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
self.velocityX = dx / distance * self.speed;
self.velocityY = dy / distance * self.speed;
// Calculate rotation angle to point towards target
var angle = Math.atan2(dy, dx) + Math.PI / 2;
self.graphics.rotation = angle;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
};
return self;
});
var ArtilleryBullet = Container.expand(function (startX, startY, targetX, targetY, damage) {
var self = Container.call(this);
self.graphics = self.attachAsset('artilleryShell', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = startX;
self.y = startY;
self.damage = damage;
self.speed = 4;
var dx = targetX - startX;
var dy = targetY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
self.velocityX = dx / distance * self.speed;
self.velocityY = dy / distance * self.speed;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
};
return self;
});
var Boss = Container.expand(function () {
var self = Container.call(this);
self.enemyType = 'boss';
self.health = 500;
self.maxHealth = 500;
self.damage = 50;
self.speed = 0.5;
self.targetX = 1024;
self.targetY = 1366;
self.graphics = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5
});
// Create health bar for boss
self.healthBarBg = self.attachAsset('healthBarBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 1.5
});
self.healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 1.5
});
self.healthBarBg.x = 0;
self.healthBarBg.y = -170;
self.healthBarFill.x = -95;
self.healthBarFill.y = -170;
self.updateHealthBar = function () {
var healthPercent = Math.max(0, self.health / self.maxHealth);
var newWidth = 76 * healthPercent;
self.healthBarFill.width = newWidth;
if (healthPercent > 0.6) {
self.healthBarFill.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
self.healthBarFill.tint = 0xffff00; // Yellow
} else {
self.healthBarFill.tint = 0xff0000; // Red
}
};
self.updateHealthBar();
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
self.health = 0;
return true;
}
return false;
};
self.update = function () {
// Move towards base
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
return self;
});
var Bullet = Container.expand(function (startX, startY, targetX, targetY, damage) {
var self = Container.call(this);
self.graphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = startX;
self.y = startY;
self.damage = damage;
self.speed = 8;
var dx = targetX - startX;
var dy = targetY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
self.velocityX = dx / distance * self.speed;
self.velocityY = dy / distance * self.speed;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
};
return self;
});
var Enemy = Container.expand(function (enemyType) {
var self = Container.call(this);
self.enemyType = enemyType;
self.health = 60;
self.maxHealth = 60;
self.damage = 15;
self.speed = 1;
self.targetX = 1024;
self.targetY = 1366;
if (enemyType === 'tank') {
self.health = 120;
self.maxHealth = 120;
self.damage = 30;
self.speed = 0.8;
self.graphics = self.attachAsset('enemyTank', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (enemyType === 'soldier') {
self.health = 50;
self.maxHealth = 50;
self.damage = 10;
self.speed = 1.2;
self.graphics = self.attachAsset('enemySoldier', {
anchorX: 0.5,
anchorY: 0.5
});
}
// Create health bar
self.healthBarBg = self.attachAsset('healthBarBackground', {
anchorX: 0.5,
anchorY: 0.5
});
self.healthBarFill = self.attachAsset('healthBarFill', {
anchorX: 0,
anchorY: 0.5
});
self.healthBarBg.x = 0;
self.healthBarBg.y = -60;
self.healthBarFill.x = -38;
self.healthBarFill.y = -60;
self.updateHealthBar = function () {
var healthPercent = Math.max(0, self.health / self.maxHealth);
var newWidth = 76 * healthPercent;
self.healthBarFill.width = newWidth;
if (healthPercent > 0.6) {
self.healthBarFill.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
self.healthBarFill.tint = 0xffff00; // Yellow
} else {
self.healthBarFill.tint = 0xff0000; // Red
}
};
self.updateHealthBar();
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
self.health = 0;
return true;
}
return false;
};
self.update = function () {
// Move towards base
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
return self;
});
var GridCell = Container.expand(function (gridX, gridY) {
var self = Container.call(this);
self.gridX = gridX;
self.gridY = gridY;
self.occupied = false;
self.unit = null;
self.graphics = self.attachAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
});
self.down = function (x, y, obj) {
if (selectedUnitType === 'erase') {
// Always allow erase action if there's a unit to erase
if (self.occupied) {
deployUnit(self.gridX, self.gridY);
}
} else {
// Only deploy if cell is not occupied and player has enough money
if (!self.occupied && playerMoney >= selectedUnitCost) {
deployUnit(self.gridX, self.gridY);
}
}
};
return self;
});
var LaserBullet = Container.expand(function (startX, startY, targetX, targetY, damage) {
var self = Container.call(this);
self.graphics = self.attachAsset('laserBeam', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = startX;
self.y = startY;
self.damage = damage;
self.speed = 15;
var dx = targetX - startX;
var dy = targetY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
self.velocityX = dx / distance * self.speed;
self.velocityY = dy / distance * self.speed;
// Calculate rotation angle to point towards target
var angle = Math.atan2(dy, dx) + Math.PI / 2;
self.graphics.rotation = angle;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
};
return self;
});
var SniperBullet = Container.expand(function (startX, startY, targetX, targetY, damage) {
var self = Container.call(this);
self.graphics = self.attachAsset('sniperBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = startX;
self.y = startY;
self.damage = damage;
self.speed = 20;
var dx = targetX - startX;
var dy = targetY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
self.velocityX = dx / distance * self.speed;
self.velocityY = dy / distance * self.speed;
// Calculate rotation angle to point towards target
var angle = Math.atan2(dy, dx) + Math.PI / 2;
self.graphics.rotation = angle;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
};
return self;
});
var TankBullet = Container.expand(function (startX, startY, targetX, targetY, damage) {
var self = Container.call(this);
self.graphics = self.attachAsset('tankShell', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = startX;
self.y = startY;
self.damage = damage;
self.speed = 6;
var dx = targetX - startX;
var dy = targetY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
self.velocityX = dx / distance * self.speed;
self.velocityY = dy / distance * self.speed;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
};
return self;
});
var Unit = Container.expand(function (unitType) {
var self = Container.call(this);
self.unitType = unitType;
self.health = 100;
self.maxHealth = 100;
self.attackDamage = 20;
self.attackRange = 200;
self.attackCooldown = 0;
self.attackSpeed = 60; // frames between attacks
self.cost = 50;
// Set unit-specific properties
if (unitType === 'tank') {
self.health = 150;
self.maxHealth = 150;
self.attackDamage = 40;
self.attackRange = 280;
self.attackSpeed = 90;
self.cost = 100;
self.graphics = self.attachAsset('tank', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (unitType === 'soldier') {
self.health = 80;
self.maxHealth = 80;
self.attackDamage = 15;
self.attackRange = 180;
self.attackSpeed = 45;
self.cost = 30;
self.graphics = self.attachAsset('soldier', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (unitType === 'aircraft') {
self.health = 100;
self.maxHealth = 100;
self.attackDamage = 25;
self.attackRange = 350;
self.attackSpeed = 60;
self.cost = 80;
self.graphics = self.attachAsset('aircraft', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (unitType === 'artillery') {
self.health = 120;
self.maxHealth = 120;
self.attackDamage = 60;
self.attackRange = 450;
self.attackSpeed = 150;
self.cost = 150;
self.graphics = self.attachAsset('artillery', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (unitType === 'sniper') {
self.health = 60;
self.maxHealth = 60;
self.attackDamage = 80;
self.attackRange = 500;
self.attackSpeed = 180;
self.cost = 120;
self.graphics = self.attachAsset('sniper', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (unitType === 'laser') {
self.health = 90;
self.maxHealth = 90;
self.attackDamage = 35;
self.attackRange = 250;
self.attackSpeed = 30;
self.cost = 200;
self.graphics = self.attachAsset('laser', {
anchorX: 0.5,
anchorY: 0.5
});
}
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.health = 0;
return true; // unit destroyed
}
return false;
};
self.canAttack = function () {
return self.attackCooldown <= 0;
};
self.attack = function (target) {
if (self.canAttack()) {
self.attackCooldown = self.attackSpeed;
return true;
}
return false;
};
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2F4F2F
});
/****
* Game Code
****/
var GRID_SIZE = 120;
var GRID_COLS = 8;
var GRID_ROWS = 6;
var GRID_START_X = 400;
var GRID_START_Y = 800;
// Game state variables
var playerMoney = 200;
var currentWave = 1;
var maxWaves = 10;
var enemiesThisWave = 0;
var maxEnemiesThisWave = 3;
var enemySpawnTimer = 0;
var enemySpawnDelay = 120;
var selectedUnitType = 'soldier';
var selectedUnitCost = 30;
var gameState = 'playing'; // 'playing', 'gameOver', 'victory'
var baseHealth = 100;
var maxBaseHealth = 100;
// Game arrays
var playerUnits = [];
var enemies = [];
var bullets = [];
var tankBullets = [];
var aircraftBullets = [];
var artilleryBullets = [];
var sniperBullets = [];
var laserBullets = [];
var gridCells = [];
// Create base
var base = game.addChild(LK.getAsset('base', {
anchorX: 0.5,
anchorY: 0.5
}));
base.x = 1024;
base.y = 1366;
// Create grid
for (var row = 0; row < GRID_ROWS; row++) {
for (var col = 0; col < GRID_COLS; col++) {
var cell = new GridCell(col, row);
cell.x = GRID_START_X + col * GRID_SIZE;
cell.y = GRID_START_Y + row * GRID_SIZE;
gridCells.push(cell);
game.addChild(cell);
}
}
// UI Elements
var moneyText = new Text2('Money: $' + playerMoney, {
size: 60,
fill: 0xFFFFFF
});
moneyText.anchor.set(0, 0);
moneyText.x = 120;
moneyText.y = 100;
LK.gui.topLeft.addChild(moneyText);
var waveText = new Text2('Wave: ' + currentWave + '/' + maxWaves, {
size: 60,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
LK.gui.top.addChild(waveText);
var baseHealthText = new Text2('Base Health: ' + baseHealth, {
size: 50,
fill: 0xFFFFFF
});
baseHealthText.anchor.set(0.5, 0);
baseHealthText.y = 80;
LK.gui.top.addChild(baseHealthText);
// Unit selection buttons
var soldierButton = LK.getAsset('soldier', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
soldierButton.x = 200;
soldierButton.y = 200;
LK.gui.topLeft.addChild(soldierButton);
var tankButton = LK.getAsset('tank', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
tankButton.x = 300;
tankButton.y = 200;
LK.gui.topLeft.addChild(tankButton);
var aircraftButton = LK.getAsset('aircraft', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.3,
scaleY: 1.3
});
aircraftButton.x = 400;
aircraftButton.y = 200;
LK.gui.topLeft.addChild(aircraftButton);
// Cost labels for unit buttons
var soldierCostText = new Text2('$30', {
size: 30,
fill: 0xFFD700
});
soldierCostText.anchor.set(0.5, 0);
soldierCostText.x = 200;
soldierCostText.y = 250;
LK.gui.topLeft.addChild(soldierCostText);
var tankCostText = new Text2('$100', {
size: 30,
fill: 0xFFD700
});
tankCostText.anchor.set(0.5, 0);
tankCostText.x = 300;
tankCostText.y = 250;
LK.gui.topLeft.addChild(tankCostText);
var aircraftCostText = new Text2('$80', {
size: 30,
fill: 0xFFD700
});
aircraftCostText.anchor.set(0.5, 0);
aircraftCostText.x = 400;
aircraftCostText.y = 250;
LK.gui.topLeft.addChild(aircraftCostText);
// Additional tower buttons
var artilleryButton = LK.getAsset('artillery', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
artilleryButton.x = 500;
artilleryButton.y = 200;
LK.gui.topLeft.addChild(artilleryButton);
var sniperButton = LK.getAsset('sniper', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.4,
scaleY: 1.4
});
sniperButton.x = 600;
sniperButton.y = 200;
LK.gui.topLeft.addChild(sniperButton);
var laserButton = LK.getAsset('laser', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.3,
scaleY: 1.3
});
laserButton.x = 700;
laserButton.y = 200;
LK.gui.topLeft.addChild(laserButton);
// Add erase button
var eraseButton = LK.getAsset('eraseButton', {
anchorX: 0.5,
anchorY: 0.5
});
eraseButton.x = 800;
eraseButton.y = 200;
LK.gui.topLeft.addChild(eraseButton);
// Add erase button label
var eraseButtonText = new Text2('X', {
size: 40,
fill: 0xFFFFFF
});
eraseButtonText.anchor.set(0.5, 0.5);
eraseButtonText.x = 800;
eraseButtonText.y = 200;
LK.gui.topLeft.addChild(eraseButtonText);
// Additional cost labels
var artilleryCostText = new Text2('$150', {
size: 30,
fill: 0xFFD700
});
artilleryCostText.anchor.set(0.5, 0);
artilleryCostText.x = 500;
artilleryCostText.y = 250;
LK.gui.topLeft.addChild(artilleryCostText);
var sniperCostText = new Text2('$120', {
size: 30,
fill: 0xFFD700
});
sniperCostText.anchor.set(0.5, 0);
sniperCostText.x = 600;
sniperCostText.y = 250;
LK.gui.topLeft.addChild(sniperCostText);
var laserCostText = new Text2('$200', {
size: 30,
fill: 0xFFD700
});
laserCostText.anchor.set(0.5, 0);
laserCostText.x = 700;
laserCostText.y = 250;
LK.gui.topLeft.addChild(laserCostText);
var eraseCostText = new Text2('ERASE', {
size: 25,
fill: 0xFFD700
});
eraseCostText.anchor.set(0.5, 0);
eraseCostText.x = 800;
eraseCostText.y = 250;
LK.gui.topLeft.addChild(eraseCostText);
// Button event handlers
soldierButton.down = function () {
selectedUnitType = 'soldier';
selectedUnitCost = 30;
updateButtonSelection();
};
tankButton.down = function () {
selectedUnitType = 'tank';
selectedUnitCost = 100;
updateButtonSelection();
};
aircraftButton.down = function () {
selectedUnitType = 'aircraft';
selectedUnitCost = 80;
updateButtonSelection();
};
artilleryButton.down = function () {
selectedUnitType = 'artillery';
selectedUnitCost = 150;
updateButtonSelection();
};
sniperButton.down = function () {
selectedUnitType = 'sniper';
selectedUnitCost = 120;
updateButtonSelection();
};
laserButton.down = function () {
selectedUnitType = 'laser';
selectedUnitCost = 200;
updateButtonSelection();
};
eraseButton.down = function () {
selectedUnitType = 'erase';
selectedUnitCost = 0;
updateButtonSelection();
};
function updateButtonSelection() {
soldierButton.alpha = selectedUnitType === 'soldier' ? 1.0 : 0.6;
tankButton.alpha = selectedUnitType === 'tank' ? 1.0 : 0.6;
aircraftButton.alpha = selectedUnitType === 'aircraft' ? 1.0 : 0.6;
artilleryButton.alpha = selectedUnitType === 'artillery' ? 1.0 : 0.6;
sniperButton.alpha = selectedUnitType === 'sniper' ? 1.0 : 0.6;
laserButton.alpha = selectedUnitType === 'laser' ? 1.0 : 0.6;
eraseButton.alpha = selectedUnitType === 'erase' ? 1.0 : 0.6;
// Update cost label highlighting
soldierCostText.alpha = selectedUnitType === 'soldier' ? 1.0 : 0.6;
tankCostText.alpha = selectedUnitType === 'tank' ? 1.0 : 0.6;
aircraftCostText.alpha = selectedUnitType === 'aircraft' ? 1.0 : 0.6;
artilleryCostText.alpha = selectedUnitType === 'artillery' ? 1.0 : 0.6;
sniperCostText.alpha = selectedUnitType === 'sniper' ? 1.0 : 0.6;
laserCostText.alpha = selectedUnitType === 'laser' ? 1.0 : 0.6;
eraseCostText.alpha = selectedUnitType === 'erase' ? 1.0 : 0.6;
}
function deployUnit(gridX, gridY) {
var cellIndex = gridY * GRID_COLS + gridX;
var cell = gridCells[cellIndex];
if (selectedUnitType === 'erase') {
// Erase existing tower
if (cell.occupied && cell.unit) {
// Remove unit from playerUnits array
for (var i = playerUnits.length - 1; i >= 0; i--) {
if (playerUnits[i] === cell.unit) {
cell.unit.destroy();
playerUnits.splice(i, 1);
break;
}
}
// Mark cell as unoccupied
cell.occupied = false;
cell.unit = null;
}
} else {
// Deploy new unit
if (!cell.occupied && playerMoney >= selectedUnitCost) {
var unit = new Unit(selectedUnitType);
unit.x = GRID_START_X + gridX * GRID_SIZE;
unit.y = GRID_START_Y + gridY * GRID_SIZE;
playerUnits.push(unit);
game.addChild(unit);
// Mark grid cell as occupied
cell.occupied = true;
cell.unit = unit;
playerMoney -= selectedUnitCost;
moneyText.setText('Money: $' + playerMoney);
LK.getSound('unitDeploy').play();
}
}
}
function spawnEnemy() {
var enemy;
if (currentWave === 10 && enemiesThisWave === 0) {
// Spawn boss on wave 10
enemy = new Boss();
} else {
var enemyType = Math.random() < 0.6 ? 'soldier' : 'tank';
enemy = new Enemy(enemyType);
}
// Spawn from top of screen
enemy.x = Math.random() * 1500 + 200;
enemy.y = 100;
enemies.push(enemy);
game.addChild(enemy);
enemiesThisWave++;
}
function findNearestEnemy(unit) {
var nearestEnemy = null;
var nearestDistance = unit.attackRange;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var dx = enemy.x - unit.x;
var dy = enemy.y - unit.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= nearestDistance) {
nearestEnemy = enemy;
nearestDistance = distance;
}
}
return nearestEnemy;
}
function createBullet(startX, startY, targetX, targetY, damage) {
var bullet = new Bullet(startX, startY, targetX, targetY, damage);
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
}
function createTankBullet(startX, startY, targetX, targetY, damage) {
var bullet = new TankBullet(startX, startY, targetX, targetY, damage);
tankBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
}
function createAircraftBullet(startX, startY, targetX, targetY, damage) {
var bullet = new AircraftBullet(startX, startY, targetX, targetY, damage);
aircraftBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
}
function createArtilleryBullet(startX, startY, targetX, targetY, damage) {
var bullet = new ArtilleryBullet(startX, startY, targetX, targetY, damage);
artilleryBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
}
function createSniperBullet(startX, startY, targetX, targetY, damage) {
var bullet = new SniperBullet(startX, startY, targetX, targetY, damage);
sniperBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
}
function createLaserBullet(startX, startY, targetX, targetY, damage) {
var bullet = new LaserBullet(startX, startY, targetX, targetY, damage);
laserBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
}
function checkWaveComplete() {
if (enemiesThisWave >= maxEnemiesThisWave && enemies.length === 0) {
currentWave++;
if (currentWave > maxWaves) {
// After wave 10, loop back to wave 2
currentWave = 2;
}
// Start next wave
enemiesThisWave = 0;
if (currentWave === 10) {
// Wave 10: only spawn the boss
maxEnemiesThisWave = 1;
} else {
maxEnemiesThisWave = Math.min(3 + currentWave, 15);
}
enemySpawnDelay = Math.max(60, 120 - currentWave * 5);
playerMoney += 50 + currentWave * 10;
waveText.setText('Wave: ' + currentWave + '/' + maxWaves);
moneyText.setText('Money: $' + playerMoney);
showWaveSection();
}
}
// Wave section display for retreat scenarios
var waveSectionText = new Text2('SECTION ' + currentWave, {
size: 120,
fill: 0xFFFF00
});
waveSectionText.anchor.set(0.5, 0.5);
waveSectionText.x = 1024;
waveSectionText.y = 400;
waveSectionText.alpha = 0;
game.addChild(waveSectionText);
function showWaveSection() {
waveSectionText.setText('SECTION ' + currentWave);
waveSectionText.alpha = 1;
tween(waveSectionText, {
alpha: 0
}, {
duration: 3000,
easing: tween.easeOut
});
}
// Initialize selection
updateButtonSelection();
game.update = function () {
if (gameState !== 'playing') return;
// Spawn enemies
if (enemiesThisWave < maxEnemiesThisWave) {
enemySpawnTimer++;
if (enemySpawnTimer >= enemySpawnDelay) {
spawnEnemy();
enemySpawnTimer = 0;
}
}
// Update units and handle combat
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var target = findNearestEnemy(unit);
if (target && unit.canAttack()) {
// Calculate angle to target for tower rotation
var dx = target.x - unit.x;
var dy = target.y - unit.y;
var targetAngle = Math.atan2(dy, dx);
// Smooth rotation using tween
tween.stop(unit.graphics, {
rotation: true
});
tween(unit.graphics, {
rotation: targetAngle
}, {
duration: 200,
easing: tween.easeOut
});
if (unit.attack(target)) {
if (unit.unitType === 'tank') {
createTankBullet(unit.x, unit.y, target.x, target.y, unit.attackDamage);
} else if (unit.unitType === 'aircraft') {
createAircraftBullet(unit.x, unit.y, target.x, target.y, unit.attackDamage);
} else if (unit.unitType === 'artillery') {
createArtilleryBullet(unit.x, unit.y, target.x, target.y, unit.attackDamage);
} else if (unit.unitType === 'sniper') {
createSniperBullet(unit.x, unit.y, target.x, target.y, unit.attackDamage);
} else if (unit.unitType === 'laser') {
createLaserBullet(unit.x, unit.y, target.x, target.y, unit.attackDamage);
} else {
createBullet(unit.x, unit.y, target.x, target.y, unit.attackDamage);
}
}
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
// Check if enemy reached base
var distanceToBase = Math.sqrt(Math.pow(enemy.x - base.x, 2) + Math.pow(enemy.y - base.y, 2));
if (distanceToBase < 100) {
baseHealth -= enemy.damage;
baseHealthText.setText('Base Health: ' + Math.max(0, baseHealth));
showWaveSection(); // Show section when under attack
if (baseHealth <= 0) {
gameState = 'gameOver';
LK.showGameOver();
return;
}
enemy.destroy();
enemies.splice(i, 1);
continue;
}
}
// Update bullets and check collisions
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
// Check if bullet is out of bounds
if (bullet.x < 0 || bullet.x > 2048 || bullet.y < 0 || bullet.y > 2732) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check bullet-enemy collisions
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
if (enemy.takeDamage(bullet.damage)) {
playerMoney += 10;
moneyText.setText('Money: $' + playerMoney);
LK.getSound('enemyHit').play();
enemy.destroy();
enemies.splice(j, 1);
}
bullet.destroy();
bullets.splice(i, 1);
break;
}
}
}
// Update tank bullets and check collisions
for (var i = tankBullets.length - 1; i >= 0; i--) {
var bullet = tankBullets[i];
// Check if bullet is out of bounds
if (bullet.x < 0 || bullet.x > 2048 || bullet.y < 0 || bullet.y > 2732) {
bullet.destroy();
tankBullets.splice(i, 1);
continue;
}
// Check bullet-enemy collisions
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
if (enemy.takeDamage(bullet.damage)) {
playerMoney += 10;
moneyText.setText('Money: $' + playerMoney);
LK.getSound('enemyHit').play();
enemy.destroy();
enemies.splice(j, 1);
}
bullet.destroy();
tankBullets.splice(i, 1);
break;
}
}
}
// Update aircraft bullets and check collisions
for (var i = aircraftBullets.length - 1; i >= 0; i--) {
var bullet = aircraftBullets[i];
// Check if bullet is out of bounds
if (bullet.x < 0 || bullet.x > 2048 || bullet.y < 0 || bullet.y > 2732) {
bullet.destroy();
aircraftBullets.splice(i, 1);
continue;
}
// Check bullet-enemy collisions
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
if (enemy.takeDamage(bullet.damage)) {
playerMoney += 10;
moneyText.setText('Money: $' + playerMoney);
LK.getSound('enemyHit').play();
enemy.destroy();
enemies.splice(j, 1);
}
bullet.destroy();
aircraftBullets.splice(i, 1);
break;
}
}
}
// Update artillery bullets and check collisions
for (var i = artilleryBullets.length - 1; i >= 0; i--) {
var bullet = artilleryBullets[i];
// Check if bullet is out of bounds
if (bullet.x < 0 || bullet.x > 2048 || bullet.y < 0 || bullet.y > 2732) {
bullet.destroy();
artilleryBullets.splice(i, 1);
continue;
}
// Check bullet-enemy collisions
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
if (enemy.takeDamage(bullet.damage)) {
playerMoney += 10;
moneyText.setText('Money: $' + playerMoney);
LK.getSound('enemyHit').play();
enemy.destroy();
enemies.splice(j, 1);
}
bullet.destroy();
artilleryBullets.splice(i, 1);
break;
}
}
}
// Update sniper bullets and check collisions
for (var i = sniperBullets.length - 1; i >= 0; i--) {
var bullet = sniperBullets[i];
// Check if bullet is out of bounds
if (bullet.x < 0 || bullet.x > 2048 || bullet.y < 0 || bullet.y > 2732) {
bullet.destroy();
sniperBullets.splice(i, 1);
continue;
}
// Check bullet-enemy collisions
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
if (enemy.takeDamage(bullet.damage)) {
playerMoney += 10;
moneyText.setText('Money: $' + playerMoney);
LK.getSound('enemyHit').play();
enemy.destroy();
enemies.splice(j, 1);
}
bullet.destroy();
sniperBullets.splice(i, 1);
break;
}
}
}
// Update laser bullets and check collisions
for (var i = laserBullets.length - 1; i >= 0; i--) {
var bullet = laserBullets[i];
// Check if bullet is out of bounds
if (bullet.x < 0 || bullet.x > 2048 || bullet.y < 0 || bullet.y > 2732) {
bullet.destroy();
laserBullets.splice(i, 1);
continue;
}
// Check bullet-enemy collisions
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
if (enemy.takeDamage(bullet.damage)) {
playerMoney += 10;
moneyText.setText('Money: $' + playerMoney);
LK.getSound('enemyHit').play();
enemy.destroy();
enemies.splice(j, 1);
}
bullet.destroy();
laserBullets.splice(i, 1);
break;
}
}
}
checkWaveComplete();
};
make a bird's eye view tank. In-Game asset. 2d. High contrast. No shadows
create ultra realastic bullet. In-Game asset. 2d. High contrast. No shadows
Make a bird's eye view airplane.. In-Game asset. 2d. High contrast. No shadows
Create a beautiful place with grassy, earthy views, but it should not have a roof; it should have walls.. In-Game asset. 2d. High contrast. No shadows
The enemy soldiers should look at us from a bird's eye view, but the soldier should look forward with a weapon in hand.. In-Game asset. 2d. High contrast. No shadows
Let's look at soldiers from a bird's eye view, but the soldier should look back and have a weapon in hand.. In-Game asset. 2d. High contrast. No shadows
create a missile rocket. In-Game asset. 2d. High contrast. No shadows
create bullet. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
create artillery. In-Game asset. 2d. High contrast. No shadows
create motionless giant rock. In-Game asset. 2d. High contrast. No shadows
create gridcell. In-Game asset. 2d. High contrast. No shadows
bird's eye view laser weapon 1024x. In-Game asset. 2d. High contrast. No shadows