User prompt
Double the player's health.
User prompt
When you kill an enemy it gives you 10 coins ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Delete the attack button. When you move, it makes you attack.
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'if (player.isMoving && x < 2048 / 2) {' Line Number: 1031
User prompt
Show a health bar on the top of your screen and add waves. After you defeat 5 waves, you win the game.
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'shootButton.up();' Line Number: 994
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'player.targetX = x;' Line Number: 958
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'shootButton.down(x, y);' Line Number: 964
User prompt
Please fix the bug: 'TypeError: undefined is not an object (evaluating 'player.update')' in or related to this line: 'player.update();' Line Number: 1002
User prompt
Make a home screen with a start button and a shop. Each time you kill an enemy, it gives you 2 points. ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Use your finger to move the player and delete the joystick.
User prompt
Make the joystick move the player.
Code edit (1 edits merged)
Please save this source code
User prompt
Space Defender
Initial prompt
Add a joystick and a shoot button, player and a gun.
/**** * 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();