Code edit (1 edits merged)
Please save this source code
User prompt
add sound for each event.
User prompt
write a small orange “Glaud” in a logical place on the screen.
User prompt
Make the text visible on the screen. The design should be good.
User prompt
Identify and solve some basic logic errors in the game.
User prompt
Identify and solve some basic logic errors in the game.
User prompt
Identify the features that will make it better and implement them in an optimized way, don't make any mistakes, it's clean and playable. Make it a user-friendly game. ↪💡 Consider importing and using the following plugins: @upit/tween.v1, @upit/storage.v1
User prompt
Identify the features that will make it better and implement them in an optimized way, don't make any mistakes, it's clean and playable. Make it a user-friendly game.
User prompt
Identify the features that will make it better and implement them in an optimized way, don't make any mistakes, it's clean and playable. Make it a user-friendly game. ↪💡 Consider importing and using the following plugins: @upit/tween.v1, @upit/storage.v1
User prompt
Identify the features that will make it better and implement them in an optimized way, don't make any mistakes, it's clean and playable. Make it a user-friendly game. ↪💡 Consider importing and using the following plugins: @upit/tween.v1, @upit/storage.v1
Code edit (1 edits merged)
Please save this source code
User prompt
Space Defender
Initial prompt
game
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0, level: 1 }); /**** * Classes ****/ var Asteroid = Container.expand(function () { var self = Container.call(this); // Random size asteroid var size = Math.random() * 0.7 + 0.7; // 0.7 to 1.4 var asteroidGraphics = self.attachAsset('asteroid', { anchorX: 0.5, anchorY: 0.5, scaleX: size, scaleY: size }); self.health = 30 * size; self.speed = 3 / size; // Bigger asteroids move slower self.rotationSpeed = (Math.random() - 0.5) * 0.05; self.points = Math.floor(50 * size); self.takeDamage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0x888888, 200); LK.getSound('enemyShoot', { volume: 0.5 }).play(); // Play hit sound tween(asteroidGraphics, { tint: 0xFFFFFF }, { duration: 50, onFinish: function onFinish() { asteroidGraphics.tint = 0x999999; } }); if (self.health <= 0) { self.onDestroyed(); } }; self.onDestroyed = function () { // Add points to score LK.setScore(LK.getScore() + self.points); // Create explosion var explosion = new Explosion(self.x, self.y, size); game.addChild(explosion); LK.getSound('explosion').play(); // Remove from game var index = asteroids.indexOf(self); if (index > -1) { asteroids.splice(index, 1); self.destroy(); } }; self.update = function () { self.y += self.speed; asteroidGraphics.rotation += self.rotationSpeed; // Check if asteroid is off-screen if (self.y > 2732 + 100) { var index = asteroids.indexOf(self); if (index > -1) { asteroids.splice(index, 1); self.destroy(); } } }; return self; }); var Boss = Container.expand(function () { var self = Container.call(this); var bossGraphics = self.attachAsset('boss', { anchorX: 0.5, anchorY: 0.5 }); self.health = 500; self.maxHealth = 500; self.speed = 2; self.points = 2000; self.active = false; self.phase = 1; self.directionX = 1; self.shootCooldown = 30; self.specialAttackCooldown = 300; // Health bar self.healthBar = self.attachAsset('healthBar', { anchorX: 0.5, anchorY: 0.5, y: -150 }); self.takeDamage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xff0000, 200); LK.getSound('enemyShoot', { volume: 0.5 }).play(); // Play hit sound tween(bossGraphics, { tint: 0xFFFFFF }, { duration: 50, onFinish: function onFinish() { bossGraphics.tint = 0xff3300; } }); // Update health bar scale self.healthBar.scaleX = Math.max(0, self.health / self.maxHealth); // Phase transition at 50% health if (self.health <= self.maxHealth / 2 && self.phase === 1) { self.phase = 2; self.speed = 4; LK.effects.flashObject(self, 0xff00ff, 1000); // Purple flash for phase transition } if (self.health <= 0) { self.onDestroyed(); } }; self.onDestroyed = function () { // Add points to score LK.setScore(LK.getScore() + self.points); // Create multiple explosions for (var i = 0; i < 8; i++) { var explosionX = self.x + (Math.random() * 300 - 150); var explosionY = self.y + (Math.random() * 200 - 100); var explosion = new Explosion(explosionX, explosionY, 2); game.addChild(explosion); // Stagger the explosions (function (delay) { LK.setTimeout(function () { LK.getSound('explosion').play(); }, delay * 200); })(i); } // Complete level level++; if (level > storage.level) { storage.level = level; } // Check high score if (LK.getScore() > storage.highScore) { storage.highScore = LK.getScore(); } // Show win screen LK.showYouWin(); // Remove from game var index = bosses.indexOf(self); if (index > -1) { bosses.splice(index, 1); self.destroy(); } }; self.shoot = function () { // Basic attack - 3 bullets for (var i = -1; i <= 1; i++) { var bullet = new EnemyBullet(); bullet.x = self.x + i * 80; bullet.y = self.y + 100; game.addChild(bullet); enemyBullets.push(bullet); } LK.getSound('enemyShoot').play(); }; self.specialAttack = function () { // Special attack based on phase if (self.phase === 1) { // Phase 1: Circle of 8 bullets for (var i = 0; i < 8; i++) { var angle = i / 8 * Math.PI * 2; var bullet = new EnemyBullet(); bullet.x = self.x + Math.cos(angle) * 100; bullet.y = self.y + Math.sin(angle) * 100; bullet.directionX = Math.cos(angle); bullet.directionY = Math.sin(angle); game.addChild(bullet); enemyBullets.push(bullet); } } else { // Phase 2: Targeted volley at player for (var i = 0; i < 5; i++) { var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y + 100; // Target the player var dx = player.x - bullet.x; var dy = player.y - bullet.y; var dist = Math.sqrt(dx * dx + dy * dy); bullet.directionX = dx / dist; bullet.directionY = dy / dist; bullet.speed = 8 + i; game.addChild(bullet); enemyBullets.push(bullet); // Timeout was empty and served no purpose, bullet already added. } } LK.getSound('enemyShoot').play(); }; self.update = function () { if (!self.active) { return; } // Movement self.x += self.speed * self.directionX; // Bounce at screen edges if (self.x < 300) { self.x = 300; self.directionX = 1; } else if (self.x > 2048 - 300) { self.x = 2048 - 300; self.directionX = -1; } // Regular attacks self.shootCooldown--; if (self.shootCooldown <= 0) { self.shoot(); self.shootCooldown = self.phase === 1 ? 60 : 40; } // Special attacks self.specialAttackCooldown--; if (self.specialAttackCooldown <= 0) { self.specialAttack(); self.specialAttackCooldown = 300; } }; self.activate = function () { self.active = true; }; return self; }); var Enemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.health = 20; self.speed = 2; self.shootCooldown = Math.floor(Math.random() * 120) + 60; self.points = 100; self.movementPattern = "straight"; // Default movement pattern self.directionX = 0; self.directionChangeTimer = 0; self.takeDamage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xff0000, 200); LK.getSound('enemyShoot', { volume: 0.5 }).play(); // Play hit sound tween(enemyGraphics, { tint: 0xFFFFFF }, { duration: 50, onFinish: function onFinish() { enemyGraphics.tint = 0xff0000; } }); if (self.health <= 0) { self.onDestroyed(); } }; self.onDestroyed = function () { // Add points to score LK.setScore(LK.getScore() + self.points); // Create explosion var explosion = new Explosion(self.x, self.y, 1); game.addChild(explosion); LK.getSound('explosion').play(); // Increment enemies defeated count for level progression enemiesDefeated++; // Remove from game first var index = enemies.indexOf(self); if (index > -1) { enemies.splice(index, 1); // Check if should drop powerup (20% chance) after removal is confirmed if (Math.random() < 0.2) { var powerup = new Powerup(); powerup.x = self.x; powerup.y = self.y; game.addChild(powerup); powerups.push(powerup); } // Remove from game self.destroy(); } }; self.shoot = function () { var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y + 50; game.addChild(bullet); enemyBullets.push(bullet); LK.getSound('enemyShoot').play(); }; self.setMovementPattern = function (pattern) { self.movementPattern = pattern; if (pattern === "zigzag") { self.directionX = Math.random() < 0.5 ? -1 : 1; self.speed = 3; } else if (pattern === "circle") { self.angle = Math.random() * Math.PI * 2; self.centerX = self.x; self.centerY = self.y; self.radius = 100; self.speed = 0.02; } else if (pattern === "sine") { self.startX = self.x; self.amplitude = 150; self.frequency = 0.01; self.angle = Math.random() * Math.PI * 2; } }; self.update = function () { // Movement based on pattern if (self.movementPattern === "straight") { self.y += self.speed; } else if (self.movementPattern === "zigzag") { self.y += self.speed; self.x += self.speed * 2 * self.directionX; self.directionChangeTimer++; if (self.directionChangeTimer > 60) { self.directionX *= -1; self.directionChangeTimer = 0; } // Bounce off screen edges if (self.x < 50) { self.x = 50; self.directionX = 1; } else if (self.x > 2048 - 50) { self.x = 2048 - 50; self.directionX = -1; } } else if (self.movementPattern === "circle") { self.angle += self.speed; self.x = self.centerX + Math.cos(self.angle) * self.radius; self.y = self.centerY + Math.sin(self.angle) * self.radius + self.speed * 20; } else if (self.movementPattern === "sine") { self.y += self.speed * 2; self.angle += self.frequency; self.x = self.startX + Math.sin(self.angle) * self.amplitude; } // Shooting self.shootCooldown--; if (self.shootCooldown <= 0) { self.shoot(); self.shootCooldown = Math.floor(Math.random() * 120) + 120; } // Remove if off screen if (self.y > 2732 + 100) { var index = enemies.indexOf(self); if (index > -1) { enemies.splice(index, 1); self.destroy(); } } }; 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.directionX = 0; self.directionY = 1; self.update = function () { // Move based on direction self.x += self.directionX * self.speed; self.y += self.directionY * self.speed; // Check if bullet is off-screen if (self.y > 2732 + 50 || self.y < -50 || self.x < -50 || self.x > 2048 + 50) { self.removeFromGame(); } }; self.removeFromGame = function () { var index = enemyBullets.indexOf(self); if (index > -1) { enemyBullets.splice(index, 1); self.destroy(); } }; return self; }); var Explosion = Container.expand(function (x, y, size) { var self = Container.call(this); self.x = x; self.y = y; var explosionGraphics = self.attachAsset('explosion', { anchorX: 0.5, anchorY: 0.5, scaleX: size || 1, scaleY: size || 1, alpha: 0.9 }); // Animation for explosion self.duration = 30; // 0.5 seconds at 60fps self.timer = 0; self.update = function () { self.timer++; // Scale up and fade out var progress = self.timer / self.duration; explosionGraphics.scaleX = (size || 1) * (1 + progress); explosionGraphics.scaleY = (size || 1) * (1 + progress); explosionGraphics.alpha = 1 - progress; if (self.timer >= self.duration) { self.destroy(); } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.health = 100; self.speed = 15; self.shootCooldown = 0; self.invulnerable = false; self.powerupTimer = 0; self.powerupActive = false; self.takeDamage = function (amount) { if (self.invulnerable) { return; } self.health -= amount; LK.getSound('enemyShoot', { volume: 0.7 }).play(); // Play player hit sound if (self.health <= 0) { self.health = 0; self.onDestroyed(); } else { // Flash player when hit self.invulnerable = true; LK.effects.flashObject(self, 0xffffff, 500); // Screen shake on hit LK.effects.flashScreen(0xFF0000, 300); // Create small explosions around player for visual feedback for (var i = 0; i < 3; i++) { var explosionX = self.x + (Math.random() * 150 - 75); var explosionY = self.y + (Math.random() * 150 - 75); var smallExplosion = new Explosion(explosionX, explosionY, 0.5); game.addChild(smallExplosion); } LK.setTimeout(function () { self.invulnerable = false; }, 1000); } }; self.applyPowerup = function () { self.powerupActive = true; self.powerupTimer = 300; // 5 seconds at 60fps playerGraphics.tint = 0x00ff00; // Green tint for powered up LK.getSound('powerup').play(); // Visual effect for power-up tween(playerGraphics, { alpha: 0.5, scaleX: 1.2, scaleY: 1.2 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(playerGraphics, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeOut }); } }); // Create a powerup aura if (!self.powerupAura) { self.powerupAura = LK.getAsset('powerup', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2, alpha: 0.3, tint: 0x00FFAA }); self.addChild(self.powerupAura); } else { self.powerupAura.alpha = 0.3; } }; self.shoot = function () { if (self.shootCooldown <= 0) { var bullet = new PlayerBullet(); bullet.x = self.x; bullet.y = self.y - 70; game.addChild(bullet); bullets.push(bullet); LK.getSound('playerShoot').play(); // If powered up, shoot 3 bullets instead of 1 if (self.powerupActive) { var bulletLeft = new PlayerBullet(); bulletLeft.x = self.x - 40; bulletLeft.y = self.y - 50; var bulletRight = new PlayerBullet(); bulletRight.x = self.x + 40; bulletRight.y = self.y - 50; game.addChild(bulletLeft); game.addChild(bulletRight); bullets.push(bulletLeft); bullets.push(bulletRight); } self.shootCooldown = self.powerupActive ? 10 : 15; } }; self.onDestroyed = function () { var explosion = new Explosion(self.x, self.y, 2); game.addChild(explosion); LK.getSound('explosion').play(); // Show game over LK.showGameOver(); }; self.update = function () { if (self.shootCooldown > 0) { self.shootCooldown--; } if (self.powerupActive) { self.powerupTimer--; // Animate the powerup aura if (self.powerupAura) { self.powerupAura.rotation += 0.02; // Pulse the aura self.powerupAuraTime = (self.powerupAuraTime || 0) + 0.05; self.powerupAura.alpha = 0.2 + Math.sin(self.powerupAuraTime) * 0.1; self.powerupAura.scale.set(1.8 + Math.sin(self.powerupAuraTime) * 0.2); } // Warning animation when powerup is about to expire if (self.powerupTimer < 60) { // Flash the player when powerup is about to end playerGraphics.alpha = 0.5 + Math.sin(self.powerupTimer * 0.2) * 0.5; } if (self.powerupTimer <= 0) { self.powerupActive = false; playerGraphics.alpha = 1; // Remove powerup aura if (self.powerupAura) { self.powerupAura.destroy(); self.powerupAura = null; } // Reset powerup aura animation timer - Redundant as aura is destroyed // self.powerupAuraTime = 0; // The aura is already destroyed above, removing duplicate block. } } }; 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; // Move upwards self.damage = 10; self.update = function () { self.y += self.speed; // Check if bullet is off-screen if (self.y < -50) { self.removeFromGame(); } }; self.removeFromGame = function () { var index = bullets.indexOf(self); if (index > -1) { bullets.splice(index, 1); self.destroy(); } }; return self; }); var Powerup = Container.expand(function () { var self = Container.call(this); var powerupGraphics = self.attachAsset('powerup', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 3; self.pulsateTimer = 0; self.update = function () { self.y += self.speed; // Pulsate effect self.pulsateTimer += 0.05; var scale = 1 + Math.sin(self.pulsateTimer) * 0.2; powerupGraphics.scaleX = scale; powerupGraphics.scaleY = scale; powerupGraphics.alpha = 0.7 + Math.sin(self.pulsateTimer) * 0.3; // Pulsate alpha // Remove if off screen if (self.y > 2732 + 50) { self.removeFromGame(); } }; self.removeFromGame = function () { var index = powerups.indexOf(self); if (index > -1) { powerups.splice(index, 1); self.destroy(); } }; self.alpha = 0; self.scale.set(0.5); tween(self, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeOut }); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000011 // Deep space blue }); /**** * Game Code ****/ // Game state variables var player; var bullets = []; var enemies = []; var enemyBullets = []; var asteroids = []; var powerups = []; var bosses = []; var level = storage.level || 1; var enemySpawnTimer = 0; var asteroidSpawnTimer = 0; var bossSpawned = false; var gameStarted = false; var levelEnemiesRequired; var enemiesDefeated = 0; var dragNode = null; // Set up UI var scoreTxt = new Text2('Score: 0', { size: 70, fill: 0xFFFFFF }); scoreTxt.anchor.set(1, 0); // Anchor top-right scoreTxt.x = -20; // Padding from right edge scoreTxt.y = 20; // Padding from top edge LK.gui.topRight.addChild(scoreTxt); var highScoreTxt = new Text2('High Score: ' + storage.highScore, { size: 60, // Increased size fill: 0xFFFFFF }); highScoreTxt.anchor.set(1, 0); // Anchor top-right highScoreTxt.x = -20; // Align with scoreTxt padding highScoreTxt.y = scoreTxt.y + scoreTxt.height + 10; // Position below scoreTxt LK.gui.topRight.addChild(highScoreTxt); var levelTxt = new Text2('Level: ' + level, { size: 70, fill: 0xFFFFFF }); levelTxt.anchor.set(0, 0); // Anchor top-left now LK.gui.topLeft.addChild(levelTxt); levelTxt.x = 120; // Move slightly right from absolute left levelTxt.y = 20; // Move slightly down from absolute top, matching score padding // Add Glaud text var glaudTxt = new Text2('Glaud', { size: 40, // Small size fill: 0xFF8C00 // Orange color }); glaudTxt.anchor.set(0, 0); // Anchor top-left // Position below levelTxt glaudTxt.x = levelTxt.x; glaudTxt.y = levelTxt.y + levelTxt.height + 10; // 10px padding below LK.gui.topLeft.addChild(glaudTxt); var healthBar = LK.getAsset('healthBar', { anchorX: 0, anchorY: 0.5, scaleX: 1, scaleY: 0.6 }); // Center the health bar horizontally relative to the bottom-center anchor healthBar.x = 0; // Position the bar slightly up from the absolute bottom edge healthBar.y = -50; LK.gui.bottom.addChild(healthBar); var healthTxt = new Text2('Health', { size: 60, fill: 0xFFFFFF }); healthTxt.anchor.set(0.5, 1); // Anchor bottom-center // Position text centered horizontally, just above the health bar healthTxt.x = healthBar.x; healthTxt.y = healthBar.y - healthBar.height * healthBar.scaleY / 2 - 10; LK.gui.bottom.addChild(healthTxt); // Progress bar for level completion var progressBarBg = LK.getAsset('healthBar', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 0.4, tint: 0x555555 }); progressBarBg.x = 2048 / 2; progressBarBg.y = 100; LK.gui.top.addChild(progressBarBg); var progressBar = LK.getAsset('healthBar', { anchorX: 0, anchorY: 0.5, scaleX: 0, scaleY: 0.3, tint: 0x00FFFF }); progressBar.x = 2048 / 2 - progressBarBg.width / 2; progressBar.y = 100; LK.gui.top.addChild(progressBar); var progressTxt = new Text2('Progress', { size: 50, // Increased size fill: 0xFFFFFF }); progressTxt.anchor.set(0.5, 1); // Anchor bottom-center progressTxt.x = progressBarBg.x; // Position text above the background bar progressTxt.y = progressBarBg.y - progressBarBg.height * progressBarBg.scaleY / 2 - 15; LK.gui.top.addChild(progressTxt); // Initialize player function initPlayer() { player = new Player(); player.x = 2048 / 2; player.y = 2732 + 100; // Start off-screen game.addChild(player); // Animate player entrance tween(player, { y: 2732 - 200, alpha: 1 }, { duration: 1000, easing: tween.easeOutBack }); } // Initialize level function initLevel() { // Clear any existing enemies and bullets for (var i = enemies.length - 1; i >= 0; i--) { enemies[i].destroy(); } for (var i = enemyBullets.length - 1; i >= 0; i--) { enemyBullets[i].destroy(); } for (var i = asteroids.length - 1; i >= 0; i--) { asteroids[i].destroy(); } for (var i = powerups.length - 1; i >= 0; i--) { powerups[i].destroy(); } for (var i = bosses.length - 1; i >= 0; i--) { bosses[i].destroy(); } enemies = []; enemyBullets = []; asteroids = []; powerups = []; bosses = []; bossSpawned = false; enemiesDefeated = 0; // Set level difficulty levelEnemiesRequired = level * 15; enemySpawnTimer = 60; asteroidSpawnTimer = 180; // Update UI levelTxt.setText('Level: ' + level); // Stop pulsating effect on progress text if active if (progressTxt.pulsating) { tween.cancel(progressTxt.scale); tween.cancel(progressTxt); progressTxt.scale.set(1); progressTxt.alpha = 1; progressTxt.pulsating = false; } // Start music LK.playMusic('bgMusic'); } // Spawn enemy with random pattern function spawnEnemy() { var enemy = new Enemy(); // Random x position enemy.x = Math.random() * (2048 - 200) + 100; enemy.y = -100; // Set health and pattern based on level enemy.health = 20 + level * 5; // Choose movement pattern var patterns = ["straight", "zigzag", "sine"]; if (level >= 3) { patterns.push("circle"); } var pattern = patterns[Math.floor(Math.random() * patterns.length)]; enemy.setMovementPattern(pattern); // Add spawn animation enemy.alpha = 0; enemy.scale.set(0.5); game.addChild(enemy); enemies.push(enemy); // Animate entrance tween(enemy, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 500, easing: tween.easeOutBack }); } // Spawn asteroid function spawnAsteroid() { var asteroid = new Asteroid(); // Random x position asteroid.x = Math.random() * (2048 - 200) + 100; asteroid.y = -100; game.addChild(asteroid); asteroids.push(asteroid); // Add entry animation asteroid.alpha = 0.5; tween(asteroid, { alpha: 1, rotation: Math.random() * Math.PI * 2 }, { duration: 800, easing: tween.easeOut }); } // Spawn boss function spawnBoss() { var boss = new Boss(); boss.x = 2048 / 2; boss.y = 300; // Adjust boss health based on level boss.health = 500 + level * 100; boss.maxHealth = boss.health; game.addChild(boss); bosses.push(boss); LK.getSound('explosion', { volume: 0.8 }).play(); // Play boss spawn sound // Activate boss after entrance animation tween(boss, { y: 300 }, { duration: 2000, easing: tween.easeOutBack, onFinish: function onFinish() { boss.activate(); } }); } // Check collisions function checkCollisions() { // Player bullets vs enemies for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; // Check against enemies for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (bullet.intersects(enemy)) { // Create small impact effect var impact = new Explosion(bullet.x, bullet.y, 0.3); impact.duration = 15; // Shorter duration for impact game.addChild(impact); enemy.takeDamage(bullet.damage); bullet.removeFromGame(); break; } } // Check against asteroids for (var j = asteroids.length - 1; j >= 0; j--) { var asteroid = asteroids[j]; if (bullet.intersects(asteroid)) { // Create small impact effect var impact = new Explosion(bullet.x, bullet.y, 0.3); impact.duration = 15; // Shorter duration for impact game.addChild(impact); asteroid.takeDamage(bullet.damage); bullet.removeFromGame(); break; } } // Check against bosses for (var j = bosses.length - 1; j >= 0; j--) { var boss = bosses[j]; if (bullet.intersects(boss) && boss.active) { // Create small impact effect var impact = new Explosion(bullet.x, bullet.y, 0.3); impact.duration = 15; // Shorter duration for impact game.addChild(impact); boss.takeDamage(bullet.damage); bullet.removeFromGame(); break; } } } // Enemy bullets vs player for (var i = enemyBullets.length - 1; i >= 0; i--) { var bullet = enemyBullets[i]; if (bullet.intersects(player)) { player.takeDamage(bullet.damage); bullet.removeFromGame(); } } // Player vs enemies for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; if (player.intersects(enemy)) { player.takeDamage(20); enemy.takeDamage(enemy.health); // Destroy enemy } } // Player vs asteroids for (var i = asteroids.length - 1; i >= 0; i--) { var asteroid = asteroids[i]; if (player.intersects(asteroid)) { player.takeDamage(30); asteroid.takeDamage(asteroid.health); // Destroy asteroid } } // Player vs powerups for (var i = powerups.length - 1; i >= 0; i--) { var powerup = powerups[i]; if (player.intersects(powerup)) { // Remove the powerup first to prevent potential multiple applications powerup.removeFromGame(); // Then apply the effect player.applyPowerup(); } } } // Handle player movement function handleDrag(x, y) { if (dragNode) { dragNode.x = x; dragNode.y = y; // Constrain player to screen bounds if (dragNode.x < 50) { dragNode.x = 50; } if (dragNode.x > 2048 - 50) { dragNode.x = 2048 - 50; } if (dragNode.y < 50) { dragNode.y = 50; } if (dragNode.y > 2732 - 50) { dragNode.y = 2732 - 50; } } } // Event handlers game.down = function (x, y, obj) { // Only set drag if we're started if (gameStarted) { dragNode = player; handleDrag(x, y); } else { // Start game on first touch gameStarted = true; initPlayer(); initLevel(); } }; game.up = function (x, y, obj) { dragNode = null; }; game.move = function (x, y, obj) { handleDrag(x, y); }; // Main game update loop game.update = function () { if (!gameStarted) { // Show start screen text if (!game.startText) { game.startText = new Text2('Tap to Start\nSpace Defender', { size: 150, fill: 0xFFFFFF }); game.startText.anchor.set(0.5, 0.5); game.startText.x = 2048 / 2; game.startText.y = 2732 / 2; game.addChild(game.startText); // Add background stars for start screen game.stars = []; for (var i = 0; i < 50; i++) { var star = LK.getAsset('playerBullet', { anchorX: 0.5, anchorY: 0.5, scaleX: Math.random() * 0.3 + 0.1, scaleY: Math.random() * 0.3 + 0.1, alpha: Math.random() * 0.7 + 0.3, tint: 0xFFFFFF }); star.x = Math.random() * 2048; star.y = Math.random() * 2732; star.speed = Math.random() * 2 + 1; game.addChild(star); game.stars.push(star); } // Add pulsating effect to start text game.pulseValue = 0; // Add pulsing to the start text using tween tween(game.startText, { alpha: 0.5 }, { duration: 1000, easing: tween.easeInOut, loop: true, yoyo: true }); } // Animate stars in start screen if (game.stars) { // Manual scaling removed, handled by tween animation // game.pulseValue += 0.05; // game.startText.scale.set(1 + Math.sin(game.pulseValue) * 0.05); for (var i = 0; i < game.stars.length; i++) { var star = game.stars[i]; star.y += star.speed; if (star.y > 2732) { star.y = 0; star.x = Math.random() * 2048; } } } return; } else if (game.startText) { game.startText.destroy(); game.startText = null; // Clean up stars if (game.stars) { for (var i = 0; i < game.stars.length; i++) { game.stars[i].destroy(); } game.stars = null; } } // Update UI scoreTxt.setText('Score: ' + LK.getScore()); // Update high score if needed if (LK.getScore() > storage.highScore) { storage.highScore = LK.getScore(); highScoreTxt.setText('High Score: ' + storage.highScore); } // Update progress bar if (!bossSpawned) { var progress = enemiesDefeated / levelEnemiesRequired; progressBar.scaleX = progress; progressTxt.setText('Level Progress: ' + Math.floor(progress * 100) + '%'); } else { progressTxt.setText('BOSS FIGHT!'); if (!progressTxt.pulsating) { progressTxt.pulsating = true; tween(progressTxt.scale, { x: 1.2, y: 1.2 }, { duration: 300, easing: tween.easeInOut, loop: true, yoyo: true }); tween(progressTxt, { alpha: 0.5 }, { duration: 300, easing: tween.easeInOut, loop: true, yoyo: true }); } progressBar.tint = 0xFF0000; progressBar.scaleX = 1; } if (player) { // Update health bar healthBar.scaleX = Math.max(0, player.health / 100); // Auto-fire player.shoot(); } // Spawn enemies if (!bossSpawned) { enemySpawnTimer--; if (enemySpawnTimer <= 0) { spawnEnemy(); // Spawn rate increases with level enemySpawnTimer = Math.max(20, 100 - level * 10); } // Spawn asteroids asteroidSpawnTimer--; if (asteroidSpawnTimer <= 0) { spawnAsteroid(); asteroidSpawnTimer = Math.max(60, 300 - level * 20); } // Check if we should spawn boss if (enemiesDefeated >= levelEnemiesRequired) { bossSpawned = true; spawnBoss(); } } // Update all game objects for (var i = bullets.length - 1; i >= 0; i--) { bullets[i].update(); } for (var i = enemies.length - 1; i >= 0; i--) { enemies[i].update(); } for (var i = enemyBullets.length - 1; i >= 0; i--) { enemyBullets[i].update(); } for (var i = asteroids.length - 1; i >= 0; i--) { asteroids[i].update(); } for (var i = powerups.length - 1; i >= 0; i--) { powerups[i].update(); } for (var i = bosses.length - 1; i >= 0; i--) { bosses[i].update(); } // Check for collisions checkCollisions(); }; // Start game music LK.playMusic('bgMusic', { fade: { start: 0, end: 0.3, duration: 1000 } });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
level: 1
});
/****
* Classes
****/
var Asteroid = Container.expand(function () {
var self = Container.call(this);
// Random size asteroid
var size = Math.random() * 0.7 + 0.7; // 0.7 to 1.4
var asteroidGraphics = self.attachAsset('asteroid', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: size,
scaleY: size
});
self.health = 30 * size;
self.speed = 3 / size; // Bigger asteroids move slower
self.rotationSpeed = (Math.random() - 0.5) * 0.05;
self.points = Math.floor(50 * size);
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0x888888, 200);
LK.getSound('enemyShoot', {
volume: 0.5
}).play(); // Play hit sound
tween(asteroidGraphics, {
tint: 0xFFFFFF
}, {
duration: 50,
onFinish: function onFinish() {
asteroidGraphics.tint = 0x999999;
}
});
if (self.health <= 0) {
self.onDestroyed();
}
};
self.onDestroyed = function () {
// Add points to score
LK.setScore(LK.getScore() + self.points);
// Create explosion
var explosion = new Explosion(self.x, self.y, size);
game.addChild(explosion);
LK.getSound('explosion').play();
// Remove from game
var index = asteroids.indexOf(self);
if (index > -1) {
asteroids.splice(index, 1);
self.destroy();
}
};
self.update = function () {
self.y += self.speed;
asteroidGraphics.rotation += self.rotationSpeed;
// Check if asteroid is off-screen
if (self.y > 2732 + 100) {
var index = asteroids.indexOf(self);
if (index > -1) {
asteroids.splice(index, 1);
self.destroy();
}
}
};
return self;
});
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 500;
self.maxHealth = 500;
self.speed = 2;
self.points = 2000;
self.active = false;
self.phase = 1;
self.directionX = 1;
self.shootCooldown = 30;
self.specialAttackCooldown = 300;
// Health bar
self.healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5,
y: -150
});
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xff0000, 200);
LK.getSound('enemyShoot', {
volume: 0.5
}).play(); // Play hit sound
tween(bossGraphics, {
tint: 0xFFFFFF
}, {
duration: 50,
onFinish: function onFinish() {
bossGraphics.tint = 0xff3300;
}
});
// Update health bar scale
self.healthBar.scaleX = Math.max(0, self.health / self.maxHealth);
// Phase transition at 50% health
if (self.health <= self.maxHealth / 2 && self.phase === 1) {
self.phase = 2;
self.speed = 4;
LK.effects.flashObject(self, 0xff00ff, 1000); // Purple flash for phase transition
}
if (self.health <= 0) {
self.onDestroyed();
}
};
self.onDestroyed = function () {
// Add points to score
LK.setScore(LK.getScore() + self.points);
// Create multiple explosions
for (var i = 0; i < 8; i++) {
var explosionX = self.x + (Math.random() * 300 - 150);
var explosionY = self.y + (Math.random() * 200 - 100);
var explosion = new Explosion(explosionX, explosionY, 2);
game.addChild(explosion);
// Stagger the explosions
(function (delay) {
LK.setTimeout(function () {
LK.getSound('explosion').play();
}, delay * 200);
})(i);
}
// Complete level
level++;
if (level > storage.level) {
storage.level = level;
}
// Check high score
if (LK.getScore() > storage.highScore) {
storage.highScore = LK.getScore();
}
// Show win screen
LK.showYouWin();
// Remove from game
var index = bosses.indexOf(self);
if (index > -1) {
bosses.splice(index, 1);
self.destroy();
}
};
self.shoot = function () {
// Basic attack - 3 bullets
for (var i = -1; i <= 1; i++) {
var bullet = new EnemyBullet();
bullet.x = self.x + i * 80;
bullet.y = self.y + 100;
game.addChild(bullet);
enemyBullets.push(bullet);
}
LK.getSound('enemyShoot').play();
};
self.specialAttack = function () {
// Special attack based on phase
if (self.phase === 1) {
// Phase 1: Circle of 8 bullets
for (var i = 0; i < 8; i++) {
var angle = i / 8 * Math.PI * 2;
var bullet = new EnemyBullet();
bullet.x = self.x + Math.cos(angle) * 100;
bullet.y = self.y + Math.sin(angle) * 100;
bullet.directionX = Math.cos(angle);
bullet.directionY = Math.sin(angle);
game.addChild(bullet);
enemyBullets.push(bullet);
}
} else {
// Phase 2: Targeted volley at player
for (var i = 0; i < 5; i++) {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + 100;
// Target the player
var dx = player.x - bullet.x;
var dy = player.y - bullet.y;
var dist = Math.sqrt(dx * dx + dy * dy);
bullet.directionX = dx / dist;
bullet.directionY = dy / dist;
bullet.speed = 8 + i;
game.addChild(bullet);
enemyBullets.push(bullet);
// Timeout was empty and served no purpose, bullet already added.
}
}
LK.getSound('enemyShoot').play();
};
self.update = function () {
if (!self.active) {
return;
}
// Movement
self.x += self.speed * self.directionX;
// Bounce at screen edges
if (self.x < 300) {
self.x = 300;
self.directionX = 1;
} else if (self.x > 2048 - 300) {
self.x = 2048 - 300;
self.directionX = -1;
}
// Regular attacks
self.shootCooldown--;
if (self.shootCooldown <= 0) {
self.shoot();
self.shootCooldown = self.phase === 1 ? 60 : 40;
}
// Special attacks
self.specialAttackCooldown--;
if (self.specialAttackCooldown <= 0) {
self.specialAttack();
self.specialAttackCooldown = 300;
}
};
self.activate = function () {
self.active = true;
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 20;
self.speed = 2;
self.shootCooldown = Math.floor(Math.random() * 120) + 60;
self.points = 100;
self.movementPattern = "straight"; // Default movement pattern
self.directionX = 0;
self.directionChangeTimer = 0;
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xff0000, 200);
LK.getSound('enemyShoot', {
volume: 0.5
}).play(); // Play hit sound
tween(enemyGraphics, {
tint: 0xFFFFFF
}, {
duration: 50,
onFinish: function onFinish() {
enemyGraphics.tint = 0xff0000;
}
});
if (self.health <= 0) {
self.onDestroyed();
}
};
self.onDestroyed = function () {
// Add points to score
LK.setScore(LK.getScore() + self.points);
// Create explosion
var explosion = new Explosion(self.x, self.y, 1);
game.addChild(explosion);
LK.getSound('explosion').play();
// Increment enemies defeated count for level progression
enemiesDefeated++;
// Remove from game first
var index = enemies.indexOf(self);
if (index > -1) {
enemies.splice(index, 1);
// Check if should drop powerup (20% chance) after removal is confirmed
if (Math.random() < 0.2) {
var powerup = new Powerup();
powerup.x = self.x;
powerup.y = self.y;
game.addChild(powerup);
powerups.push(powerup);
}
// Remove from game
self.destroy();
}
};
self.shoot = function () {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + 50;
game.addChild(bullet);
enemyBullets.push(bullet);
LK.getSound('enemyShoot').play();
};
self.setMovementPattern = function (pattern) {
self.movementPattern = pattern;
if (pattern === "zigzag") {
self.directionX = Math.random() < 0.5 ? -1 : 1;
self.speed = 3;
} else if (pattern === "circle") {
self.angle = Math.random() * Math.PI * 2;
self.centerX = self.x;
self.centerY = self.y;
self.radius = 100;
self.speed = 0.02;
} else if (pattern === "sine") {
self.startX = self.x;
self.amplitude = 150;
self.frequency = 0.01;
self.angle = Math.random() * Math.PI * 2;
}
};
self.update = function () {
// Movement based on pattern
if (self.movementPattern === "straight") {
self.y += self.speed;
} else if (self.movementPattern === "zigzag") {
self.y += self.speed;
self.x += self.speed * 2 * self.directionX;
self.directionChangeTimer++;
if (self.directionChangeTimer > 60) {
self.directionX *= -1;
self.directionChangeTimer = 0;
}
// Bounce off screen edges
if (self.x < 50) {
self.x = 50;
self.directionX = 1;
} else if (self.x > 2048 - 50) {
self.x = 2048 - 50;
self.directionX = -1;
}
} else if (self.movementPattern === "circle") {
self.angle += self.speed;
self.x = self.centerX + Math.cos(self.angle) * self.radius;
self.y = self.centerY + Math.sin(self.angle) * self.radius + self.speed * 20;
} else if (self.movementPattern === "sine") {
self.y += self.speed * 2;
self.angle += self.frequency;
self.x = self.startX + Math.sin(self.angle) * self.amplitude;
}
// Shooting
self.shootCooldown--;
if (self.shootCooldown <= 0) {
self.shoot();
self.shootCooldown = Math.floor(Math.random() * 120) + 120;
}
// Remove if off screen
if (self.y > 2732 + 100) {
var index = enemies.indexOf(self);
if (index > -1) {
enemies.splice(index, 1);
self.destroy();
}
}
};
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.directionX = 0;
self.directionY = 1;
self.update = function () {
// Move based on direction
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
// Check if bullet is off-screen
if (self.y > 2732 + 50 || self.y < -50 || self.x < -50 || self.x > 2048 + 50) {
self.removeFromGame();
}
};
self.removeFromGame = function () {
var index = enemyBullets.indexOf(self);
if (index > -1) {
enemyBullets.splice(index, 1);
self.destroy();
}
};
return self;
});
var Explosion = Container.expand(function (x, y, size) {
var self = Container.call(this);
self.x = x;
self.y = y;
var explosionGraphics = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: size || 1,
scaleY: size || 1,
alpha: 0.9
});
// Animation for explosion
self.duration = 30; // 0.5 seconds at 60fps
self.timer = 0;
self.update = function () {
self.timer++;
// Scale up and fade out
var progress = self.timer / self.duration;
explosionGraphics.scaleX = (size || 1) * (1 + progress);
explosionGraphics.scaleY = (size || 1) * (1 + progress);
explosionGraphics.alpha = 1 - progress;
if (self.timer >= self.duration) {
self.destroy();
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.speed = 15;
self.shootCooldown = 0;
self.invulnerable = false;
self.powerupTimer = 0;
self.powerupActive = false;
self.takeDamage = function (amount) {
if (self.invulnerable) {
return;
}
self.health -= amount;
LK.getSound('enemyShoot', {
volume: 0.7
}).play(); // Play player hit sound
if (self.health <= 0) {
self.health = 0;
self.onDestroyed();
} else {
// Flash player when hit
self.invulnerable = true;
LK.effects.flashObject(self, 0xffffff, 500);
// Screen shake on hit
LK.effects.flashScreen(0xFF0000, 300);
// Create small explosions around player for visual feedback
for (var i = 0; i < 3; i++) {
var explosionX = self.x + (Math.random() * 150 - 75);
var explosionY = self.y + (Math.random() * 150 - 75);
var smallExplosion = new Explosion(explosionX, explosionY, 0.5);
game.addChild(smallExplosion);
}
LK.setTimeout(function () {
self.invulnerable = false;
}, 1000);
}
};
self.applyPowerup = function () {
self.powerupActive = true;
self.powerupTimer = 300; // 5 seconds at 60fps
playerGraphics.tint = 0x00ff00; // Green tint for powered up
LK.getSound('powerup').play();
// Visual effect for power-up
tween(playerGraphics, {
alpha: 0.5,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(playerGraphics, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut
});
}
});
// Create a powerup aura
if (!self.powerupAura) {
self.powerupAura = LK.getAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2,
alpha: 0.3,
tint: 0x00FFAA
});
self.addChild(self.powerupAura);
} else {
self.powerupAura.alpha = 0.3;
}
};
self.shoot = function () {
if (self.shootCooldown <= 0) {
var bullet = new PlayerBullet();
bullet.x = self.x;
bullet.y = self.y - 70;
game.addChild(bullet);
bullets.push(bullet);
LK.getSound('playerShoot').play();
// If powered up, shoot 3 bullets instead of 1
if (self.powerupActive) {
var bulletLeft = new PlayerBullet();
bulletLeft.x = self.x - 40;
bulletLeft.y = self.y - 50;
var bulletRight = new PlayerBullet();
bulletRight.x = self.x + 40;
bulletRight.y = self.y - 50;
game.addChild(bulletLeft);
game.addChild(bulletRight);
bullets.push(bulletLeft);
bullets.push(bulletRight);
}
self.shootCooldown = self.powerupActive ? 10 : 15;
}
};
self.onDestroyed = function () {
var explosion = new Explosion(self.x, self.y, 2);
game.addChild(explosion);
LK.getSound('explosion').play();
// Show game over
LK.showGameOver();
};
self.update = function () {
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
if (self.powerupActive) {
self.powerupTimer--;
// Animate the powerup aura
if (self.powerupAura) {
self.powerupAura.rotation += 0.02;
// Pulse the aura
self.powerupAuraTime = (self.powerupAuraTime || 0) + 0.05;
self.powerupAura.alpha = 0.2 + Math.sin(self.powerupAuraTime) * 0.1;
self.powerupAura.scale.set(1.8 + Math.sin(self.powerupAuraTime) * 0.2);
}
// Warning animation when powerup is about to expire
if (self.powerupTimer < 60) {
// Flash the player when powerup is about to end
playerGraphics.alpha = 0.5 + Math.sin(self.powerupTimer * 0.2) * 0.5;
}
if (self.powerupTimer <= 0) {
self.powerupActive = false;
playerGraphics.alpha = 1;
// Remove powerup aura
if (self.powerupAura) {
self.powerupAura.destroy();
self.powerupAura = null;
}
// Reset powerup aura animation timer - Redundant as aura is destroyed
// self.powerupAuraTime = 0;
// The aura is already destroyed above, removing duplicate block.
}
}
};
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; // Move upwards
self.damage = 10;
self.update = function () {
self.y += self.speed;
// Check if bullet is off-screen
if (self.y < -50) {
self.removeFromGame();
}
};
self.removeFromGame = function () {
var index = bullets.indexOf(self);
if (index > -1) {
bullets.splice(index, 1);
self.destroy();
}
};
return self;
});
var Powerup = Container.expand(function () {
var self = Container.call(this);
var powerupGraphics = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.pulsateTimer = 0;
self.update = function () {
self.y += self.speed;
// Pulsate effect
self.pulsateTimer += 0.05;
var scale = 1 + Math.sin(self.pulsateTimer) * 0.2;
powerupGraphics.scaleX = scale;
powerupGraphics.scaleY = scale;
powerupGraphics.alpha = 0.7 + Math.sin(self.pulsateTimer) * 0.3; // Pulsate alpha
// Remove if off screen
if (self.y > 2732 + 50) {
self.removeFromGame();
}
};
self.removeFromGame = function () {
var index = powerups.indexOf(self);
if (index > -1) {
powerups.splice(index, 1);
self.destroy();
}
};
self.alpha = 0;
self.scale.set(0.5);
tween(self, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000011 // Deep space blue
});
/****
* Game Code
****/
// Game state variables
var player;
var bullets = [];
var enemies = [];
var enemyBullets = [];
var asteroids = [];
var powerups = [];
var bosses = [];
var level = storage.level || 1;
var enemySpawnTimer = 0;
var asteroidSpawnTimer = 0;
var bossSpawned = false;
var gameStarted = false;
var levelEnemiesRequired;
var enemiesDefeated = 0;
var dragNode = null;
// Set up UI
var scoreTxt = new Text2('Score: 0', {
size: 70,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(1, 0); // Anchor top-right
scoreTxt.x = -20; // Padding from right edge
scoreTxt.y = 20; // Padding from top edge
LK.gui.topRight.addChild(scoreTxt);
var highScoreTxt = new Text2('High Score: ' + storage.highScore, {
size: 60,
// Increased size
fill: 0xFFFFFF
});
highScoreTxt.anchor.set(1, 0); // Anchor top-right
highScoreTxt.x = -20; // Align with scoreTxt padding
highScoreTxt.y = scoreTxt.y + scoreTxt.height + 10; // Position below scoreTxt
LK.gui.topRight.addChild(highScoreTxt);
var levelTxt = new Text2('Level: ' + level, {
size: 70,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0, 0); // Anchor top-left now
LK.gui.topLeft.addChild(levelTxt);
levelTxt.x = 120; // Move slightly right from absolute left
levelTxt.y = 20; // Move slightly down from absolute top, matching score padding
// Add Glaud text
var glaudTxt = new Text2('Glaud', {
size: 40,
// Small size
fill: 0xFF8C00 // Orange color
});
glaudTxt.anchor.set(0, 0); // Anchor top-left
// Position below levelTxt
glaudTxt.x = levelTxt.x;
glaudTxt.y = levelTxt.y + levelTxt.height + 10; // 10px padding below
LK.gui.topLeft.addChild(glaudTxt);
var healthBar = LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0.5,
scaleX: 1,
scaleY: 0.6
});
// Center the health bar horizontally relative to the bottom-center anchor
healthBar.x = 0;
// Position the bar slightly up from the absolute bottom edge
healthBar.y = -50;
LK.gui.bottom.addChild(healthBar);
var healthTxt = new Text2('Health', {
size: 60,
fill: 0xFFFFFF
});
healthTxt.anchor.set(0.5, 1); // Anchor bottom-center
// Position text centered horizontally, just above the health bar
healthTxt.x = healthBar.x;
healthTxt.y = healthBar.y - healthBar.height * healthBar.scaleY / 2 - 10;
LK.gui.bottom.addChild(healthTxt);
// Progress bar for level completion
var progressBarBg = LK.getAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1,
scaleY: 0.4,
tint: 0x555555
});
progressBarBg.x = 2048 / 2;
progressBarBg.y = 100;
LK.gui.top.addChild(progressBarBg);
var progressBar = LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0.5,
scaleX: 0,
scaleY: 0.3,
tint: 0x00FFFF
});
progressBar.x = 2048 / 2 - progressBarBg.width / 2;
progressBar.y = 100;
LK.gui.top.addChild(progressBar);
var progressTxt = new Text2('Progress', {
size: 50,
// Increased size
fill: 0xFFFFFF
});
progressTxt.anchor.set(0.5, 1); // Anchor bottom-center
progressTxt.x = progressBarBg.x;
// Position text above the background bar
progressTxt.y = progressBarBg.y - progressBarBg.height * progressBarBg.scaleY / 2 - 15;
LK.gui.top.addChild(progressTxt);
// Initialize player
function initPlayer() {
player = new Player();
player.x = 2048 / 2;
player.y = 2732 + 100; // Start off-screen
game.addChild(player);
// Animate player entrance
tween(player, {
y: 2732 - 200,
alpha: 1
}, {
duration: 1000,
easing: tween.easeOutBack
});
}
// Initialize level
function initLevel() {
// Clear any existing enemies and bullets
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
}
for (var i = enemyBullets.length - 1; i >= 0; i--) {
enemyBullets[i].destroy();
}
for (var i = asteroids.length - 1; i >= 0; i--) {
asteroids[i].destroy();
}
for (var i = powerups.length - 1; i >= 0; i--) {
powerups[i].destroy();
}
for (var i = bosses.length - 1; i >= 0; i--) {
bosses[i].destroy();
}
enemies = [];
enemyBullets = [];
asteroids = [];
powerups = [];
bosses = [];
bossSpawned = false;
enemiesDefeated = 0;
// Set level difficulty
levelEnemiesRequired = level * 15;
enemySpawnTimer = 60;
asteroidSpawnTimer = 180;
// Update UI
levelTxt.setText('Level: ' + level);
// Stop pulsating effect on progress text if active
if (progressTxt.pulsating) {
tween.cancel(progressTxt.scale);
tween.cancel(progressTxt);
progressTxt.scale.set(1);
progressTxt.alpha = 1;
progressTxt.pulsating = false;
}
// Start music
LK.playMusic('bgMusic');
}
// Spawn enemy with random pattern
function spawnEnemy() {
var enemy = new Enemy();
// Random x position
enemy.x = Math.random() * (2048 - 200) + 100;
enemy.y = -100;
// Set health and pattern based on level
enemy.health = 20 + level * 5;
// Choose movement pattern
var patterns = ["straight", "zigzag", "sine"];
if (level >= 3) {
patterns.push("circle");
}
var pattern = patterns[Math.floor(Math.random() * patterns.length)];
enemy.setMovementPattern(pattern);
// Add spawn animation
enemy.alpha = 0;
enemy.scale.set(0.5);
game.addChild(enemy);
enemies.push(enemy);
// Animate entrance
tween(enemy, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeOutBack
});
}
// Spawn asteroid
function spawnAsteroid() {
var asteroid = new Asteroid();
// Random x position
asteroid.x = Math.random() * (2048 - 200) + 100;
asteroid.y = -100;
game.addChild(asteroid);
asteroids.push(asteroid);
// Add entry animation
asteroid.alpha = 0.5;
tween(asteroid, {
alpha: 1,
rotation: Math.random() * Math.PI * 2
}, {
duration: 800,
easing: tween.easeOut
});
}
// Spawn boss
function spawnBoss() {
var boss = new Boss();
boss.x = 2048 / 2;
boss.y = 300;
// Adjust boss health based on level
boss.health = 500 + level * 100;
boss.maxHealth = boss.health;
game.addChild(boss);
bosses.push(boss);
LK.getSound('explosion', {
volume: 0.8
}).play(); // Play boss spawn sound
// Activate boss after entrance animation
tween(boss, {
y: 300
}, {
duration: 2000,
easing: tween.easeOutBack,
onFinish: function onFinish() {
boss.activate();
}
});
}
// Check collisions
function checkCollisions() {
// Player bullets vs enemies
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
// Check against enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
// Create small impact effect
var impact = new Explosion(bullet.x, bullet.y, 0.3);
impact.duration = 15; // Shorter duration for impact
game.addChild(impact);
enemy.takeDamage(bullet.damage);
bullet.removeFromGame();
break;
}
}
// Check against asteroids
for (var j = asteroids.length - 1; j >= 0; j--) {
var asteroid = asteroids[j];
if (bullet.intersects(asteroid)) {
// Create small impact effect
var impact = new Explosion(bullet.x, bullet.y, 0.3);
impact.duration = 15; // Shorter duration for impact
game.addChild(impact);
asteroid.takeDamage(bullet.damage);
bullet.removeFromGame();
break;
}
}
// Check against bosses
for (var j = bosses.length - 1; j >= 0; j--) {
var boss = bosses[j];
if (bullet.intersects(boss) && boss.active) {
// Create small impact effect
var impact = new Explosion(bullet.x, bullet.y, 0.3);
impact.duration = 15; // Shorter duration for impact
game.addChild(impact);
boss.takeDamage(bullet.damage);
bullet.removeFromGame();
break;
}
}
}
// Enemy bullets vs player
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
if (bullet.intersects(player)) {
player.takeDamage(bullet.damage);
bullet.removeFromGame();
}
}
// Player vs enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (player.intersects(enemy)) {
player.takeDamage(20);
enemy.takeDamage(enemy.health); // Destroy enemy
}
}
// Player vs asteroids
for (var i = asteroids.length - 1; i >= 0; i--) {
var asteroid = asteroids[i];
if (player.intersects(asteroid)) {
player.takeDamage(30);
asteroid.takeDamage(asteroid.health); // Destroy asteroid
}
}
// Player vs powerups
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
if (player.intersects(powerup)) {
// Remove the powerup first to prevent potential multiple applications
powerup.removeFromGame();
// Then apply the effect
player.applyPowerup();
}
}
}
// Handle player movement
function handleDrag(x, y) {
if (dragNode) {
dragNode.x = x;
dragNode.y = y;
// Constrain player to screen bounds
if (dragNode.x < 50) {
dragNode.x = 50;
}
if (dragNode.x > 2048 - 50) {
dragNode.x = 2048 - 50;
}
if (dragNode.y < 50) {
dragNode.y = 50;
}
if (dragNode.y > 2732 - 50) {
dragNode.y = 2732 - 50;
}
}
}
// Event handlers
game.down = function (x, y, obj) {
// Only set drag if we're started
if (gameStarted) {
dragNode = player;
handleDrag(x, y);
} else {
// Start game on first touch
gameStarted = true;
initPlayer();
initLevel();
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
game.move = function (x, y, obj) {
handleDrag(x, y);
};
// Main game update loop
game.update = function () {
if (!gameStarted) {
// Show start screen text
if (!game.startText) {
game.startText = new Text2('Tap to Start\nSpace Defender', {
size: 150,
fill: 0xFFFFFF
});
game.startText.anchor.set(0.5, 0.5);
game.startText.x = 2048 / 2;
game.startText.y = 2732 / 2;
game.addChild(game.startText);
// Add background stars for start screen
game.stars = [];
for (var i = 0; i < 50; i++) {
var star = LK.getAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: Math.random() * 0.3 + 0.1,
scaleY: Math.random() * 0.3 + 0.1,
alpha: Math.random() * 0.7 + 0.3,
tint: 0xFFFFFF
});
star.x = Math.random() * 2048;
star.y = Math.random() * 2732;
star.speed = Math.random() * 2 + 1;
game.addChild(star);
game.stars.push(star);
}
// Add pulsating effect to start text
game.pulseValue = 0;
// Add pulsing to the start text using tween
tween(game.startText, {
alpha: 0.5
}, {
duration: 1000,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
}
// Animate stars in start screen
if (game.stars) {
// Manual scaling removed, handled by tween animation
// game.pulseValue += 0.05;
// game.startText.scale.set(1 + Math.sin(game.pulseValue) * 0.05);
for (var i = 0; i < game.stars.length; i++) {
var star = game.stars[i];
star.y += star.speed;
if (star.y > 2732) {
star.y = 0;
star.x = Math.random() * 2048;
}
}
}
return;
} else if (game.startText) {
game.startText.destroy();
game.startText = null;
// Clean up stars
if (game.stars) {
for (var i = 0; i < game.stars.length; i++) {
game.stars[i].destroy();
}
game.stars = null;
}
}
// Update UI
scoreTxt.setText('Score: ' + LK.getScore());
// Update high score if needed
if (LK.getScore() > storage.highScore) {
storage.highScore = LK.getScore();
highScoreTxt.setText('High Score: ' + storage.highScore);
}
// Update progress bar
if (!bossSpawned) {
var progress = enemiesDefeated / levelEnemiesRequired;
progressBar.scaleX = progress;
progressTxt.setText('Level Progress: ' + Math.floor(progress * 100) + '%');
} else {
progressTxt.setText('BOSS FIGHT!');
if (!progressTxt.pulsating) {
progressTxt.pulsating = true;
tween(progressTxt.scale, {
x: 1.2,
y: 1.2
}, {
duration: 300,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
tween(progressTxt, {
alpha: 0.5
}, {
duration: 300,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
}
progressBar.tint = 0xFF0000;
progressBar.scaleX = 1;
}
if (player) {
// Update health bar
healthBar.scaleX = Math.max(0, player.health / 100);
// Auto-fire
player.shoot();
}
// Spawn enemies
if (!bossSpawned) {
enemySpawnTimer--;
if (enemySpawnTimer <= 0) {
spawnEnemy();
// Spawn rate increases with level
enemySpawnTimer = Math.max(20, 100 - level * 10);
}
// Spawn asteroids
asteroidSpawnTimer--;
if (asteroidSpawnTimer <= 0) {
spawnAsteroid();
asteroidSpawnTimer = Math.max(60, 300 - level * 20);
}
// Check if we should spawn boss
if (enemiesDefeated >= levelEnemiesRequired) {
bossSpawned = true;
spawnBoss();
}
}
// Update all game objects
for (var i = bullets.length - 1; i >= 0; i--) {
bullets[i].update();
}
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].update();
}
for (var i = enemyBullets.length - 1; i >= 0; i--) {
enemyBullets[i].update();
}
for (var i = asteroids.length - 1; i >= 0; i--) {
asteroids[i].update();
}
for (var i = powerups.length - 1; i >= 0; i--) {
powerups[i].update();
}
for (var i = bosses.length - 1; i >= 0; i--) {
bosses[i].update();
}
// Check for collisions
checkCollisions();
};
// Start game music
LK.playMusic('bgMusic', {
fade: {
start: 0,
end: 0.3,
duration: 1000
}
});
asteroid. In-Game asset. 2d. High contrast. No shadows
spaceship enemy. In-Game asset. 2d. High contrast. No shadows
enemyBullet. In-Game asset. 2d. High contrast. No shadows
playerBullet. In-Game asset. 2d. High contrast. No shadows
explosion. In-Game asset. 2d. High contrast. No shadows
powerup. In-Game asset. 2d. High contrast. No shadows
boss spaceship. In-Game asset. 2d. High contrast. No shadows