/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0 }); /**** * Classes ****/ var Enemy = Container.expand(function (type) { var self = Container.call(this); self.type = type || 'basicEnemy'; self.enemyGraphic = self.attachAsset(self.type, { anchorX: 0.5, anchorY: 0.5 }); switch (self.type) { case 'basicEnemy': self.maxHealth = 100; self.speed = 1.5; self.goldValue = 10; break; case 'fastEnemy': self.maxHealth = 75; self.speed = 2.5; self.goldValue = 15; break; case 'tankEnemy': self.maxHealth = 250; self.speed = 0.8; self.goldValue = 25; break; case 'bossEnemy': self.maxHealth = 500; self.speed = 1.0; self.goldValue = 100; break; default: self.maxHealth = 100; self.speed = 1.5; self.goldValue = 10; } self.health = self.maxHealth; self.currentPathIndex = 0; self.reachedEnd = false; // Health bar self.healthBarBackground = new Container(); self.healthBarBackground.width = self.enemyGraphic.width * 1.2; self.healthBarBackground.height = 10; self.healthBarBackground.x = -self.healthBarBackground.width / 2; self.healthBarBackground.y = -self.enemyGraphic.height / 2 - 15; self.addChild(self.healthBarBackground); self.healthBar = new Container(); self.healthBar.width = self.healthBarBackground.width; self.healthBar.height = self.healthBarBackground.height; self.healthBar.x = self.healthBarBackground.x; self.healthBar.y = self.healthBarBackground.y; self.addChild(self.healthBar); self.updateHealthBar = function () { var healthPercentage = self.health / self.maxHealth; self.healthBar.width = self.healthBarBackground.width * healthPercentage; // Change color based on health percentage if (healthPercentage > 0.6) { self.healthBar.tint = 0x00FF00; // Green } else if (healthPercentage > 0.3) { self.healthBar.tint = 0xFFFF00; // Yellow } else { self.healthBar.tint = 0xFF0000; // Red } }; self.takeDamage = function (amount) { self.health -= amount; self.updateHealthBar(); // Briefly flash the enemy when taking damage tween(self.enemyGraphic, { alpha: 0.5 }, { duration: 100, onFinish: function onFinish() { tween(self.enemyGraphic, { alpha: 1 }, { duration: 100 }); } }); if (self.health <= 0) { return true; // Enemy is dead } return false; // Enemy is still alive }; self.moveAlongPath = function (path) { if (self.currentPathIndex >= path.length) { self.reachedEnd = true; return true; } var targetX = path[self.currentPathIndex].x; var targetY = path[self.currentPathIndex].y; var dx = targetX - self.x; var dy = targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 5) { self.currentPathIndex++; return false; } var normalizedX = dx / distance; var normalizedY = dy / distance; self.x += normalizedX * self.speed; self.y += normalizedY * self.speed; return false; }; self.updateHealthBar(); return self; }); var MapTile = Container.expand(function (tileType) { var self = Container.call(this); self.tileType = tileType || 'grass'; self.canPlaceTower = self.tileType === 'grass'; var tileGraphic = self.attachAsset(self.tileType, { anchorX: 0.5, anchorY: 0.5 }); self.setHighlight = function (highlight) { if (highlight && self.canPlaceTower) { tileGraphic.alpha = 0.8; } else { tileGraphic.alpha = 1.0; } }; return self; }); var Projectile = Container.expand(function (targetEnemy, damage, speed, type) { var self = Container.call(this); self.targetEnemy = targetEnemy; self.damage = damage || 25; self.speed = speed || 10; self.projectileType = type || 'projectile'; var projectileGraphic = self.attachAsset(self.projectileType, { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { if (!self.targetEnemy || !self.targetEnemy.parent) { self.destroy(); return; } var dx = self.targetEnemy.x - self.x; var dy = self.targetEnemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 15) { // Hit enemy if (self.targetEnemy.takeDamage(self.damage)) { // Enemy is dead gameState.gold += self.targetEnemy.goldValue; gameState.score += self.targetEnemy.goldValue; updateGoldText(); updateScoreText(); LK.getSound('enemyDeath').play(); var enemyIndex = gameState.enemies.indexOf(self.targetEnemy); if (enemyIndex !== -1) { self.targetEnemy.destroy(); gameState.enemies.splice(enemyIndex, 1); } } self.destroy(); return; } // Move toward enemy var normalizedX = dx / distance; var normalizedY = dy / distance; self.x += normalizedX * self.speed; self.y += normalizedY * self.speed; // Rotate projectile to face direction of travel self.rotation = Math.atan2(dy, dx) + Math.PI / 2; }; return self; }); var Tower = Container.expand(function (type) { var self = Container.call(this); self.towerType = type || 'baseTower'; self.baseTower = self.attachAsset('baseTower', { anchorX: 0.5, anchorY: 0.5 }); self.towerGraphic = self.attachAsset(self.towerType, { anchorX: 0.5, anchorY: 0.5, y: -10 }); // Tower properties based on type switch (self.towerType) { case 'cannonTower': self.damage = 40; self.range = 200; self.attackSpeed = 1.5; // Attacks per second self.cost = 100; self.projectileSpeed = 8; self.targetingStrategy = 'first'; // Target first enemy in path break; case 'archerTower': self.damage = 20; self.range = 300; self.attackSpeed = 3; // Attacks per second self.cost = 150; self.projectileSpeed = 15; self.targetingStrategy = 'first'; break; case 'magicTower': self.damage = 30; self.range = 250; self.attackSpeed = 2; // Attacks per second self.cost = 200; self.projectileSpeed = 10; self.targetingStrategy = 'strongest'; // Target enemy with most health break; default: self.damage = 25; self.range = 200; self.attackSpeed = 1; // Attacks per second self.cost = 50; self.projectileSpeed = 8; self.targetingStrategy = 'first'; } self.level = 1; self.attackCooldown = 0; self.rangeIndicator = null; self.selected = false; // Show range indicator when tower is selected self.showRange = function (show) { if (show && !self.rangeIndicator) { self.rangeIndicator = self.attachAsset('rangeIndicator', { anchorX: 0.5, anchorY: 0.5, alpha: 0.3, scaleX: self.range / 300, // Scale to match tower's range scaleY: self.range / 300 }); self.rangeIndicator.zIndex = -1; } else if (!show && self.rangeIndicator) { self.removeChild(self.rangeIndicator); self.rangeIndicator = null; } }; self.upgrade = function () { if (self.level >= 3) return false; // Max level reached var upgradeCost = self.cost * self.level; if (gameState.gold < upgradeCost) return false; // Not enough gold gameState.gold -= upgradeCost; self.level++; // Upgrade stats self.damage *= 1.5; self.range *= 1.2; self.attackSpeed *= 1.2; // Update visuals for upgraded tower self.towerGraphic.scale.set(1 + (self.level - 1) * 0.2); // Update range indicator if visible if (self.rangeIndicator) { self.removeChild(self.rangeIndicator); self.showRange(true); } updateGoldText(); return true; }; self.findTarget = function (enemies) { var target = null; var maxPriority = -1; var minDistance = Infinity; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= self.range) { var priority = 0; switch (self.targetingStrategy) { case 'first': priority = enemy.currentPathIndex + (1 - distance / self.range); break; case 'strongest': priority = enemy.health; break; case 'closest': priority = self.range - distance; break; } if (priority > maxPriority || priority === maxPriority && distance < minDistance) { maxPriority = priority; minDistance = distance; target = enemy; } } } return target; }; self.attackEnemy = function (enemy) { if (!enemy) return; // Create projectile var projectile = new Projectile(enemy, self.damage, self.projectileSpeed); projectile.x = self.x; projectile.y = self.y; game.addChild(projectile); gameState.projectiles.push(projectile); LK.getSound('projectileShot').play(); // Reset cooldown self.attackCooldown = 60 / self.attackSpeed; // Tower attack animation tween(self.towerGraphic, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100, onFinish: function onFinish() { tween(self.towerGraphic, { scaleX: 1 + (self.level - 1) * 0.2, scaleY: 1 + (self.level - 1) * 0.2 }, { duration: 100 }); } }); }; self.update = function () { if (self.attackCooldown > 0) { self.attackCooldown--; } else { var target = self.findTarget(gameState.enemies); if (target) { self.attackEnemy(target); } } }; self.down = function (x, y, obj) { // Select this tower, deselect others selectTower(self); }; return self; }); var TowerButton = Container.expand(function (towerType, cost) { var self = Container.call(this); self.towerType = towerType; self.cost = cost; var button = self.attachAsset('uiButton', { anchorX: 0.5, anchorY: 0.5 }); var towerPreview = self.attachAsset(towerType, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); var costText = new Text2(cost.toString(), { size: 24, fill: 0xFFFFFF }); costText.anchor.set(0.5, 0.5); costText.y = 30; self.addChild(costText); self.setEnabled = function (enabled) { button.alpha = enabled ? 1.0 : 0.5; towerPreview.alpha = enabled ? 1.0 : 0.5; self.enabled = enabled; }; self.down = function (x, y, obj) { if (!self.enabled) return; // Set the selected tower type for placement gameState.selectedTowerType = self.towerType; gameState.placingTower = true; // Deselect any selected tower if (gameState.selectedTower) { gameState.selectedTower.showRange(false); gameState.selectedTower = null; } // Show tower placeholders for (var i = 0; i < gameState.map.length; i++) { for (var j = 0; j < gameState.map[i].length; j++) { var tile = gameState.map[i][j]; if (tile.canPlaceTower && !tile.tower) { tile.setHighlight(true); } } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2B5329 // Green background for the game }); /**** * Game Code ****/ // Game state variables // Tower assets // Enemy assets // Map and UI assets // Sounds // Music var gameState = { gold: 200, lives: 10, score: 0, wave: 0, enemyCount: 0, waveInProgress: false, selectedTower: null, selectedTowerType: null, placingTower: false, map: [], towers: [], enemies: [], projectiles: [], path: [], tileSize: 100, mapWidth: 10, mapHeight: 10 }; // Create UI elements var scoreText = new Text2('Score: 0', { size: 36, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); var goldText = new Text2('Gold: ' + gameState.gold, { size: 36, fill: 0xFFD700 }); goldText.anchor.set(0, 0); goldText.x = 150; LK.gui.topLeft.addChild(goldText); var livesText = new Text2('Lives: ' + gameState.lives, { size: 36, fill: 0xFF0000 }); livesText.anchor.set(1, 0); livesText.x = -150; LK.gui.topRight.addChild(livesText); var waveText = new Text2('Wave: ' + gameState.wave, { size: 36, fill: 0xFFFFFF }); waveText.anchor.set(0.5, 0); waveText.y = 50; LK.gui.top.addChild(waveText); var nextWaveButton = new Container(); var nextWaveButtonGraphic = nextWaveButton.attachAsset('uiButton', { anchorX: 0.5, anchorY: 0.5 }); var nextWaveButtonText = new Text2('Next Wave', { size: 30, fill: 0xFFFFFF }); nextWaveButtonText.anchor.set(0.5, 0.5); nextWaveButton.addChild(nextWaveButtonText); nextWaveButton.x = 0; nextWaveButton.y = -100; LK.gui.bottom.addChild(nextWaveButton); // Create tower buttons UI var towerButtonsContainer = new Container(); towerButtonsContainer.y = -200; LK.gui.bottom.addChild(towerButtonsContainer); var towerButtons = [{ type: 'cannonTower', cost: 100 }, { type: 'archerTower', cost: 150 }, { type: 'magicTower', cost: 200 }]; var buttonSpacing = 220; for (var i = 0; i < towerButtons.length; i++) { var button = new TowerButton(towerButtons[i].type, towerButtons[i].cost); button.x = (i - (towerButtons.length - 1) / 2) * buttonSpacing; towerButtonsContainer.addChild(button); } // Tower upgrade button and sell button var upgradeButton = new Container(); var upgradeButtonGraphic = upgradeButton.attachAsset('uiButton', { anchorX: 0.5, anchorY: 0.5 }); var upgradeButtonText = new Text2('Upgrade', { size: 30, fill: 0xFFFFFF }); upgradeButtonText.anchor.set(0.5, 0.5); upgradeButton.addChild(upgradeButtonText); upgradeButton.visible = false; upgradeButton.x = -120; upgradeButton.y = -300; LK.gui.bottom.addChild(upgradeButton); var sellButton = new Container(); var sellButtonGraphic = sellButton.attachAsset('uiButton', { anchorX: 0.5, anchorY: 0.5 }); var sellButtonText = new Text2('Sell', { size: 30, fill: 0xFFFFFF }); sellButtonText.anchor.set(0.5, 0.5); sellButton.addChild(sellButtonText); sellButton.visible = false; sellButton.x = 120; sellButton.y = -300; LK.gui.bottom.addChild(sellButton); // Update functions for UI elements function updateScoreText() { scoreText.setText('Score: ' + gameState.score); } function updateGoldText() { goldText.setText('Gold: ' + gameState.gold); // Update tower buttons enabled/disabled state based on gold for (var i = 0; i < towerButtonsContainer.children.length; i++) { var button = towerButtonsContainer.children[i]; button.setEnabled(gameState.gold >= button.cost); } } function updateLivesText() { livesText.setText('Lives: ' + gameState.lives); } function updateWaveText() { waveText.setText('Wave: ' + gameState.wave); } // Generate the game map with path function generateMap() { // Define map layout var layout = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 1, 0, 0, 0, 0, 0, 1, 0, 0], [0, 1, 0, 0, 0, 0, 0, 1, 0, 0], [0, 1, 0, 1, 1, 1, 0, 1, 0, 0], [0, 1, 0, 1, 0, 1, 0, 1, 0, 0], [0, 1, 0, 1, 0, 1, 0, 1, 0, 0], [0, 1, 0, 1, 0, 1, 1, 1, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 2]]; var startX = 0; var startY = 0; var endX = 0; var endY = 0; // X offset to center map on screen var offsetX = (2048 - gameState.mapWidth * gameState.tileSize) / 2; var offsetY = (2732 - gameState.mapHeight * gameState.tileSize) / 2 - 150; // Offset to account for UI // Create map tiles based on layout for (var i = 0; i < gameState.mapHeight; i++) { gameState.map[i] = []; for (var j = 0; j < gameState.mapWidth; j++) { var tileType = layout[i][j] === 1 ? 'path' : 'grass'; // Handle start point (entry point) if (layout[i][j] === 1 && (i === 0 || j === 0 || i === gameState.mapHeight - 1 || j === gameState.mapWidth - 1)) { if (gameState.path.length === 0) { startX = j * gameState.tileSize + offsetX + gameState.tileSize / 2; startY = i * gameState.tileSize + offsetY + gameState.tileSize / 2; } } // Handle end point (base) if (layout[i][j] === 2) { tileType = 'baseStructure'; endX = j * gameState.tileSize + offsetX + gameState.tileSize / 2; endY = i * gameState.tileSize + offsetY + gameState.tileSize / 2; } var tile = new MapTile(tileType); tile.x = j * gameState.tileSize + offsetX + gameState.tileSize / 2; tile.y = i * gameState.tileSize + offsetY + gameState.tileSize / 2; tile.gridX = j; tile.gridY = i; tile.tower = null; gameState.map[i][j] = tile; game.addChild(tile); // Add to path if it's a path tile if (tileType === 'path') { gameState.path.push({ x: tile.x, y: tile.y, tile: tile }); } } } // Sort path from start to end (using a simplified approach for this straight path) // This is a simplification - a real game would need a proper path finding algorithm gameState.path.sort(function (a, b) { var distA = Math.sqrt(Math.pow(a.x - startX, 2) + Math.pow(a.y - startY, 2)); var distB = Math.sqrt(Math.pow(b.x - startX, 2) + Math.pow(b.y - startY, 2)); return distA - distB; }); // The first point should be off-screen for enemy spawn gameState.path.unshift({ x: startX - gameState.tileSize, y: startY, offScreen: true }); // The last point is the base gameState.path.push({ x: endX, y: endY, isBase: true }); } // Start a new wave of enemies function startWave() { if (gameState.waveInProgress) return; gameState.wave++; updateWaveText(); gameState.waveInProgress = true; gameState.enemyCount = 5 + gameState.wave * 2; // More enemies per wave LK.getSound('waveStart').play(); var spawnInterval = LK.setInterval(function () { spawnEnemy(); gameState.enemyCount--; if (gameState.enemyCount <= 0) { LK.clearInterval(spawnInterval); // Wave is considered over when all enemies are spawned // The actual check for wave completion is in the update function } }, 1500 - gameState.wave * 100); // Spawn faster in later waves } // Spawn an enemy function spawnEnemy() { var enemyTypes = ['basicEnemy']; // Add more enemy types as waves progress if (gameState.wave >= 3) enemyTypes.push('fastEnemy'); if (gameState.wave >= 5) enemyTypes.push('tankEnemy'); if (gameState.wave >= 10 && gameState.wave % 5 === 0) enemyTypes.push('bossEnemy'); var randomType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)]; // Always spawn a boss on wave 5, 10, 15, etc. if (gameState.wave % 5 === 0 && gameState.enemyCount === 0) { randomType = 'bossEnemy'; } var enemy = new Enemy(randomType); // Spawn at the start of the path // Use the first path point (index 0), which is off-screen var startPoint = gameState.path[0]; enemy.x = startPoint.x; enemy.y = startPoint.y; // Set the initial path index to 1 so enemies start moving toward the first visible point enemy.currentPathIndex = 1; gameState.enemies.push(enemy); game.addChild(enemy); } // Check if wave is complete function checkWaveComplete() { if (gameState.waveInProgress && gameState.enemyCount <= 0 && gameState.enemies.length === 0) { gameState.waveInProgress = false; gameState.gold += 50 + gameState.wave * 10; // Bonus gold for completing wave updateGoldText(); } } // Place a tower on the map function placeTower(gridX, gridY) { var tile = gameState.map[gridY][gridX]; if (!tile.canPlaceTower || tile.tower) return false; var towerCost = 0; switch (gameState.selectedTowerType) { case 'cannonTower': towerCost = 100; break; case 'archerTower': towerCost = 150; break; case 'magicTower': towerCost = 200; break; default: towerCost = 100; } if (gameState.gold < towerCost) return false; var tower = new Tower(gameState.selectedTowerType); tower.x = tile.x; tower.y = tile.y; tower.gridX = gridX; tower.gridY = gridY; tile.tower = tower; gameState.towers.push(tower); game.addChild(tower); gameState.gold -= towerCost; updateGoldText(); LK.getSound('buildTower').play(); // Clear tower placement mode clearTowerPlacement(); return true; } // Select a tower function selectTower(tower) { // Deselect previously selected tower if (gameState.selectedTower) { gameState.selectedTower.showRange(false); } // Clear tower placement mode clearTowerPlacement(); // Select new tower gameState.selectedTower = tower; tower.showRange(true); // Show upgrade and sell buttons upgradeButton.visible = true; sellButton.visible = true; } // Clear tower placement mode function clearTowerPlacement() { gameState.placingTower = false; gameState.selectedTowerType = null; // Remove highlights from all tiles for (var i = 0; i < gameState.map.length; i++) { for (var j = 0; j < gameState.map[i].length; j++) { gameState.map[i][j].setHighlight(false); } } } // Handle game over function checkGameOver() { if (gameState.lives <= 0) { // Update high score if (gameState.score > storage.highScore) { storage.highScore = gameState.score; } // Game over LK.getSound('gameOver').play(); LK.showGameOver(); } } // Event handlers nextWaveButton.down = function () { if (!gameState.waveInProgress) { startWave(); } }; upgradeButton.down = function () { if (gameState.selectedTower) { if (gameState.selectedTower.upgrade()) { // Success - tower was upgraded } else { // Failed to upgrade (either max level or not enough gold) } } }; sellButton.down = function () { if (gameState.selectedTower) { // Refund 70% of tower cost var refundAmount = Math.floor(gameState.selectedTower.cost * 0.7); gameState.gold += refundAmount; updateGoldText(); // Remove tower from map and arrays var tile = gameState.map[gameState.selectedTower.gridY][gameState.selectedTower.gridX]; tile.tower = null; var towerIndex = gameState.towers.indexOf(gameState.selectedTower); if (towerIndex !== -1) { gameState.towers.splice(towerIndex, 1); } gameState.selectedTower.destroy(); gameState.selectedTower = null; // Hide buttons upgradeButton.visible = false; sellButton.visible = false; } }; game.down = function (x, y, obj) { if (gameState.placingTower) { // Find which tile was clicked for (var i = 0; i < gameState.map.length; i++) { for (var j = 0; j < gameState.map[i].length; j++) { var tile = gameState.map[i][j]; var dx = tile.x - x; var dy = tile.y - y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < gameState.tileSize / 2) { if (placeTower(j, i)) { return; // Tower placed successfully } } } } // Clicked outside valid tile, cancel placement clearTowerPlacement(); } else { // Check if clicked on a tower var towerClicked = false; for (var i = 0; i < gameState.towers.length; i++) { var tower = gameState.towers[i]; var dx = tower.x - x; var dy = tower.y - y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < gameState.tileSize / 2) { selectTower(tower); towerClicked = true; break; } } // If clicked outside tower, deselect current tower if (!towerClicked && gameState.selectedTower) { gameState.selectedTower.showRange(false); gameState.selectedTower = null; upgradeButton.visible = false; sellButton.visible = false; } } }; // Main update function game.update = function () { // Update all towers for (var i = 0; i < gameState.towers.length; i++) { gameState.towers[i].update(); } // Update all projectiles for (var i = gameState.projectiles.length - 1; i >= 0; i--) { gameState.projectiles[i].update(); // Check if projectile needs to be removed if (!gameState.projectiles[i].parent) { gameState.projectiles.splice(i, 1); } } // Update all enemies for (var i = gameState.enemies.length - 1; i >= 0; i--) { var enemy = gameState.enemies[i]; var reachedEnd = enemy.moveAlongPath(gameState.path); if (reachedEnd) { // Enemy reached the base, player loses a life gameState.lives--; updateLivesText(); LK.getSound('playerDamage').play(); // Remove enemy enemy.destroy(); gameState.enemies.splice(i, 1); // Flash screen LK.effects.flashScreen(0xFF0000, 500); // Check for game over checkGameOver(); } } // Check if wave is complete checkWaveComplete(); // Update UI button states nextWaveButton.alpha = gameState.waveInProgress ? 0.5 : 1.0; }; // Initialize game function initGame() { // Reset game state gameState.gold = 200; gameState.lives = 10; gameState.score = 0; gameState.wave = 0; gameState.waveInProgress = false; gameState.selectedTower = null; gameState.selectedTowerType = null; gameState.placingTower = false; gameState.map = []; gameState.towers = []; gameState.enemies = []; gameState.projectiles = []; gameState.path = []; // Update UI updateScoreText(); updateGoldText(); updateLivesText(); updateWaveText(); // Generate map generateMap(); // Hide upgrade and sell buttons upgradeButton.visible = false; sellButton.visible = false; // Play background music LK.playMusic('bgMusic'); } // Start the game initGame();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0
});
/****
* Classes
****/
var Enemy = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'basicEnemy';
self.enemyGraphic = self.attachAsset(self.type, {
anchorX: 0.5,
anchorY: 0.5
});
switch (self.type) {
case 'basicEnemy':
self.maxHealth = 100;
self.speed = 1.5;
self.goldValue = 10;
break;
case 'fastEnemy':
self.maxHealth = 75;
self.speed = 2.5;
self.goldValue = 15;
break;
case 'tankEnemy':
self.maxHealth = 250;
self.speed = 0.8;
self.goldValue = 25;
break;
case 'bossEnemy':
self.maxHealth = 500;
self.speed = 1.0;
self.goldValue = 100;
break;
default:
self.maxHealth = 100;
self.speed = 1.5;
self.goldValue = 10;
}
self.health = self.maxHealth;
self.currentPathIndex = 0;
self.reachedEnd = false;
// Health bar
self.healthBarBackground = new Container();
self.healthBarBackground.width = self.enemyGraphic.width * 1.2;
self.healthBarBackground.height = 10;
self.healthBarBackground.x = -self.healthBarBackground.width / 2;
self.healthBarBackground.y = -self.enemyGraphic.height / 2 - 15;
self.addChild(self.healthBarBackground);
self.healthBar = new Container();
self.healthBar.width = self.healthBarBackground.width;
self.healthBar.height = self.healthBarBackground.height;
self.healthBar.x = self.healthBarBackground.x;
self.healthBar.y = self.healthBarBackground.y;
self.addChild(self.healthBar);
self.updateHealthBar = function () {
var healthPercentage = self.health / self.maxHealth;
self.healthBar.width = self.healthBarBackground.width * healthPercentage;
// Change color based on health percentage
if (healthPercentage > 0.6) {
self.healthBar.tint = 0x00FF00; // Green
} else if (healthPercentage > 0.3) {
self.healthBar.tint = 0xFFFF00; // Yellow
} else {
self.healthBar.tint = 0xFF0000; // Red
}
};
self.takeDamage = function (amount) {
self.health -= amount;
self.updateHealthBar();
// Briefly flash the enemy when taking damage
tween(self.enemyGraphic, {
alpha: 0.5
}, {
duration: 100,
onFinish: function onFinish() {
tween(self.enemyGraphic, {
alpha: 1
}, {
duration: 100
});
}
});
if (self.health <= 0) {
return true; // Enemy is dead
}
return false; // Enemy is still alive
};
self.moveAlongPath = function (path) {
if (self.currentPathIndex >= path.length) {
self.reachedEnd = true;
return true;
}
var targetX = path[self.currentPathIndex].x;
var targetY = path[self.currentPathIndex].y;
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 5) {
self.currentPathIndex++;
return false;
}
var normalizedX = dx / distance;
var normalizedY = dy / distance;
self.x += normalizedX * self.speed;
self.y += normalizedY * self.speed;
return false;
};
self.updateHealthBar();
return self;
});
var MapTile = Container.expand(function (tileType) {
var self = Container.call(this);
self.tileType = tileType || 'grass';
self.canPlaceTower = self.tileType === 'grass';
var tileGraphic = self.attachAsset(self.tileType, {
anchorX: 0.5,
anchorY: 0.5
});
self.setHighlight = function (highlight) {
if (highlight && self.canPlaceTower) {
tileGraphic.alpha = 0.8;
} else {
tileGraphic.alpha = 1.0;
}
};
return self;
});
var Projectile = Container.expand(function (targetEnemy, damage, speed, type) {
var self = Container.call(this);
self.targetEnemy = targetEnemy;
self.damage = damage || 25;
self.speed = speed || 10;
self.projectileType = type || 'projectile';
var projectileGraphic = self.attachAsset(self.projectileType, {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
if (!self.targetEnemy || !self.targetEnemy.parent) {
self.destroy();
return;
}
var dx = self.targetEnemy.x - self.x;
var dy = self.targetEnemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 15) {
// Hit enemy
if (self.targetEnemy.takeDamage(self.damage)) {
// Enemy is dead
gameState.gold += self.targetEnemy.goldValue;
gameState.score += self.targetEnemy.goldValue;
updateGoldText();
updateScoreText();
LK.getSound('enemyDeath').play();
var enemyIndex = gameState.enemies.indexOf(self.targetEnemy);
if (enemyIndex !== -1) {
self.targetEnemy.destroy();
gameState.enemies.splice(enemyIndex, 1);
}
}
self.destroy();
return;
}
// Move toward enemy
var normalizedX = dx / distance;
var normalizedY = dy / distance;
self.x += normalizedX * self.speed;
self.y += normalizedY * self.speed;
// Rotate projectile to face direction of travel
self.rotation = Math.atan2(dy, dx) + Math.PI / 2;
};
return self;
});
var Tower = Container.expand(function (type) {
var self = Container.call(this);
self.towerType = type || 'baseTower';
self.baseTower = self.attachAsset('baseTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.towerGraphic = self.attachAsset(self.towerType, {
anchorX: 0.5,
anchorY: 0.5,
y: -10
});
// Tower properties based on type
switch (self.towerType) {
case 'cannonTower':
self.damage = 40;
self.range = 200;
self.attackSpeed = 1.5; // Attacks per second
self.cost = 100;
self.projectileSpeed = 8;
self.targetingStrategy = 'first'; // Target first enemy in path
break;
case 'archerTower':
self.damage = 20;
self.range = 300;
self.attackSpeed = 3; // Attacks per second
self.cost = 150;
self.projectileSpeed = 15;
self.targetingStrategy = 'first';
break;
case 'magicTower':
self.damage = 30;
self.range = 250;
self.attackSpeed = 2; // Attacks per second
self.cost = 200;
self.projectileSpeed = 10;
self.targetingStrategy = 'strongest'; // Target enemy with most health
break;
default:
self.damage = 25;
self.range = 200;
self.attackSpeed = 1; // Attacks per second
self.cost = 50;
self.projectileSpeed = 8;
self.targetingStrategy = 'first';
}
self.level = 1;
self.attackCooldown = 0;
self.rangeIndicator = null;
self.selected = false;
// Show range indicator when tower is selected
self.showRange = function (show) {
if (show && !self.rangeIndicator) {
self.rangeIndicator = self.attachAsset('rangeIndicator', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3,
scaleX: self.range / 300,
// Scale to match tower's range
scaleY: self.range / 300
});
self.rangeIndicator.zIndex = -1;
} else if (!show && self.rangeIndicator) {
self.removeChild(self.rangeIndicator);
self.rangeIndicator = null;
}
};
self.upgrade = function () {
if (self.level >= 3) return false; // Max level reached
var upgradeCost = self.cost * self.level;
if (gameState.gold < upgradeCost) return false; // Not enough gold
gameState.gold -= upgradeCost;
self.level++;
// Upgrade stats
self.damage *= 1.5;
self.range *= 1.2;
self.attackSpeed *= 1.2;
// Update visuals for upgraded tower
self.towerGraphic.scale.set(1 + (self.level - 1) * 0.2);
// Update range indicator if visible
if (self.rangeIndicator) {
self.removeChild(self.rangeIndicator);
self.showRange(true);
}
updateGoldText();
return true;
};
self.findTarget = function (enemies) {
var target = null;
var maxPriority = -1;
var minDistance = Infinity;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.range) {
var priority = 0;
switch (self.targetingStrategy) {
case 'first':
priority = enemy.currentPathIndex + (1 - distance / self.range);
break;
case 'strongest':
priority = enemy.health;
break;
case 'closest':
priority = self.range - distance;
break;
}
if (priority > maxPriority || priority === maxPriority && distance < minDistance) {
maxPriority = priority;
minDistance = distance;
target = enemy;
}
}
}
return target;
};
self.attackEnemy = function (enemy) {
if (!enemy) return;
// Create projectile
var projectile = new Projectile(enemy, self.damage, self.projectileSpeed);
projectile.x = self.x;
projectile.y = self.y;
game.addChild(projectile);
gameState.projectiles.push(projectile);
LK.getSound('projectileShot').play();
// Reset cooldown
self.attackCooldown = 60 / self.attackSpeed;
// Tower attack animation
tween(self.towerGraphic, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
onFinish: function onFinish() {
tween(self.towerGraphic, {
scaleX: 1 + (self.level - 1) * 0.2,
scaleY: 1 + (self.level - 1) * 0.2
}, {
duration: 100
});
}
});
};
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
} else {
var target = self.findTarget(gameState.enemies);
if (target) {
self.attackEnemy(target);
}
}
};
self.down = function (x, y, obj) {
// Select this tower, deselect others
selectTower(self);
};
return self;
});
var TowerButton = Container.expand(function (towerType, cost) {
var self = Container.call(this);
self.towerType = towerType;
self.cost = cost;
var button = self.attachAsset('uiButton', {
anchorX: 0.5,
anchorY: 0.5
});
var towerPreview = self.attachAsset(towerType, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
var costText = new Text2(cost.toString(), {
size: 24,
fill: 0xFFFFFF
});
costText.anchor.set(0.5, 0.5);
costText.y = 30;
self.addChild(costText);
self.setEnabled = function (enabled) {
button.alpha = enabled ? 1.0 : 0.5;
towerPreview.alpha = enabled ? 1.0 : 0.5;
self.enabled = enabled;
};
self.down = function (x, y, obj) {
if (!self.enabled) return;
// Set the selected tower type for placement
gameState.selectedTowerType = self.towerType;
gameState.placingTower = true;
// Deselect any selected tower
if (gameState.selectedTower) {
gameState.selectedTower.showRange(false);
gameState.selectedTower = null;
}
// Show tower placeholders
for (var i = 0; i < gameState.map.length; i++) {
for (var j = 0; j < gameState.map[i].length; j++) {
var tile = gameState.map[i][j];
if (tile.canPlaceTower && !tile.tower) {
tile.setHighlight(true);
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2B5329 // Green background for the game
});
/****
* Game Code
****/
// Game state variables
// Tower assets
// Enemy assets
// Map and UI assets
// Sounds
// Music
var gameState = {
gold: 200,
lives: 10,
score: 0,
wave: 0,
enemyCount: 0,
waveInProgress: false,
selectedTower: null,
selectedTowerType: null,
placingTower: false,
map: [],
towers: [],
enemies: [],
projectiles: [],
path: [],
tileSize: 100,
mapWidth: 10,
mapHeight: 10
};
// Create UI elements
var scoreText = new Text2('Score: 0', {
size: 36,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
var goldText = new Text2('Gold: ' + gameState.gold, {
size: 36,
fill: 0xFFD700
});
goldText.anchor.set(0, 0);
goldText.x = 150;
LK.gui.topLeft.addChild(goldText);
var livesText = new Text2('Lives: ' + gameState.lives, {
size: 36,
fill: 0xFF0000
});
livesText.anchor.set(1, 0);
livesText.x = -150;
LK.gui.topRight.addChild(livesText);
var waveText = new Text2('Wave: ' + gameState.wave, {
size: 36,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
waveText.y = 50;
LK.gui.top.addChild(waveText);
var nextWaveButton = new Container();
var nextWaveButtonGraphic = nextWaveButton.attachAsset('uiButton', {
anchorX: 0.5,
anchorY: 0.5
});
var nextWaveButtonText = new Text2('Next Wave', {
size: 30,
fill: 0xFFFFFF
});
nextWaveButtonText.anchor.set(0.5, 0.5);
nextWaveButton.addChild(nextWaveButtonText);
nextWaveButton.x = 0;
nextWaveButton.y = -100;
LK.gui.bottom.addChild(nextWaveButton);
// Create tower buttons UI
var towerButtonsContainer = new Container();
towerButtonsContainer.y = -200;
LK.gui.bottom.addChild(towerButtonsContainer);
var towerButtons = [{
type: 'cannonTower',
cost: 100
}, {
type: 'archerTower',
cost: 150
}, {
type: 'magicTower',
cost: 200
}];
var buttonSpacing = 220;
for (var i = 0; i < towerButtons.length; i++) {
var button = new TowerButton(towerButtons[i].type, towerButtons[i].cost);
button.x = (i - (towerButtons.length - 1) / 2) * buttonSpacing;
towerButtonsContainer.addChild(button);
}
// Tower upgrade button and sell button
var upgradeButton = new Container();
var upgradeButtonGraphic = upgradeButton.attachAsset('uiButton', {
anchorX: 0.5,
anchorY: 0.5
});
var upgradeButtonText = new Text2('Upgrade', {
size: 30,
fill: 0xFFFFFF
});
upgradeButtonText.anchor.set(0.5, 0.5);
upgradeButton.addChild(upgradeButtonText);
upgradeButton.visible = false;
upgradeButton.x = -120;
upgradeButton.y = -300;
LK.gui.bottom.addChild(upgradeButton);
var sellButton = new Container();
var sellButtonGraphic = sellButton.attachAsset('uiButton', {
anchorX: 0.5,
anchorY: 0.5
});
var sellButtonText = new Text2('Sell', {
size: 30,
fill: 0xFFFFFF
});
sellButtonText.anchor.set(0.5, 0.5);
sellButton.addChild(sellButtonText);
sellButton.visible = false;
sellButton.x = 120;
sellButton.y = -300;
LK.gui.bottom.addChild(sellButton);
// Update functions for UI elements
function updateScoreText() {
scoreText.setText('Score: ' + gameState.score);
}
function updateGoldText() {
goldText.setText('Gold: ' + gameState.gold);
// Update tower buttons enabled/disabled state based on gold
for (var i = 0; i < towerButtonsContainer.children.length; i++) {
var button = towerButtonsContainer.children[i];
button.setEnabled(gameState.gold >= button.cost);
}
}
function updateLivesText() {
livesText.setText('Lives: ' + gameState.lives);
}
function updateWaveText() {
waveText.setText('Wave: ' + gameState.wave);
}
// Generate the game map with path
function generateMap() {
// Define map layout
var layout = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 1, 0, 0, 0, 0, 0, 1, 0, 0], [0, 1, 0, 0, 0, 0, 0, 1, 0, 0], [0, 1, 0, 1, 1, 1, 0, 1, 0, 0], [0, 1, 0, 1, 0, 1, 0, 1, 0, 0], [0, 1, 0, 1, 0, 1, 0, 1, 0, 0], [0, 1, 0, 1, 0, 1, 1, 1, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 2]];
var startX = 0;
var startY = 0;
var endX = 0;
var endY = 0;
// X offset to center map on screen
var offsetX = (2048 - gameState.mapWidth * gameState.tileSize) / 2;
var offsetY = (2732 - gameState.mapHeight * gameState.tileSize) / 2 - 150; // Offset to account for UI
// Create map tiles based on layout
for (var i = 0; i < gameState.mapHeight; i++) {
gameState.map[i] = [];
for (var j = 0; j < gameState.mapWidth; j++) {
var tileType = layout[i][j] === 1 ? 'path' : 'grass';
// Handle start point (entry point)
if (layout[i][j] === 1 && (i === 0 || j === 0 || i === gameState.mapHeight - 1 || j === gameState.mapWidth - 1)) {
if (gameState.path.length === 0) {
startX = j * gameState.tileSize + offsetX + gameState.tileSize / 2;
startY = i * gameState.tileSize + offsetY + gameState.tileSize / 2;
}
}
// Handle end point (base)
if (layout[i][j] === 2) {
tileType = 'baseStructure';
endX = j * gameState.tileSize + offsetX + gameState.tileSize / 2;
endY = i * gameState.tileSize + offsetY + gameState.tileSize / 2;
}
var tile = new MapTile(tileType);
tile.x = j * gameState.tileSize + offsetX + gameState.tileSize / 2;
tile.y = i * gameState.tileSize + offsetY + gameState.tileSize / 2;
tile.gridX = j;
tile.gridY = i;
tile.tower = null;
gameState.map[i][j] = tile;
game.addChild(tile);
// Add to path if it's a path tile
if (tileType === 'path') {
gameState.path.push({
x: tile.x,
y: tile.y,
tile: tile
});
}
}
}
// Sort path from start to end (using a simplified approach for this straight path)
// This is a simplification - a real game would need a proper path finding algorithm
gameState.path.sort(function (a, b) {
var distA = Math.sqrt(Math.pow(a.x - startX, 2) + Math.pow(a.y - startY, 2));
var distB = Math.sqrt(Math.pow(b.x - startX, 2) + Math.pow(b.y - startY, 2));
return distA - distB;
});
// The first point should be off-screen for enemy spawn
gameState.path.unshift({
x: startX - gameState.tileSize,
y: startY,
offScreen: true
});
// The last point is the base
gameState.path.push({
x: endX,
y: endY,
isBase: true
});
}
// Start a new wave of enemies
function startWave() {
if (gameState.waveInProgress) return;
gameState.wave++;
updateWaveText();
gameState.waveInProgress = true;
gameState.enemyCount = 5 + gameState.wave * 2; // More enemies per wave
LK.getSound('waveStart').play();
var spawnInterval = LK.setInterval(function () {
spawnEnemy();
gameState.enemyCount--;
if (gameState.enemyCount <= 0) {
LK.clearInterval(spawnInterval);
// Wave is considered over when all enemies are spawned
// The actual check for wave completion is in the update function
}
}, 1500 - gameState.wave * 100); // Spawn faster in later waves
}
// Spawn an enemy
function spawnEnemy() {
var enemyTypes = ['basicEnemy'];
// Add more enemy types as waves progress
if (gameState.wave >= 3) enemyTypes.push('fastEnemy');
if (gameState.wave >= 5) enemyTypes.push('tankEnemy');
if (gameState.wave >= 10 && gameState.wave % 5 === 0) enemyTypes.push('bossEnemy');
var randomType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
// Always spawn a boss on wave 5, 10, 15, etc.
if (gameState.wave % 5 === 0 && gameState.enemyCount === 0) {
randomType = 'bossEnemy';
}
var enemy = new Enemy(randomType);
// Spawn at the start of the path
// Use the first path point (index 0), which is off-screen
var startPoint = gameState.path[0];
enemy.x = startPoint.x;
enemy.y = startPoint.y;
// Set the initial path index to 1 so enemies start moving toward the first visible point
enemy.currentPathIndex = 1;
gameState.enemies.push(enemy);
game.addChild(enemy);
}
// Check if wave is complete
function checkWaveComplete() {
if (gameState.waveInProgress && gameState.enemyCount <= 0 && gameState.enemies.length === 0) {
gameState.waveInProgress = false;
gameState.gold += 50 + gameState.wave * 10; // Bonus gold for completing wave
updateGoldText();
}
}
// Place a tower on the map
function placeTower(gridX, gridY) {
var tile = gameState.map[gridY][gridX];
if (!tile.canPlaceTower || tile.tower) return false;
var towerCost = 0;
switch (gameState.selectedTowerType) {
case 'cannonTower':
towerCost = 100;
break;
case 'archerTower':
towerCost = 150;
break;
case 'magicTower':
towerCost = 200;
break;
default:
towerCost = 100;
}
if (gameState.gold < towerCost) return false;
var tower = new Tower(gameState.selectedTowerType);
tower.x = tile.x;
tower.y = tile.y;
tower.gridX = gridX;
tower.gridY = gridY;
tile.tower = tower;
gameState.towers.push(tower);
game.addChild(tower);
gameState.gold -= towerCost;
updateGoldText();
LK.getSound('buildTower').play();
// Clear tower placement mode
clearTowerPlacement();
return true;
}
// Select a tower
function selectTower(tower) {
// Deselect previously selected tower
if (gameState.selectedTower) {
gameState.selectedTower.showRange(false);
}
// Clear tower placement mode
clearTowerPlacement();
// Select new tower
gameState.selectedTower = tower;
tower.showRange(true);
// Show upgrade and sell buttons
upgradeButton.visible = true;
sellButton.visible = true;
}
// Clear tower placement mode
function clearTowerPlacement() {
gameState.placingTower = false;
gameState.selectedTowerType = null;
// Remove highlights from all tiles
for (var i = 0; i < gameState.map.length; i++) {
for (var j = 0; j < gameState.map[i].length; j++) {
gameState.map[i][j].setHighlight(false);
}
}
}
// Handle game over
function checkGameOver() {
if (gameState.lives <= 0) {
// Update high score
if (gameState.score > storage.highScore) {
storage.highScore = gameState.score;
}
// Game over
LK.getSound('gameOver').play();
LK.showGameOver();
}
}
// Event handlers
nextWaveButton.down = function () {
if (!gameState.waveInProgress) {
startWave();
}
};
upgradeButton.down = function () {
if (gameState.selectedTower) {
if (gameState.selectedTower.upgrade()) {
// Success - tower was upgraded
} else {
// Failed to upgrade (either max level or not enough gold)
}
}
};
sellButton.down = function () {
if (gameState.selectedTower) {
// Refund 70% of tower cost
var refundAmount = Math.floor(gameState.selectedTower.cost * 0.7);
gameState.gold += refundAmount;
updateGoldText();
// Remove tower from map and arrays
var tile = gameState.map[gameState.selectedTower.gridY][gameState.selectedTower.gridX];
tile.tower = null;
var towerIndex = gameState.towers.indexOf(gameState.selectedTower);
if (towerIndex !== -1) {
gameState.towers.splice(towerIndex, 1);
}
gameState.selectedTower.destroy();
gameState.selectedTower = null;
// Hide buttons
upgradeButton.visible = false;
sellButton.visible = false;
}
};
game.down = function (x, y, obj) {
if (gameState.placingTower) {
// Find which tile was clicked
for (var i = 0; i < gameState.map.length; i++) {
for (var j = 0; j < gameState.map[i].length; j++) {
var tile = gameState.map[i][j];
var dx = tile.x - x;
var dy = tile.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < gameState.tileSize / 2) {
if (placeTower(j, i)) {
return; // Tower placed successfully
}
}
}
}
// Clicked outside valid tile, cancel placement
clearTowerPlacement();
} else {
// Check if clicked on a tower
var towerClicked = false;
for (var i = 0; i < gameState.towers.length; i++) {
var tower = gameState.towers[i];
var dx = tower.x - x;
var dy = tower.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < gameState.tileSize / 2) {
selectTower(tower);
towerClicked = true;
break;
}
}
// If clicked outside tower, deselect current tower
if (!towerClicked && gameState.selectedTower) {
gameState.selectedTower.showRange(false);
gameState.selectedTower = null;
upgradeButton.visible = false;
sellButton.visible = false;
}
}
};
// Main update function
game.update = function () {
// Update all towers
for (var i = 0; i < gameState.towers.length; i++) {
gameState.towers[i].update();
}
// Update all projectiles
for (var i = gameState.projectiles.length - 1; i >= 0; i--) {
gameState.projectiles[i].update();
// Check if projectile needs to be removed
if (!gameState.projectiles[i].parent) {
gameState.projectiles.splice(i, 1);
}
}
// Update all enemies
for (var i = gameState.enemies.length - 1; i >= 0; i--) {
var enemy = gameState.enemies[i];
var reachedEnd = enemy.moveAlongPath(gameState.path);
if (reachedEnd) {
// Enemy reached the base, player loses a life
gameState.lives--;
updateLivesText();
LK.getSound('playerDamage').play();
// Remove enemy
enemy.destroy();
gameState.enemies.splice(i, 1);
// Flash screen
LK.effects.flashScreen(0xFF0000, 500);
// Check for game over
checkGameOver();
}
}
// Check if wave is complete
checkWaveComplete();
// Update UI button states
nextWaveButton.alpha = gameState.waveInProgress ? 0.5 : 1.0;
};
// Initialize game
function initGame() {
// Reset game state
gameState.gold = 200;
gameState.lives = 10;
gameState.score = 0;
gameState.wave = 0;
gameState.waveInProgress = false;
gameState.selectedTower = null;
gameState.selectedTowerType = null;
gameState.placingTower = false;
gameState.map = [];
gameState.towers = [];
gameState.enemies = [];
gameState.projectiles = [];
gameState.path = [];
// Update UI
updateScoreText();
updateGoldText();
updateLivesText();
updateWaveText();
// Generate map
generateMap();
// Hide upgrade and sell buttons
upgradeButton.visible = false;
sellButton.visible = false;
// Play background music
LK.playMusic('bgMusic');
}
// Start the game
initGame();
a archer robot. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a robotic heart. In-Game asset. 2d. High contrast. No shadows
a alien with space ship. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a king alien with space ship. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a cannon robot. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a fast alien with space ship. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a magic wizard robot. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a tank alien with space ship. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat