/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Asteroid = Container.expand(function () { var self = Container.call(this); var size = 60 + Math.floor(Math.random() * 60); var asteroidGraphics = self.attachAsset('asteroid', { anchorX: 0.5, anchorY: 0.5, scaleX: size / 60, scaleY: size / 60 }); self.health = 20 + Math.floor(size / 10); self.speed = 1 + Math.random() * 3; self.rotationSpeed = (Math.random() - 0.5) * 0.05; self.points = 50; self.update = function () { self.y += self.speed; asteroidGraphics.rotation += self.rotationSpeed; // Check if asteroid is off-screen if (self.y > 2832) { return true; // Mark for removal } return false; }; self.damage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xffffff, 100); if (self.health <= 0) { LK.getSound('explosion').play(); return true; // Asteroid destroyed } return false; }; return self; }); var EnemyBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('enemyBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.damage = 10; self.update = function () { self.y += self.speed; // Check if bullet is off-screen if (self.y > 2832) { return true; // Mark for removal } return false; }; return self; }); var EnemyShip = Container.expand(function () { var self = Container.call(this); var shipGraphics = self.attachAsset('enemyShip', { anchorX: 0.5, anchorY: 0.5 }); self.health = 30; self.type = 'standard'; self.speed = 2 + Math.random() * 2; self.points = 100; self.fireRate = 120; // Frames between shots self.fireCooldown = Math.floor(Math.random() * 60); self.update = function () { self.y += self.speed; // Check if enemy is off-screen if (self.y > 2832) { return true; // Mark for removal } if (Math.random() < 0.02) { self.direction = Math.random() > 0.5 ? 1 : -1; } if (self.direction) { self.x += self.direction * (self.speed * 0.8); // Keep within bounds if (self.x < 100) { self.x = 100; self.direction = 1; } else if (self.x > 2048 - 100) { self.x = 2048 - 100; self.direction = -1; } } // Shooting if (self.fireCooldown <= 0) { self.fireCooldown = self.fireRate; self.shoot(); } else { self.fireCooldown--; } return false; }; self.shoot = function () { var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y + 40; enemyBullets.push(bullet); game.addChild(bullet); LK.getSound('enemyShoot').play(); }; self.damage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xffffff, 100); if (self.health <= 0) { LK.getSound('explosion').play(); return true; // Enemy destroyed } return false; }; return self; }); var HomeScreen = Container.expand(function () { var self = Container.call(this); // Title text var titleText = new Text2('SPACE DEFENDER', { size: 120, fill: 0x33ccff }); titleText.anchor.set(0.5, 0.5); titleText.x = 2048 / 2; titleText.y = 600; self.addChild(titleText); // Start button var startButton = LK.getAsset('playerShip', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2, tint: 0x33ff33 }); startButton.x = 2048 / 2; startButton.y = 1200; self.addChild(startButton); // Start text var startText = new Text2('START', { size: 80, fill: 0xFFFFFF }); startText.anchor.set(0.5, 0.5); startText.x = 2048 / 2; startText.y = 1200; self.addChild(startText); // Shop button var shopButton = LK.getAsset('powerUp', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 3, tint: 0xffcc33 }); shopButton.x = 2048 / 2; shopButton.y = 1600; self.addChild(shopButton); // Shop text var shopText = new Text2('SHOP', { size: 80, fill: 0xFFFFFF }); shopText.anchor.set(0.5, 0.5); shopText.x = 2048 / 2; shopText.y = 1600; self.addChild(shopText); // Coins display var coins = storage.coins || 0; var coinsText = new Text2('COINS: ' + coins, { size: 60, fill: 0xffcc33 }); coinsText.anchor.set(0.5, 0); coinsText.x = 2048 / 2; coinsText.y = 1800; self.addChild(coinsText); // Event handlers startButton.interactive = true; startButton.down = function () { game.gameState = 'playing'; self.destroy(); initGame(); }; shopButton.interactive = true; shopButton.down = function () { game.gameState = 'shop'; self.destroy(); showShop(); }; return self; }); var PlayerBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('playerBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 15; self.power = 10; self.update = function () { self.y -= self.speed; // Check if bullet is off-screen if (self.y < -50) { return true; // Mark for removal } return false; }; return self; }); var PlayerShip = Container.expand(function () { var self = Container.call(this); var shipGraphics = self.attachAsset('playerShip', { anchorX: 0.5, anchorY: 0.5 }); self.health = 200; self.maxHealth = 200; self.fireRate = 20; // Frames between shots self.fireCooldown = 0; self.bulletSpeed = 15; self.speed = 8; self.powerLevel = 1; self.targetX = 0; self.targetY = 0; self.isMoving = false; self.update = function () { if (self.fireCooldown > 0) { self.fireCooldown--; } // Move toward target position if moving if (self.isMoving) { // Calculate direction vector var dx = self.targetX - self.x; var dy = self.targetY - self.y; // Calculate distance var distance = Math.sqrt(dx * dx + dy * dy); // If we're close enough, just set position directly if (distance < self.speed) { self.x = self.targetX; self.y = self.targetY; } else { // Normalize direction vector and move dx = dx / distance * self.speed; dy = dy / distance * self.speed; // Update position self.x += dx; self.y += dy; } // Keep player in bounds self.x = Math.max(50, Math.min(self.x, 2048 - 50)); self.y = Math.max(50, Math.min(self.y, 2732 - 50)); } }; self.damage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xff0000, 300); // Add visual feedback var damageText = new Text2('-' + amount, { size: 40, fill: 0xff3333 }); damageText.anchor.set(0.5, 0.5); damageText.x = self.x; damageText.y = self.y - 60; game.addChild(damageText); // Animate damage text tween(damageText, { y: damageText.y - 80, alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { damageText.destroy(); } }); if (self.health <= 0) { self.health = 0; return true; // Ship destroyed } return false; }; self.shoot = function () { if (self.fireCooldown <= 0) { self.fireCooldown = self.fireRate; LK.getSound('playerShoot').play(); // Create bullets based on power level if (self.powerLevel === 1) { // Single bullet createBullet(self.x, self.y - 40); } else if (self.powerLevel === 2) { // Double bullets createBullet(self.x - 20, self.y - 30); createBullet(self.x + 20, self.y - 30); } else { // Triple bullets createBullet(self.x, self.y - 40); createBullet(self.x - 25, self.y - 30); createBullet(self.x + 25, self.y - 30); } } }; // Helper function to create a bullet function createBullet(x, y) { var bullet = new PlayerBullet(); bullet.x = x; bullet.y = y; playerBullets.push(bullet); game.addChild(bullet); } return self; }); var PowerUp = Container.expand(function () { var self = Container.call(this); var powerUpGraphics = self.attachAsset('powerUp', { anchorX: 0.5, anchorY: 0.5 }); self.type = Math.random() < 0.7 ? 'weapon' : 'health'; self.speed = 3; // Change color based on type if (self.type === 'health') { powerUpGraphics.tint = 0x33ff33; // Green for health } else { powerUpGraphics.tint = 0xffcc33; // Yellow for weapon } self.update = function () { self.y += self.speed; // Slight bobbing motion self.x += Math.sin(LK.ticks * 0.05) * 0.5; // Check if power-up is off-screen if (self.y > 2832) { return true; // Mark for removal } return false; }; return self; }); var ShootButton = Container.expand(function () { var self = Container.call(this); var buttonGraphics = self.attachAsset('shootButton', { anchorX: 0.5, anchorY: 0.5, alpha: 0.6 }); self.pressed = false; self.down = function () { self.pressed = true; buttonGraphics.alpha = 0.9; buttonGraphics.scale.set(0.9); }; self.up = function () { self.pressed = false; buttonGraphics.alpha = 0.6; buttonGraphics.scale.set(1); }; return self; }); var ShopScreen = Container.expand(function () { var self = Container.call(this); // Title var titleText = new Text2('SHOP', { size: 120, fill: 0xffcc33 }); titleText.anchor.set(0.5, 0); titleText.x = 2048 / 2; titleText.y = 300; self.addChild(titleText); // Coins display var coins = storage.coins || 0; var coinsText = new Text2('COINS: ' + coins, { size: 60, fill: 0xffcc33 }); coinsText.anchor.set(0.5, 0); coinsText.x = 2048 / 2; coinsText.y = 450; self.addChild(coinsText); // Shop items var items = [{ name: 'HEALTH+', cost: 100, effect: 'Increases max health by 20', property: 'maxHealthBonus' }, { name: 'DAMAGE+', cost: 150, effect: 'Increases weapon damage by 25%', property: 'damageBonus' }, { name: 'SPEED+', cost: 120, effect: 'Increases ship speed by 20%', property: 'speedBonus' }]; var itemY = 600; for (var i = 0; i < items.length; i++) { var item = items[i]; // Item container var itemBg = LK.getAsset('playerBullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 6, scaleY: 2, tint: 0x333366, alpha: 0.7 }); itemBg.x = 2048 / 2; itemBg.y = itemY; self.addChild(itemBg); // Item name var nameText = new Text2(item.name, { size: 60, fill: 0xFFFFFF }); nameText.anchor.set(0.5, 0.5); nameText.x = 2048 / 2 - 400; nameText.y = itemY - 40; self.addChild(nameText); // Item effect var effectText = new Text2(item.effect, { size: 40, fill: 0xcccccc }); effectText.anchor.set(0.5, 0.5); effectText.x = 2048 / 2; effectText.y = itemY + 40; self.addChild(effectText); // Cost var costText = new Text2(item.cost + ' COINS', { size: 50, fill: 0xffcc33 }); costText.anchor.set(0.5, 0.5); costText.x = 2048 / 2 + 350; costText.y = itemY; self.addChild(costText); // Buy button var buyButton = LK.getAsset('powerUp', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5, tint: coins >= item.cost ? 0x33ff33 : 0x666666 }); buyButton.x = 2048 / 2 + 500; buyButton.y = itemY; buyButton.interactive = true; buyButton.itemIndex = i; buyButton.down = function () { var selectedItem = items[this.itemIndex]; if ((storage.coins || 0) >= selectedItem.cost) { // Purchase item storage.coins = (storage.coins || 0) - selectedItem.cost; // Increase the upgrade level storage[selectedItem.property] = (storage[selectedItem.property] || 0) + 1; // Show feedback and refresh shop LK.effects.flashObject(this, 0x33ff33, 300); self.destroy(); showShop(); } else { // Not enough coins LK.effects.flashObject(this, 0xff3333, 300); } }; self.addChild(buyButton); // Level indicator var level = storage[item.property] || 0; var levelText = new Text2('LEVEL: ' + level, { size: 40, fill: 0xFFFFFF }); levelText.anchor.set(0.5, 0.5); levelText.x = 2048 / 2 - 400; levelText.y = itemY + 40; self.addChild(levelText); itemY += 250; } // Back button var backButton = LK.getAsset('playerShip', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5, rotation: Math.PI, tint: 0x3399ff }); backButton.x = 2048 / 2; backButton.y = 2300; backButton.interactive = true; backButton.down = function () { self.destroy(); showHomeScreen(); }; self.addChild(backButton); // Back text var backText = new Text2('BACK', { size: 60, fill: 0xFFFFFF }); backText.anchor.set(0.5, 0.5); backText.x = 2048 / 2; backText.y = 2300; self.addChild(backText); return self; }); var VirtualJoystick = Container.expand(function () { var self = Container.call(this); var baseGraphics = self.attachAsset('joystick', { anchorX: 0.5, anchorY: 0.5, alpha: 0.6 }); var knobGraphics = self.attachAsset('joystickKnob', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 }); self.maxDistance = 80; self.active = false; self.dx = 0; self.dy = 0; self.down = function (x, y) { var localPos = self.toLocal({ x: x, y: y }); if (Math.sqrt(Math.pow(localPos.x, 2) + Math.pow(localPos.y, 2)) <= baseGraphics.width / 2) { self.active = true; self.moveKnob(localPos.x, localPos.y); } }; self.moveKnob = function (x, y) { var dist = Math.sqrt(x * x + y * y); if (dist > self.maxDistance) { x = x / dist * self.maxDistance; y = y / dist * self.maxDistance; } knobGraphics.x = x; knobGraphics.y = y; // Normalize to -1 to 1 range for movement self.dx = x / self.maxDistance; self.dy = y / self.maxDistance; }; self.move = function (x, y) { if (self.active) { var localPos = self.toLocal({ x: x, y: y }); self.moveKnob(localPos.x, localPos.y); } }; self.up = function () { self.active = false; knobGraphics.x = 0; knobGraphics.y = 0; self.dx = 0; self.dy = 0; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Game state var gameState = 'home'; var wave = 1; var enemySpawnTimer = 0; var asteroidSpawnTimer = 0; var waveEnemyCount = 0; var waveMaxEnemies = 10; var powerUpChance = 0.2; var touchIdentifiers = {}; // Game elements var player; var shootButton; var playerBullets = []; var enemies = []; var enemyBullets = []; var asteroids = []; var powerUps = []; var stars = []; var homeScreen; var shopScreen; // UI elements var scoreText; var healthBar; var waveText; var coinsDisplay; // Show home screen function showHomeScreen() { // Clear game area game.removeChildren(); // Set background game.setBackgroundColor(0x000033); // Create starfield background createStarfield(); // Create and show home screen homeScreen = new HomeScreen(); game.addChild(homeScreen); // Set game state gameState = 'home'; } // Show shop screen function showShop() { // Clear game area game.removeChildren(); // Set background game.setBackgroundColor(0x000044); // Create starfield background createStarfield(); // Create and show shop screen shopScreen = new ShopScreen(); game.addChild(shopScreen); // Set game state gameState = 'shop'; } // Initialize game function initGame() { // Reset game state gameState = 'playing'; wave = 1; enemySpawnTimer = 0; asteroidSpawnTimer = 0; waveEnemyCount = 0; waveMaxEnemies = 5 + wave * 5; playerBullets = []; enemies = []; enemyBullets = []; asteroids = []; powerUps = []; touchIdentifiers = {}; // Set background game.setBackgroundColor(0x000022); // Create starfield background createStarfield(); // Create player player = new PlayerShip(); // Apply upgrades from shop player.health = 100 + (storage.maxHealthBonus || 0) * 20; player.maxHealth = player.health; player.powerLevel = 1 + Math.min((storage.damageBonus || 0) / 2, 2); player.speed = 8 * (1 + (storage.speedBonus || 0) * 0.2); player.x = 2048 / 2; player.y = 2732 - 300; game.addChild(player); // No shoot button needed as player shoots when moving // Create UI createUI(); // Start music LK.playMusic('gameMusic'); } // Create starfield background function createStarfield() { for (var i = 0; i < 100; i++) { var star = LK.getAsset('joystickKnob', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.1 + Math.random() * 0.2, scaleY: 0.1 + Math.random() * 0.2, alpha: 0.1 + Math.random() * 0.5, tint: 0xffffff }); star.x = Math.random() * 2048; star.y = Math.random() * 2732; star.speed = 0.5 + Math.random() * 2; stars.push(star); game.addChild(star); } } // Create UI elements function createUI() { // Score text scoreText = new Text2('SCORE: 0', { size: 60, fill: 0xFFFFFF }); scoreText.anchor.set(0, 0); scoreText.x = 30; scoreText.y = 30; LK.gui.addChild(scoreText); // Coins display coinsDisplay = new Text2('COINS: ' + (storage.coins || 0), { size: 60, fill: 0xffcc33 }); coinsDisplay.anchor.set(0.5, 0); coinsDisplay.x = 2048 / 2; coinsDisplay.y = 30; LK.gui.addChild(coinsDisplay); // Health text var healthText = new Text2('HEALTH:', { size: 40, fill: 0xFFFFFF }); healthText.anchor.set(0, 0.5); healthText.x = 30; healthText.y = 100; LK.gui.addChild(healthText); // Health bar background var healthBarBg = LK.getAsset('playerBullet', { anchorX: 0, anchorY: 0.5, scaleX: 6, scaleY: 0.8, tint: 0x444444 }); healthBarBg.x = 180; healthBarBg.y = 100; LK.gui.addChild(healthBarBg); // Health bar healthBar = LK.getAsset('playerBullet', { anchorX: 0, anchorY: 0.5, scaleX: 6, scaleY: 0.8, tint: 0x33ff33 }); healthBar.x = 180; healthBar.y = 100; LK.gui.addChild(healthBar); // Wave text waveText = new Text2('WAVE: 1 / 5', { size: 60, fill: 0xFFFFFF }); waveText.anchor.set(1, 0); waveText.x = 2048 - 30; waveText.y = 30; LK.gui.addChild(waveText); // Wave progress indicator var waveProgressBg = LK.getAsset('playerBullet', { anchorX: 1, anchorY: 0.5, scaleX: 3, scaleY: 0.4, tint: 0x444444 }); waveProgressBg.x = 2048 - 30; waveProgressBg.y = 100; LK.gui.addChild(waveProgressBg); // Wave progress bar var waveProgress = LK.getAsset('playerBullet', { anchorX: 1, anchorY: 0.5, scaleX: 3 * (1 / 5), // Start at 1/5 for wave 1 scaleY: 0.4, tint: 0x3399ff }); waveProgress.x = 2048 - 30; waveProgress.y = 100; LK.gui.addChild(waveProgress); } // Spawn enemy function spawnEnemy() { var enemy = new EnemyShip(); enemy.x = 100 + Math.random() * (2048 - 200); enemy.y = -100; enemy.direction = Math.random() > 0.5 ? 1 : -1; // Adjust enemy stats based on wave enemy.health = 30 + wave * 5; enemy.speed = 2 + Math.min(wave * 0.3, 3); enemy.fireRate = Math.max(120 - wave * 5, 60); enemy.points = 100 + wave * 10; enemies.push(enemy); game.addChild(enemy); waveEnemyCount++; } // Spawn asteroid function spawnAsteroid() { var asteroid = new Asteroid(); asteroid.x = 100 + Math.random() * (2048 - 200); asteroid.y = -100; asteroids.push(asteroid); game.addChild(asteroid); } // Spawn power-up function spawnPowerUp(x, y) { var powerUp = new PowerUp(); powerUp.x = x; powerUp.y = y; powerUps.push(powerUp); game.addChild(powerUp); } // Check collisions function checkCollisions() { // Player bullets vs enemies for (var i = playerBullets.length - 1; i >= 0; i--) { var bullet = playerBullets[i]; var hitEnemy = false; // Check against enemies for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (bullet.intersects(enemy)) { hitEnemy = true; if (enemy.damage(player.powerLevel * 10)) { // Enemy destroyed LK.setScore(LK.getScore() + enemy.points); scoreText.setText('SCORE: ' + LK.getScore()); // Award 10 coins when enemy is destroyed storage.coins = (storage.coins || 0) + 10; // Update coins display if it exists if (coinsDisplay) { coinsDisplay.setText('COINS: ' + storage.coins); } // Chance to spawn power-up if (Math.random() < powerUpChance) { spawnPowerUp(enemy.x, enemy.y); } enemy.destroy(); enemies.splice(j, 1); } break; } } // Check against asteroids if bullet didn't hit an enemy if (!hitEnemy) { for (var j = asteroids.length - 1; j >= 0; j--) { var asteroid = asteroids[j]; if (bullet.intersects(asteroid)) { hitEnemy = true; if (asteroid.damage(player.powerLevel * 10)) { // Asteroid destroyed LK.setScore(LK.getScore() + asteroid.points); scoreText.setText('SCORE: ' + LK.getScore()); // Small chance to spawn power-up if (Math.random() < powerUpChance * 0.5) { spawnPowerUp(asteroid.x, asteroid.y); } asteroid.destroy(); asteroids.splice(j, 1); } break; } } } // Remove bullet if it hit something or went off-screen if (hitEnemy || bullet.update()) { bullet.destroy(); playerBullets.splice(i, 1); } } // Enemy bullets vs player for (var i = enemyBullets.length - 1; i >= 0; i--) { var bullet = enemyBullets[i]; // Check if bullet should be removed if (bullet.update()) { bullet.destroy(); enemyBullets.splice(i, 1); continue; } // Check against player if (gameState === 'playing' && bullet.intersects(player)) { if (player.damage(bullet.damage)) { // Player destroyed gameOver(); } // Update health bar updateHealthBar(); // Remove bullet bullet.destroy(); enemyBullets.splice(i, 1); } } // Enemies vs player for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; // Check if enemy should be removed if (enemy.update()) { enemy.destroy(); enemies.splice(i, 1); continue; } // Check against player if (gameState === 'playing' && enemy.intersects(player)) { if (player.damage(30)) { // Player destroyed gameOver(); } // Update health bar updateHealthBar(); // Destroy enemy enemy.destroy(); enemies.splice(i, 1); } } // Asteroids vs player for (var i = asteroids.length - 1; i >= 0; i--) { var asteroid = asteroids[i]; // Check if asteroid should be removed if (asteroid.update()) { asteroid.destroy(); asteroids.splice(i, 1); continue; } // Check against player if (gameState === 'playing' && asteroid.intersects(player)) { if (player.damage(20)) { // Player destroyed gameOver(); } // Update health bar updateHealthBar(); // Destroy asteroid asteroid.destroy(); asteroids.splice(i, 1); } } // Power-ups vs player for (var i = powerUps.length - 1; i >= 0; i--) { var powerUp = powerUps[i]; // Check if power-up should be removed if (powerUp.update()) { powerUp.destroy(); powerUps.splice(i, 1); continue; } // Check against player if (gameState === 'playing' && powerUp.intersects(player)) { // Apply power-up effect if (powerUp.type === 'health') { player.health = Math.min(player.health + 30, 100); updateHealthBar(); } else if (powerUp.type === 'weapon') { player.powerLevel = Math.min(player.powerLevel + 1, 3); } // Play sound LK.getSound('powerUpCollect').play(); // Remove power-up powerUp.destroy(); powerUps.splice(i, 1); } } } // Update health bar function updateHealthBar() { healthBar.scale.x = 3 * (player.health / player.maxHealth); // Change color based on health percentage var healthPercent = player.health / player.maxHealth * 100; if (healthPercent > 60) { healthBar.tint = 0x33ff33; // Green } else if (healthPercent > 30) { healthBar.tint = 0xffcc33; // Yellow } else { healthBar.tint = 0xff3333; // Red } } // Game over function gameOver() { gameState = 'gameover'; LK.effects.flashScreen(0xff0000, 500); LK.setTimeout(function () { LK.showGameOver(); // Return to home screen after game over LK.setTimeout(function () { showHomeScreen(); }, 1500); }, 500); } // Handle input game.down = function (x, y, obj) { // Store touch identifier to track multiple touches if (obj.identifier !== undefined) { touchIdentifiers[obj.identifier] = { x: x, y: y }; } // Use entire screen for movement if (player) { // Set player target position player.targetX = x; player.targetY = y; player.isMoving = true; } // No shoot button check needed }; game.move = function (x, y, obj) { // Update stored touch position if (obj.identifier !== undefined && touchIdentifiers[obj.identifier]) { touchIdentifiers[obj.identifier] = { x: x, y: y }; } // Update player target position if moving - make sure player exists first if (player && player.isMoving) { player.targetX = x; player.targetY = y; // Make player shoot when moving if (LK.ticks % player.fireRate === 0) { player.shoot(); } } }; game.up = function (x, y, obj) { // Remove touch identifier if (obj.identifier !== undefined) { delete touchIdentifiers[obj.identifier]; } // Stop player movement if (player) { player.isMoving = false; } // No shoot button to release }; // Main game loop game.update = function () { // Show home screen at start if (gameState === 'home' && !homeScreen) { showHomeScreen(); return; } // Update player if it exists if (player && gameState === 'playing') { player.update(); } // Spawn enemies if (gameState === 'playing') { // Check if wave is complete if (waveEnemyCount >= waveMaxEnemies && enemies.length === 0) { // Start new wave wave++; waveEnemyCount = 0; waveMaxEnemies = 5 + wave * 5; waveText.setText('WAVE: ' + wave); // Flash wave text tween(waveText, { scale: 1.5 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { tween(waveText, { scale: 1 }, { duration: 300, easing: tween.easeIn }); } }); // Check for game win condition if (wave > 5) { gameState = 'gamewin'; LK.effects.flashScreen(0x33ff33, 500); LK.setTimeout(function () { LK.showYouWin(); // Return to home screen after win LK.setTimeout(function () { showHomeScreen(); }, 1500); }, 500); } } // Spawn enemies if (waveEnemyCount < waveMaxEnemies) { enemySpawnTimer++; if (enemySpawnTimer > Math.max(120 - wave * 5, 30)) { enemySpawnTimer = 0; spawnEnemy(); } } // Update wave progress bar var waveProgressBar = LK.gui.children.find(function (child) { return child.tint === 0x3399ff && child.scaleY === 0.4; }); if (waveProgressBar) { // Update wave text waveText.setText('WAVE: ' + wave + ' / 5'); // Show remaining enemies var remaining = waveMaxEnemies - waveEnemyCount + enemies.length; var progressScale = Math.min(wave / 5, 1); waveProgressBar.scale.x = 3 * progressScale; } // Spawn asteroids asteroidSpawnTimer++; if (asteroidSpawnTimer > Math.max(180 - wave * 3, 60)) { asteroidSpawnTimer = 0; spawnAsteroid(); } } // Check collisions checkCollisions(); // Update starfield for (var i = 0; i < stars.length; i++) { var star = stars[i]; star.y += star.speed; if (star.y > 2732) { star.y = 0; star.x = Math.random() * 2048; } } }; // Initialize home screen at startup showHomeScreen();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Asteroid = Container.expand(function () {
var self = Container.call(this);
var size = 60 + Math.floor(Math.random() * 60);
var asteroidGraphics = self.attachAsset('asteroid', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: size / 60,
scaleY: size / 60
});
self.health = 20 + Math.floor(size / 10);
self.speed = 1 + Math.random() * 3;
self.rotationSpeed = (Math.random() - 0.5) * 0.05;
self.points = 50;
self.update = function () {
self.y += self.speed;
asteroidGraphics.rotation += self.rotationSpeed;
// Check if asteroid is off-screen
if (self.y > 2832) {
return true; // Mark for removal
}
return false;
};
self.damage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xffffff, 100);
if (self.health <= 0) {
LK.getSound('explosion').play();
return true; // Asteroid destroyed
}
return false;
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = 10;
self.update = function () {
self.y += self.speed;
// Check if bullet is off-screen
if (self.y > 2832) {
return true; // Mark for removal
}
return false;
};
return self;
});
var EnemyShip = Container.expand(function () {
var self = Container.call(this);
var shipGraphics = self.attachAsset('enemyShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 30;
self.type = 'standard';
self.speed = 2 + Math.random() * 2;
self.points = 100;
self.fireRate = 120; // Frames between shots
self.fireCooldown = Math.floor(Math.random() * 60);
self.update = function () {
self.y += self.speed;
// Check if enemy is off-screen
if (self.y > 2832) {
return true; // Mark for removal
}
if (Math.random() < 0.02) {
self.direction = Math.random() > 0.5 ? 1 : -1;
}
if (self.direction) {
self.x += self.direction * (self.speed * 0.8);
// Keep within bounds
if (self.x < 100) {
self.x = 100;
self.direction = 1;
} else if (self.x > 2048 - 100) {
self.x = 2048 - 100;
self.direction = -1;
}
}
// Shooting
if (self.fireCooldown <= 0) {
self.fireCooldown = self.fireRate;
self.shoot();
} else {
self.fireCooldown--;
}
return false;
};
self.shoot = function () {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + 40;
enemyBullets.push(bullet);
game.addChild(bullet);
LK.getSound('enemyShoot').play();
};
self.damage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xffffff, 100);
if (self.health <= 0) {
LK.getSound('explosion').play();
return true; // Enemy destroyed
}
return false;
};
return self;
});
var HomeScreen = Container.expand(function () {
var self = Container.call(this);
// Title text
var titleText = new Text2('SPACE DEFENDER', {
size: 120,
fill: 0x33ccff
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 600;
self.addChild(titleText);
// Start button
var startButton = LK.getAsset('playerShip', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2,
tint: 0x33ff33
});
startButton.x = 2048 / 2;
startButton.y = 1200;
self.addChild(startButton);
// Start text
var startText = new Text2('START', {
size: 80,
fill: 0xFFFFFF
});
startText.anchor.set(0.5, 0.5);
startText.x = 2048 / 2;
startText.y = 1200;
self.addChild(startText);
// Shop button
var shopButton = LK.getAsset('powerUp', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3,
tint: 0xffcc33
});
shopButton.x = 2048 / 2;
shopButton.y = 1600;
self.addChild(shopButton);
// Shop text
var shopText = new Text2('SHOP', {
size: 80,
fill: 0xFFFFFF
});
shopText.anchor.set(0.5, 0.5);
shopText.x = 2048 / 2;
shopText.y = 1600;
self.addChild(shopText);
// Coins display
var coins = storage.coins || 0;
var coinsText = new Text2('COINS: ' + coins, {
size: 60,
fill: 0xffcc33
});
coinsText.anchor.set(0.5, 0);
coinsText.x = 2048 / 2;
coinsText.y = 1800;
self.addChild(coinsText);
// Event handlers
startButton.interactive = true;
startButton.down = function () {
game.gameState = 'playing';
self.destroy();
initGame();
};
shopButton.interactive = true;
shopButton.down = function () {
game.gameState = 'shop';
self.destroy();
showShop();
};
return self;
});
var PlayerBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 15;
self.power = 10;
self.update = function () {
self.y -= self.speed;
// Check if bullet is off-screen
if (self.y < -50) {
return true; // Mark for removal
}
return false;
};
return self;
});
var PlayerShip = Container.expand(function () {
var self = Container.call(this);
var shipGraphics = self.attachAsset('playerShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 200;
self.maxHealth = 200;
self.fireRate = 20; // Frames between shots
self.fireCooldown = 0;
self.bulletSpeed = 15;
self.speed = 8;
self.powerLevel = 1;
self.targetX = 0;
self.targetY = 0;
self.isMoving = false;
self.update = function () {
if (self.fireCooldown > 0) {
self.fireCooldown--;
}
// Move toward target position if moving
if (self.isMoving) {
// Calculate direction vector
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
// Calculate distance
var distance = Math.sqrt(dx * dx + dy * dy);
// If we're close enough, just set position directly
if (distance < self.speed) {
self.x = self.targetX;
self.y = self.targetY;
} else {
// Normalize direction vector and move
dx = dx / distance * self.speed;
dy = dy / distance * self.speed;
// Update position
self.x += dx;
self.y += dy;
}
// Keep player in bounds
self.x = Math.max(50, Math.min(self.x, 2048 - 50));
self.y = Math.max(50, Math.min(self.y, 2732 - 50));
}
};
self.damage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xff0000, 300);
// Add visual feedback
var damageText = new Text2('-' + amount, {
size: 40,
fill: 0xff3333
});
damageText.anchor.set(0.5, 0.5);
damageText.x = self.x;
damageText.y = self.y - 60;
game.addChild(damageText);
// Animate damage text
tween(damageText, {
y: damageText.y - 80,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
damageText.destroy();
}
});
if (self.health <= 0) {
self.health = 0;
return true; // Ship destroyed
}
return false;
};
self.shoot = function () {
if (self.fireCooldown <= 0) {
self.fireCooldown = self.fireRate;
LK.getSound('playerShoot').play();
// Create bullets based on power level
if (self.powerLevel === 1) {
// Single bullet
createBullet(self.x, self.y - 40);
} else if (self.powerLevel === 2) {
// Double bullets
createBullet(self.x - 20, self.y - 30);
createBullet(self.x + 20, self.y - 30);
} else {
// Triple bullets
createBullet(self.x, self.y - 40);
createBullet(self.x - 25, self.y - 30);
createBullet(self.x + 25, self.y - 30);
}
}
};
// Helper function to create a bullet
function createBullet(x, y) {
var bullet = new PlayerBullet();
bullet.x = x;
bullet.y = y;
playerBullets.push(bullet);
game.addChild(bullet);
}
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerUpGraphics = self.attachAsset('powerUp', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = Math.random() < 0.7 ? 'weapon' : 'health';
self.speed = 3;
// Change color based on type
if (self.type === 'health') {
powerUpGraphics.tint = 0x33ff33; // Green for health
} else {
powerUpGraphics.tint = 0xffcc33; // Yellow for weapon
}
self.update = function () {
self.y += self.speed;
// Slight bobbing motion
self.x += Math.sin(LK.ticks * 0.05) * 0.5;
// Check if power-up is off-screen
if (self.y > 2832) {
return true; // Mark for removal
}
return false;
};
return self;
});
var ShootButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('shootButton', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6
});
self.pressed = false;
self.down = function () {
self.pressed = true;
buttonGraphics.alpha = 0.9;
buttonGraphics.scale.set(0.9);
};
self.up = function () {
self.pressed = false;
buttonGraphics.alpha = 0.6;
buttonGraphics.scale.set(1);
};
return self;
});
var ShopScreen = Container.expand(function () {
var self = Container.call(this);
// Title
var titleText = new Text2('SHOP', {
size: 120,
fill: 0xffcc33
});
titleText.anchor.set(0.5, 0);
titleText.x = 2048 / 2;
titleText.y = 300;
self.addChild(titleText);
// Coins display
var coins = storage.coins || 0;
var coinsText = new Text2('COINS: ' + coins, {
size: 60,
fill: 0xffcc33
});
coinsText.anchor.set(0.5, 0);
coinsText.x = 2048 / 2;
coinsText.y = 450;
self.addChild(coinsText);
// Shop items
var items = [{
name: 'HEALTH+',
cost: 100,
effect: 'Increases max health by 20',
property: 'maxHealthBonus'
}, {
name: 'DAMAGE+',
cost: 150,
effect: 'Increases weapon damage by 25%',
property: 'damageBonus'
}, {
name: 'SPEED+',
cost: 120,
effect: 'Increases ship speed by 20%',
property: 'speedBonus'
}];
var itemY = 600;
for (var i = 0; i < items.length; i++) {
var item = items[i];
// Item container
var itemBg = LK.getAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 6,
scaleY: 2,
tint: 0x333366,
alpha: 0.7
});
itemBg.x = 2048 / 2;
itemBg.y = itemY;
self.addChild(itemBg);
// Item name
var nameText = new Text2(item.name, {
size: 60,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0.5);
nameText.x = 2048 / 2 - 400;
nameText.y = itemY - 40;
self.addChild(nameText);
// Item effect
var effectText = new Text2(item.effect, {
size: 40,
fill: 0xcccccc
});
effectText.anchor.set(0.5, 0.5);
effectText.x = 2048 / 2;
effectText.y = itemY + 40;
self.addChild(effectText);
// Cost
var costText = new Text2(item.cost + ' COINS', {
size: 50,
fill: 0xffcc33
});
costText.anchor.set(0.5, 0.5);
costText.x = 2048 / 2 + 350;
costText.y = itemY;
self.addChild(costText);
// Buy button
var buyButton = LK.getAsset('powerUp', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
tint: coins >= item.cost ? 0x33ff33 : 0x666666
});
buyButton.x = 2048 / 2 + 500;
buyButton.y = itemY;
buyButton.interactive = true;
buyButton.itemIndex = i;
buyButton.down = function () {
var selectedItem = items[this.itemIndex];
if ((storage.coins || 0) >= selectedItem.cost) {
// Purchase item
storage.coins = (storage.coins || 0) - selectedItem.cost;
// Increase the upgrade level
storage[selectedItem.property] = (storage[selectedItem.property] || 0) + 1;
// Show feedback and refresh shop
LK.effects.flashObject(this, 0x33ff33, 300);
self.destroy();
showShop();
} else {
// Not enough coins
LK.effects.flashObject(this, 0xff3333, 300);
}
};
self.addChild(buyButton);
// Level indicator
var level = storage[item.property] || 0;
var levelText = new Text2('LEVEL: ' + level, {
size: 40,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
levelText.x = 2048 / 2 - 400;
levelText.y = itemY + 40;
self.addChild(levelText);
itemY += 250;
}
// Back button
var backButton = LK.getAsset('playerShip', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
rotation: Math.PI,
tint: 0x3399ff
});
backButton.x = 2048 / 2;
backButton.y = 2300;
backButton.interactive = true;
backButton.down = function () {
self.destroy();
showHomeScreen();
};
self.addChild(backButton);
// Back text
var backText = new Text2('BACK', {
size: 60,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = 2048 / 2;
backText.y = 2300;
self.addChild(backText);
return self;
});
var VirtualJoystick = Container.expand(function () {
var self = Container.call(this);
var baseGraphics = self.attachAsset('joystick', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6
});
var knobGraphics = self.attachAsset('joystickKnob', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
self.maxDistance = 80;
self.active = false;
self.dx = 0;
self.dy = 0;
self.down = function (x, y) {
var localPos = self.toLocal({
x: x,
y: y
});
if (Math.sqrt(Math.pow(localPos.x, 2) + Math.pow(localPos.y, 2)) <= baseGraphics.width / 2) {
self.active = true;
self.moveKnob(localPos.x, localPos.y);
}
};
self.moveKnob = function (x, y) {
var dist = Math.sqrt(x * x + y * y);
if (dist > self.maxDistance) {
x = x / dist * self.maxDistance;
y = y / dist * self.maxDistance;
}
knobGraphics.x = x;
knobGraphics.y = y;
// Normalize to -1 to 1 range for movement
self.dx = x / self.maxDistance;
self.dy = y / self.maxDistance;
};
self.move = function (x, y) {
if (self.active) {
var localPos = self.toLocal({
x: x,
y: y
});
self.moveKnob(localPos.x, localPos.y);
}
};
self.up = function () {
self.active = false;
knobGraphics.x = 0;
knobGraphics.y = 0;
self.dx = 0;
self.dy = 0;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game state
var gameState = 'home';
var wave = 1;
var enemySpawnTimer = 0;
var asteroidSpawnTimer = 0;
var waveEnemyCount = 0;
var waveMaxEnemies = 10;
var powerUpChance = 0.2;
var touchIdentifiers = {};
// Game elements
var player;
var shootButton;
var playerBullets = [];
var enemies = [];
var enemyBullets = [];
var asteroids = [];
var powerUps = [];
var stars = [];
var homeScreen;
var shopScreen;
// UI elements
var scoreText;
var healthBar;
var waveText;
var coinsDisplay;
// Show home screen
function showHomeScreen() {
// Clear game area
game.removeChildren();
// Set background
game.setBackgroundColor(0x000033);
// Create starfield background
createStarfield();
// Create and show home screen
homeScreen = new HomeScreen();
game.addChild(homeScreen);
// Set game state
gameState = 'home';
}
// Show shop screen
function showShop() {
// Clear game area
game.removeChildren();
// Set background
game.setBackgroundColor(0x000044);
// Create starfield background
createStarfield();
// Create and show shop screen
shopScreen = new ShopScreen();
game.addChild(shopScreen);
// Set game state
gameState = 'shop';
}
// Initialize game
function initGame() {
// Reset game state
gameState = 'playing';
wave = 1;
enemySpawnTimer = 0;
asteroidSpawnTimer = 0;
waveEnemyCount = 0;
waveMaxEnemies = 5 + wave * 5;
playerBullets = [];
enemies = [];
enemyBullets = [];
asteroids = [];
powerUps = [];
touchIdentifiers = {};
// Set background
game.setBackgroundColor(0x000022);
// Create starfield background
createStarfield();
// Create player
player = new PlayerShip();
// Apply upgrades from shop
player.health = 100 + (storage.maxHealthBonus || 0) * 20;
player.maxHealth = player.health;
player.powerLevel = 1 + Math.min((storage.damageBonus || 0) / 2, 2);
player.speed = 8 * (1 + (storage.speedBonus || 0) * 0.2);
player.x = 2048 / 2;
player.y = 2732 - 300;
game.addChild(player);
// No shoot button needed as player shoots when moving
// Create UI
createUI();
// Start music
LK.playMusic('gameMusic');
}
// Create starfield background
function createStarfield() {
for (var i = 0; i < 100; i++) {
var star = LK.getAsset('joystickKnob', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1 + Math.random() * 0.2,
scaleY: 0.1 + Math.random() * 0.2,
alpha: 0.1 + Math.random() * 0.5,
tint: 0xffffff
});
star.x = Math.random() * 2048;
star.y = Math.random() * 2732;
star.speed = 0.5 + Math.random() * 2;
stars.push(star);
game.addChild(star);
}
}
// Create UI elements
function createUI() {
// Score text
scoreText = new Text2('SCORE: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
scoreText.x = 30;
scoreText.y = 30;
LK.gui.addChild(scoreText);
// Coins display
coinsDisplay = new Text2('COINS: ' + (storage.coins || 0), {
size: 60,
fill: 0xffcc33
});
coinsDisplay.anchor.set(0.5, 0);
coinsDisplay.x = 2048 / 2;
coinsDisplay.y = 30;
LK.gui.addChild(coinsDisplay);
// Health text
var healthText = new Text2('HEALTH:', {
size: 40,
fill: 0xFFFFFF
});
healthText.anchor.set(0, 0.5);
healthText.x = 30;
healthText.y = 100;
LK.gui.addChild(healthText);
// Health bar background
var healthBarBg = LK.getAsset('playerBullet', {
anchorX: 0,
anchorY: 0.5,
scaleX: 6,
scaleY: 0.8,
tint: 0x444444
});
healthBarBg.x = 180;
healthBarBg.y = 100;
LK.gui.addChild(healthBarBg);
// Health bar
healthBar = LK.getAsset('playerBullet', {
anchorX: 0,
anchorY: 0.5,
scaleX: 6,
scaleY: 0.8,
tint: 0x33ff33
});
healthBar.x = 180;
healthBar.y = 100;
LK.gui.addChild(healthBar);
// Wave text
waveText = new Text2('WAVE: 1 / 5', {
size: 60,
fill: 0xFFFFFF
});
waveText.anchor.set(1, 0);
waveText.x = 2048 - 30;
waveText.y = 30;
LK.gui.addChild(waveText);
// Wave progress indicator
var waveProgressBg = LK.getAsset('playerBullet', {
anchorX: 1,
anchorY: 0.5,
scaleX: 3,
scaleY: 0.4,
tint: 0x444444
});
waveProgressBg.x = 2048 - 30;
waveProgressBg.y = 100;
LK.gui.addChild(waveProgressBg);
// Wave progress bar
var waveProgress = LK.getAsset('playerBullet', {
anchorX: 1,
anchorY: 0.5,
scaleX: 3 * (1 / 5),
// Start at 1/5 for wave 1
scaleY: 0.4,
tint: 0x3399ff
});
waveProgress.x = 2048 - 30;
waveProgress.y = 100;
LK.gui.addChild(waveProgress);
}
// Spawn enemy
function spawnEnemy() {
var enemy = new EnemyShip();
enemy.x = 100 + Math.random() * (2048 - 200);
enemy.y = -100;
enemy.direction = Math.random() > 0.5 ? 1 : -1;
// Adjust enemy stats based on wave
enemy.health = 30 + wave * 5;
enemy.speed = 2 + Math.min(wave * 0.3, 3);
enemy.fireRate = Math.max(120 - wave * 5, 60);
enemy.points = 100 + wave * 10;
enemies.push(enemy);
game.addChild(enemy);
waveEnemyCount++;
}
// Spawn asteroid
function spawnAsteroid() {
var asteroid = new Asteroid();
asteroid.x = 100 + Math.random() * (2048 - 200);
asteroid.y = -100;
asteroids.push(asteroid);
game.addChild(asteroid);
}
// Spawn power-up
function spawnPowerUp(x, y) {
var powerUp = new PowerUp();
powerUp.x = x;
powerUp.y = y;
powerUps.push(powerUp);
game.addChild(powerUp);
}
// Check collisions
function checkCollisions() {
// Player bullets vs enemies
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
var hitEnemy = false;
// Check against enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
hitEnemy = true;
if (enemy.damage(player.powerLevel * 10)) {
// Enemy destroyed
LK.setScore(LK.getScore() + enemy.points);
scoreText.setText('SCORE: ' + LK.getScore());
// Award 10 coins when enemy is destroyed
storage.coins = (storage.coins || 0) + 10;
// Update coins display if it exists
if (coinsDisplay) {
coinsDisplay.setText('COINS: ' + storage.coins);
}
// Chance to spawn power-up
if (Math.random() < powerUpChance) {
spawnPowerUp(enemy.x, enemy.y);
}
enemy.destroy();
enemies.splice(j, 1);
}
break;
}
}
// Check against asteroids if bullet didn't hit an enemy
if (!hitEnemy) {
for (var j = asteroids.length - 1; j >= 0; j--) {
var asteroid = asteroids[j];
if (bullet.intersects(asteroid)) {
hitEnemy = true;
if (asteroid.damage(player.powerLevel * 10)) {
// Asteroid destroyed
LK.setScore(LK.getScore() + asteroid.points);
scoreText.setText('SCORE: ' + LK.getScore());
// Small chance to spawn power-up
if (Math.random() < powerUpChance * 0.5) {
spawnPowerUp(asteroid.x, asteroid.y);
}
asteroid.destroy();
asteroids.splice(j, 1);
}
break;
}
}
}
// Remove bullet if it hit something or went off-screen
if (hitEnemy || bullet.update()) {
bullet.destroy();
playerBullets.splice(i, 1);
}
}
// Enemy bullets vs player
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
// Check if bullet should be removed
if (bullet.update()) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check against player
if (gameState === 'playing' && bullet.intersects(player)) {
if (player.damage(bullet.damage)) {
// Player destroyed
gameOver();
}
// Update health bar
updateHealthBar();
// Remove bullet
bullet.destroy();
enemyBullets.splice(i, 1);
}
}
// Enemies vs player
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
// Check if enemy should be removed
if (enemy.update()) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
// Check against player
if (gameState === 'playing' && enemy.intersects(player)) {
if (player.damage(30)) {
// Player destroyed
gameOver();
}
// Update health bar
updateHealthBar();
// Destroy enemy
enemy.destroy();
enemies.splice(i, 1);
}
}
// Asteroids vs player
for (var i = asteroids.length - 1; i >= 0; i--) {
var asteroid = asteroids[i];
// Check if asteroid should be removed
if (asteroid.update()) {
asteroid.destroy();
asteroids.splice(i, 1);
continue;
}
// Check against player
if (gameState === 'playing' && asteroid.intersects(player)) {
if (player.damage(20)) {
// Player destroyed
gameOver();
}
// Update health bar
updateHealthBar();
// Destroy asteroid
asteroid.destroy();
asteroids.splice(i, 1);
}
}
// Power-ups vs player
for (var i = powerUps.length - 1; i >= 0; i--) {
var powerUp = powerUps[i];
// Check if power-up should be removed
if (powerUp.update()) {
powerUp.destroy();
powerUps.splice(i, 1);
continue;
}
// Check against player
if (gameState === 'playing' && powerUp.intersects(player)) {
// Apply power-up effect
if (powerUp.type === 'health') {
player.health = Math.min(player.health + 30, 100);
updateHealthBar();
} else if (powerUp.type === 'weapon') {
player.powerLevel = Math.min(player.powerLevel + 1, 3);
}
// Play sound
LK.getSound('powerUpCollect').play();
// Remove power-up
powerUp.destroy();
powerUps.splice(i, 1);
}
}
}
// Update health bar
function updateHealthBar() {
healthBar.scale.x = 3 * (player.health / player.maxHealth);
// Change color based on health percentage
var healthPercent = player.health / player.maxHealth * 100;
if (healthPercent > 60) {
healthBar.tint = 0x33ff33; // Green
} else if (healthPercent > 30) {
healthBar.tint = 0xffcc33; // Yellow
} else {
healthBar.tint = 0xff3333; // Red
}
}
// Game over
function gameOver() {
gameState = 'gameover';
LK.effects.flashScreen(0xff0000, 500);
LK.setTimeout(function () {
LK.showGameOver();
// Return to home screen after game over
LK.setTimeout(function () {
showHomeScreen();
}, 1500);
}, 500);
}
// Handle input
game.down = function (x, y, obj) {
// Store touch identifier to track multiple touches
if (obj.identifier !== undefined) {
touchIdentifiers[obj.identifier] = {
x: x,
y: y
};
}
// Use entire screen for movement
if (player) {
// Set player target position
player.targetX = x;
player.targetY = y;
player.isMoving = true;
}
// No shoot button check needed
};
game.move = function (x, y, obj) {
// Update stored touch position
if (obj.identifier !== undefined && touchIdentifiers[obj.identifier]) {
touchIdentifiers[obj.identifier] = {
x: x,
y: y
};
}
// Update player target position if moving - make sure player exists first
if (player && player.isMoving) {
player.targetX = x;
player.targetY = y;
// Make player shoot when moving
if (LK.ticks % player.fireRate === 0) {
player.shoot();
}
}
};
game.up = function (x, y, obj) {
// Remove touch identifier
if (obj.identifier !== undefined) {
delete touchIdentifiers[obj.identifier];
}
// Stop player movement
if (player) {
player.isMoving = false;
}
// No shoot button to release
};
// Main game loop
game.update = function () {
// Show home screen at start
if (gameState === 'home' && !homeScreen) {
showHomeScreen();
return;
}
// Update player if it exists
if (player && gameState === 'playing') {
player.update();
}
// Spawn enemies
if (gameState === 'playing') {
// Check if wave is complete
if (waveEnemyCount >= waveMaxEnemies && enemies.length === 0) {
// Start new wave
wave++;
waveEnemyCount = 0;
waveMaxEnemies = 5 + wave * 5;
waveText.setText('WAVE: ' + wave);
// Flash wave text
tween(waveText, {
scale: 1.5
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(waveText, {
scale: 1
}, {
duration: 300,
easing: tween.easeIn
});
}
});
// Check for game win condition
if (wave > 5) {
gameState = 'gamewin';
LK.effects.flashScreen(0x33ff33, 500);
LK.setTimeout(function () {
LK.showYouWin();
// Return to home screen after win
LK.setTimeout(function () {
showHomeScreen();
}, 1500);
}, 500);
}
}
// Spawn enemies
if (waveEnemyCount < waveMaxEnemies) {
enemySpawnTimer++;
if (enemySpawnTimer > Math.max(120 - wave * 5, 30)) {
enemySpawnTimer = 0;
spawnEnemy();
}
}
// Update wave progress bar
var waveProgressBar = LK.gui.children.find(function (child) {
return child.tint === 0x3399ff && child.scaleY === 0.4;
});
if (waveProgressBar) {
// Update wave text
waveText.setText('WAVE: ' + wave + ' / 5');
// Show remaining enemies
var remaining = waveMaxEnemies - waveEnemyCount + enemies.length;
var progressScale = Math.min(wave / 5, 1);
waveProgressBar.scale.x = 3 * progressScale;
}
// Spawn asteroids
asteroidSpawnTimer++;
if (asteroidSpawnTimer > Math.max(180 - wave * 3, 60)) {
asteroidSpawnTimer = 0;
spawnAsteroid();
}
}
// Check collisions
checkCollisions();
// Update starfield
for (var i = 0; i < stars.length; i++) {
var star = stars[i];
star.y += star.speed;
if (star.y > 2732) {
star.y = 0;
star.x = Math.random() * 2048;
}
}
};
// Initialize home screen at startup
showHomeScreen();