User prompt
add view leaderboard a leaderboard and name creation ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
different warriors with different stats and different ship types neither of those have a change for what anything looks like there is a warrior selection battle and ship type selection menu ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
gun type selection menu back button
User prompt
tutorial gun types not seen but effects speed and damage and finally enemies can have varied health numbers and guns ↪💡 Consider importing and using the following plugins: @upit/storage.v1, @upit/tween.v1
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'toGlobal')' in or related to this line: 'var clickPos = self.toLocal(obj.parent.toGlobal(obj.position));' Line Number: 182
User prompt
levels, home screen , and story mode ↪💡 Consider importing and using the following plugins: @upit/storage.v1
Code edit (1 edits merged)
Please save this source code
User prompt
Space Defender
Initial prompt
shooting game
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { currentLevel: 1, unlockedLevels: 1, gameMode: "story", storyProgress: 0 }); /**** * Classes ****/ var Boss = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('boss', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 1; self.hp = 10; self.shootTimer = 0; self.shootInterval = 30; self.moveDirection = 1; self.update = function () { self.y += self.speed; self.x += self.moveDirection * 2; if (self.x <= 100 || self.x >= 1948) { self.moveDirection *= -1; } self.shootTimer++; if (self.shootTimer >= self.shootInterval) { self.shootTimer = 0; var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y + 60; enemyBullets.push(bullet); game.addChild(bullet); } }; return self; }); var Enemy = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 3; // Varied health based on current level and randomization var baseHp = Math.floor(storage.currentLevel / 2) + 1; self.hp = baseHp + (Math.random() < 0.3 ? Math.floor(Math.random() * 2) + 1 : 0); self.maxHp = self.hp; self.shootTimer = 0; self.shootInterval = 120; // Visual indicator for health - tint red for high HP enemies if (self.hp > 2) { graphics.tint = 0xFF6666; } else if (self.hp > 1) { graphics.tint = 0xFFAAAA; } self.update = function () { self.y += self.speed; self.shootTimer++; if (self.shootTimer >= self.shootInterval && Math.random() < 0.01) { self.shootTimer = 0; var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y + 30; enemyBullets.push(bullet); game.addChild(bullet); } // Update tint based on current health if (self.hp < self.maxHp) { var healthRatio = self.hp / self.maxHp; if (healthRatio < 0.3) { graphics.tint = 0xFF0000; } else if (healthRatio < 0.7) { graphics.tint = 0xFF6666; } } }; return self; }); var EnemyBullet = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('enemyBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.update = function () { self.y += self.speed; }; return self; }); var GunSelectionScreen = Container.expand(function () { var self = Container.call(this); var titleText = new Text2('SELECT GUN TYPE', { size: 100, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 300; self.addChild(titleText); // Gun type buttons var gunButtons = []; var gunTypeNames = ['BASIC', 'RAPID', 'HEAVY']; var gunTypeKeys = ['basic', 'rapid', 'heavy']; var gunColors = [0x00FF00, 0xFF8000, 0xFF0000]; for (var i = 0; i < gunTypeNames.length; i++) { var gunButton = new Text2(gunTypeNames[i], { size: 80, fill: gunColors[i] }); gunButton.anchor.set(0.5, 0.5); gunButton.x = 1024; gunButton.y = 600 + i * 200; gunButton.gunType = gunTypeKeys[i]; self.addChild(gunButton); gunButtons.push(gunButton); // Add gun stats text var gun = gunTypes[gunTypeKeys[i]]; var statsText = new Text2('Damage: ' + gun.damage + ' | Speed: ' + gun.speedMultiplier + 'x', { size: 50, fill: 0xCCCCCC }); statsText.anchor.set(0.5, 0.5); statsText.x = 1024; statsText.y = 650 + i * 200; self.addChild(statsText); } var backButton = new Text2('BACK', { size: 80, fill: 0xFF0000 }); backButton.anchor.set(0.5, 0.5); backButton.x = 1024; backButton.y = 1400; self.addChild(backButton); self.down = function (x, y, obj) { var clickPos = obj.parent ? self.toLocal(obj.parent.toGlobal(obj.position)) : { x: x, y: y }; // Back button clicked if (clickPos.y > 1350 && clickPos.y < 1450) { showMenu(); return; } // Check gun buttons for (var i = 0; i < gunButtons.length; i++) { var btn = gunButtons[i]; if (Math.abs(clickPos.x - btn.x) < 200 && Math.abs(clickPos.y - btn.y) < 50) { currentGunType = btn.gunType; showMenu(); break; } } }; return self; }); var LeaderboardScreen = Container.expand(function () { var self = Container.call(this); var titleText = new Text2('LEADERBOARD', { size: 100, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 300; self.addChild(titleText); // Get leaderboard data from storage var leaderboardData = storage.leaderboard || []; // Display top 10 scores for (var i = 0; i < Math.min(10, leaderboardData.length); i++) { var entry = leaderboardData[i]; var rankText = new Text2(i + 1 + '. ' + entry.name + ' - ' + entry.score, { size: 60, fill: i < 3 ? [0xFFD700, 0xC0C0C0, 0xCD7F32][i] : 0xFFFFFF }); rankText.anchor.set(0.5, 0.5); rankText.x = 1024; rankText.y = 500 + i * 80; self.addChild(rankText); } // Show message if no scores if (leaderboardData.length === 0) { var emptyText = new Text2('No scores recorded yet!', { size: 80, fill: 0x888888 }); emptyText.anchor.set(0.5, 0.5); emptyText.x = 1024; emptyText.y = 800; self.addChild(emptyText); } var backButton = new Text2('BACK', { size: 80, fill: 0xFF0000 }); backButton.anchor.set(0.5, 0.5); backButton.x = 1024; backButton.y = 2200; self.addChild(backButton); self.down = function (x, y, obj) { var clickPos = obj.parent ? self.toLocal(obj.parent.toGlobal(obj.position)) : { x: x, y: y }; // Back button clicked if (clickPos.y > 2150 && clickPos.y < 2250) { showMenu(); } }; return self; }); var LevelSelectScreen = Container.expand(function () { var self = Container.call(this); var titleText = new Text2('SELECT LEVEL', { size: 100, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 300; self.addChild(titleText); var levelButtons = []; for (var i = 1; i <= 10; i++) { var button = new Text2(i.toString(), { size: 60, fill: i <= storage.unlockedLevels ? 0xFFFFFF : 0x666666 }); button.anchor.set(0.5, 0.5); button.x = 400 + (i - 1) % 5 * 300; button.y = 600 + Math.floor((i - 1) / 5) * 200; button.levelNumber = i; self.addChild(button); levelButtons.push(button); } var backButton = new Text2('BACK', { size: 80, fill: 0xFF0000 }); backButton.anchor.set(0.5, 0.5); backButton.x = 1024; backButton.y = 1400; self.addChild(backButton); self.down = function (x, y, obj) { var clickPos = obj.parent ? self.toLocal(obj.parent.toGlobal(obj.position)) : { x: x, y: y }; if (clickPos.y > 1350 && clickPos.y < 1450) { // Back button clicked showMenu(); return; } // Check level buttons for (var i = 0; i < levelButtons.length; i++) { var btn = levelButtons[i]; if (Math.abs(clickPos.x - btn.x) < 100 && Math.abs(clickPos.y - btn.y) < 50) { if (btn.levelNumber <= storage.unlockedLevels) { storage.gameMode = 'level'; storage.currentLevel = btn.levelNumber; startGame(); } break; } } }; return self; }); var MenuScreen = Container.expand(function () { var self = Container.call(this); var titleText = new Text2('SPACE DEFENDER', { size: 120, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 400; self.addChild(titleText); var storyButton = new Text2('STORY MODE', { size: 80, fill: 0x00FF00 }); storyButton.anchor.set(0.5, 0.5); storyButton.x = 1024; storyButton.y = 800; self.addChild(storyButton); var levelsButton = new Text2('LEVEL SELECT', { size: 80, fill: 0x0080FF }); levelsButton.anchor.set(0.5, 0.5); levelsButton.x = 1024; levelsButton.y = 1000; self.addChild(levelsButton); var endlessButton = new Text2('ENDLESS MODE', { size: 80, fill: 0xFF8000 }); endlessButton.anchor.set(0.5, 0.5); endlessButton.x = 1024; endlessButton.y = 1200; self.addChild(endlessButton); var tutorialButton = new Text2('TUTORIAL', { size: 80, fill: 0xFFFF00 }); tutorialButton.anchor.set(0.5, 0.5); tutorialButton.x = 1024; tutorialButton.y = 1400; self.addChild(tutorialButton); var gunSelectButton = new Text2('GUN SELECTION', { size: 80, fill: 0xFFFFFF }); gunSelectButton.anchor.set(0.5, 0.5); gunSelectButton.x = 1024; gunSelectButton.y = 1600; self.addChild(gunSelectButton); var warriorSelectButton = new Text2('WARRIOR SELECTION', { size: 80, fill: 0xFFFF00 }); warriorSelectButton.anchor.set(0.5, 0.5); warriorSelectButton.x = 1024; warriorSelectButton.y = 1800; self.addChild(warriorSelectButton); var shipSelectButton = new Text2('SHIP SELECTION', { size: 80, fill: 0xFF00FF }); shipSelectButton.anchor.set(0.5, 0.5); shipSelectButton.x = 1024; shipSelectButton.y = 2000; self.addChild(shipSelectButton); var leaderboardButton = new Text2('VIEW LEADERBOARD', { size: 80, fill: 0xFFD700 }); leaderboardButton.anchor.set(0.5, 0.5); leaderboardButton.x = 1024; leaderboardButton.y = 2200; self.addChild(leaderboardButton); self.down = function (x, y, obj) { var clickPos = obj.parent ? self.toLocal(obj.parent.toGlobal(obj.position)) : { x: x, y: y }; if (clickPos.y > 750 && clickPos.y < 850) { // Story mode clicked storage.gameMode = 'story'; storage.currentLevel = storage.storyProgress + 1; startGame(); } else if (clickPos.y > 950 && clickPos.y < 1050) { // Level select clicked showLevelSelect(); } else if (clickPos.y > 1150 && clickPos.y < 1250) { // Endless mode clicked storage.gameMode = 'endless'; storage.currentLevel = 1; startGame(); } else if (clickPos.y > 1350 && clickPos.y < 1450) { // Tutorial clicked showTutorial(); } else if (clickPos.y > 1550 && clickPos.y < 1650) { // Gun selection clicked showGunSelection(); } else if (clickPos.y > 1750 && clickPos.y < 1850) { // Warrior selection clicked showWarriorSelection(); } else if (clickPos.y > 1950 && clickPos.y < 2050) { // Ship selection clicked showShipSelection(); } else if (clickPos.y > 2150 && clickPos.y < 2250) { // Leaderboard clicked showLeaderboard(); } }; return self; }); var NameEntryScreen = Container.expand(function (finalScore) { var self = Container.call(this); var titleText = new Text2('NEW HIGH SCORE!', { size: 100, fill: 0xFFD700 }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 400; self.addChild(titleText); var scoreText = new Text2('Score: ' + finalScore, { size: 80, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0.5); scoreText.x = 1024; scoreText.y = 550; self.addChild(scoreText); var promptText = new Text2('Enter your name:', { size: 70, fill: 0xFFFFFF }); promptText.anchor.set(0.5, 0.5); promptText.x = 1024; promptText.y = 700; self.addChild(promptText); // Pre-defined name options for touch interface var playerName = storage.playerName || 'PLAYER'; var nameOptions = ['ACE', 'HERO', 'STAR', 'NOVA', 'COMET', playerName]; var selectedName = playerName; var nameButtons = []; for (var i = 0; i < nameOptions.length; i++) { var nameButton = new Text2(nameOptions[i], { size: 60, fill: nameOptions[i] === selectedName ? 0x00FF00 : 0xFFFFFF }); nameButton.anchor.set(0.5, 0.5); nameButton.x = 400 + i % 3 * 400; nameButton.y = 900 + Math.floor(i / 3) * 120; nameButton.nameValue = nameOptions[i]; self.addChild(nameButton); nameButtons.push(nameButton); } var saveButton = new Text2('SAVE SCORE', { size: 80, fill: 0x00FF00 }); saveButton.anchor.set(0.5, 0.5); saveButton.x = 1024; saveButton.y = 1400; self.addChild(saveButton); self.down = function (x, y, obj) { var clickPos = obj.parent ? self.toLocal(obj.parent.toGlobal(obj.position)) : { x: x, y: y }; // Check name buttons for (var i = 0; i < nameButtons.length; i++) { var btn = nameButtons[i]; if (Math.abs(clickPos.x - btn.x) < 150 && Math.abs(clickPos.y - btn.y) < 40) { // Update selection selectedName = btn.nameValue; // Update button colors for (var j = 0; j < nameButtons.length; j++) { nameButtons[j].style.fill = nameButtons[j].nameValue === selectedName ? 0x00FF00 : 0xFFFFFF; } break; } } // Save button clicked if (clickPos.y > 1350 && clickPos.y < 1450) { // Save to leaderboard var leaderboardData = storage.leaderboard || []; leaderboardData.push({ name: selectedName, score: finalScore, level: storage.currentLevel, mode: storage.gameMode }); // Sort by score (highest first) leaderboardData.sort(function (a, b) { return b.score - a.score; }); // Keep only top 20 scores if (leaderboardData.length > 20) { leaderboardData = leaderboardData.slice(0, 20); } storage.leaderboard = leaderboardData; storage.playerName = selectedName; // Return to menu showMenu(); } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); // Apply warrior and ship stats var warrior = warriorTypes[currentWarriorType]; var ship = shipTypes[currentShipType]; self.hp = Math.floor(warrior.hp * ship.armor); self.maxHp = self.hp; self.speed = warrior.speed * ship.speed; self.shootTimer = 0; self.shootInterval = Math.floor(15 / ship.fireRate); self.powerUpTimer = 0; self.rapidFire = false; self.gunChangeTimer = 0; // Tint player based on ship type graphics.tint = ship.color; self.update = function () { self.shootTimer++; self.gunChangeTimer++; // Auto-cycle gun types every 5 seconds for tutorial effect if (self.gunChangeTimer >= 300) { self.gunChangeTimer = 0; var gunTypeKeys = Object.keys(gunTypes); var currentIndex = gunTypeKeys.indexOf(currentGunType); currentGunType = gunTypeKeys[(currentIndex + 1) % gunTypeKeys.length]; } var currentGun = gunTypes[currentGunType]; var warrior = warriorTypes[currentWarriorType]; var ship = shipTypes[currentShipType]; var currentShootInterval = self.rapidFire ? 5 : Math.floor(currentGun.interval / ship.fireRate); if (self.shootTimer >= currentShootInterval) { self.shootTimer = 0; var bullet = new PlayerBullet(); bullet.x = self.x; bullet.y = self.y - 40; bullet.damage = Math.floor(currentGun.damage * warrior.gunBonus); bullet.speed = -12 * currentGun.speedMultiplier; playerBullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); } if (self.rapidFire) { self.powerUpTimer--; if (self.powerUpTimer <= 0) { self.rapidFire = false; } } }; return self; }); var PlayerBullet = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('playerBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -12; self.damage = 1; self.update = function () { self.y += self.speed; }; return self; }); var PowerUp = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('powerUp', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 4; self.type = 'rapidFire'; // rapidFire, shield self.update = function () { self.y += self.speed; self.rotation += 0.1; }; return self; }); var ShipSelectionScreen = Container.expand(function () { var self = Container.call(this); var titleText = new Text2('SELECT SHIP TYPE', { size: 100, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 300; self.addChild(titleText); // Ship type buttons var shipButtons = []; var shipTypeNames = ['FIGHTER', 'INTERCEPTOR', 'CRUISER']; var shipTypeKeys = ['fighter', 'interceptor', 'cruiser']; for (var i = 0; i < shipTypeNames.length; i++) { var ship = shipTypes[shipTypeKeys[i]]; var shipButton = new Text2(shipTypeNames[i], { size: 80, fill: ship.color }); shipButton.anchor.set(0.5, 0.5); shipButton.x = 1024; shipButton.y = 600 + i * 200; shipButton.shipType = shipTypeKeys[i]; self.addChild(shipButton); shipButtons.push(shipButton); // Add ship stats text var statsText = new Text2('Speed: ' + ship.speed + 'x | Armor: ' + ship.armor + 'x | Fire: ' + ship.fireRate + 'x', { size: 50, fill: 0xCCCCCC }); statsText.anchor.set(0.5, 0.5); statsText.x = 1024; statsText.y = 650 + i * 200; self.addChild(statsText); } var backButton = new Text2('BACK', { size: 80, fill: 0xFF0000 }); backButton.anchor.set(0.5, 0.5); backButton.x = 1024; backButton.y = 1400; self.addChild(backButton); self.down = function (x, y, obj) { var clickPos = obj.parent ? self.toLocal(obj.parent.toGlobal(obj.position)) : { x: x, y: y }; // Back button clicked if (clickPos.y > 1350 && clickPos.y < 1450) { showMenu(); return; } // Check ship buttons for (var i = 0; i < shipButtons.length; i++) { var btn = shipButtons[i]; if (Math.abs(clickPos.x - btn.x) < 200 && Math.abs(clickPos.y - btn.y) < 50) { currentShipType = btn.shipType; showMenu(); break; } } }; return self; }); var StoryPanel = Container.expand(function (levelNumber) { var self = Container.call(this); var stories = ["The alien invasion has begun!\nDefend Earth's last outpost.", "Enemy forces are multiplying.\nPrepare for heavier resistance.", "Advanced alien fighters detected.\nUpgrade your weapons!", "The mothership approaches.\nThis is our final stand!", "Victory! Earth is safe.\nBut stay vigilant, commander."]; var storyText = new Text2(stories[Math.min(levelNumber - 1, stories.length - 1)] || "Mission briefing loading...", { size: 70, fill: 0xFFFFFF }); storyText.anchor.set(0.5, 0.5); storyText.x = 1024; storyText.y = 800; self.addChild(storyText); var continueText = new Text2('TAP TO CONTINUE', { size: 60, fill: 0x00FF00 }); continueText.anchor.set(0.5, 0.5); continueText.x = 1024; continueText.y = 1200; self.addChild(continueText); self.down = function (x, y, obj) { self.destroy(); initializeGameplay(); }; return self; }); var TutorialScreen = Container.expand(function () { var self = Container.call(this); var tutorialSteps = ["Welcome to Space Defender!\nTap and drag to move your ship.", "Your ship fires automatically.\nDestroy enemies to score points!", "Collect blue power-ups for rapid fire.\nAvoid enemy bullets!", "Different gun types affect your damage:\nBasic, Rapid, and Heavy modes.", "Enemies have different health levels.\nSome take multiple hits to destroy!", "Ready to begin your mission?\nTap to start the game!"]; var currentStep = 0; var stepText = new Text2(tutorialSteps[0], { size: 80, fill: 0xFFFFFF }); stepText.anchor.set(0.5, 0.5); stepText.x = 1024; stepText.y = 800; self.addChild(stepText); var nextText = new Text2('TAP TO CONTINUE', { size: 60, fill: 0x00FF00 }); nextText.anchor.set(0.5, 0.5); nextText.x = 1024; nextText.y = 1200; self.addChild(nextText); var skipText = new Text2('SKIP TUTORIAL', { size: 50, fill: 0xFF0000 }); skipText.anchor.set(0.5, 0.5); skipText.x = 1024; skipText.y = 1400; self.addChild(skipText); self.down = function (x, y, obj) { var clickPos = obj.parent ? self.toLocal(obj.parent.toGlobal(obj.position)) : { x: x, y: y }; // Skip tutorial if (clickPos.y > 1350 && clickPos.y < 1450) { self.destroy(); initializeGameplay(); return; } // Next step currentStep++; if (currentStep >= tutorialSteps.length) { self.destroy(); initializeGameplay(); } else { stepText.setText(tutorialSteps[currentStep]); } }; return self; }); var WarriorSelectionScreen = Container.expand(function () { var self = Container.call(this); var titleText = new Text2('SELECT WARRIOR', { size: 100, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 300; self.addChild(titleText); // Warrior type buttons var warriorButtons = []; var warriorTypeNames = ['ACE PILOT', 'VETERAN', 'COMMANDER']; var warriorTypeKeys = ['pilot', 'veteran', 'commander']; for (var i = 0; i < warriorTypeNames.length; i++) { var warrior = warriorTypes[warriorTypeKeys[i]]; var warriorButton = new Text2(warriorTypeNames[i], { size: 80, fill: warrior.color }); warriorButton.anchor.set(0.5, 0.5); warriorButton.x = 1024; warriorButton.y = 600 + i * 200; warriorButton.warriorType = warriorTypeKeys[i]; self.addChild(warriorButton); warriorButtons.push(warriorButton); // Add warrior stats text var statsText = new Text2('HP: ' + warrior.hp + ' | Speed: ' + warrior.speed + 'x | Gun: ' + warrior.gunBonus + 'x', { size: 50, fill: 0xCCCCCC }); statsText.anchor.set(0.5, 0.5); statsText.x = 1024; statsText.y = 650 + i * 200; self.addChild(statsText); } var backButton = new Text2('BACK', { size: 80, fill: 0xFF0000 }); backButton.anchor.set(0.5, 0.5); backButton.x = 1024; backButton.y = 1400; self.addChild(backButton); self.down = function (x, y, obj) { var clickPos = obj.parent ? self.toLocal(obj.parent.toGlobal(obj.position)) : { x: x, y: y }; // Back button clicked if (clickPos.y > 1350 && clickPos.y < 1450) { showMenu(); return; } // Check warrior buttons for (var i = 0; i < warriorButtons.length; i++) { var btn = warriorButtons[i]; if (Math.abs(clickPos.x - btn.x) < 200 && Math.abs(clickPos.y - btn.y) < 50) { currentWarriorType = btn.warriorType; showMenu(); break; } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000033 }); /**** * Game Code ****/ // Game state variables var gameState = 'menu'; // menu, levelSelect, story, playing, tutorial var menuScreen = null; var levelSelectScreen = null; var storyPanel = null; var tutorialScreen = null; var gunSelectionScreen = null; var warriorSelectionScreen = null; var shipSelectionScreen = null; var leaderboardScreen = null; var nameEntryScreen = null; // Gun types system var gunTypes = { basic: { damage: 1, speedMultiplier: 1.0, interval: 15 }, rapid: { damage: 1, speedMultiplier: 1.2, interval: 8 }, heavy: { damage: 3, speedMultiplier: 0.8, interval: 25 } }; var currentGunType = 'basic'; // Warrior types system var warriorTypes = { pilot: { name: 'ACE PILOT', hp: 3, speed: 1.0, gunBonus: 1.0, color: 0x00FF00 }, veteran: { name: 'VETERAN', hp: 5, speed: 0.8, gunBonus: 1.2, color: 0xFF8000 }, commander: { name: 'COMMANDER', hp: 4, speed: 1.2, gunBonus: 1.5, color: 0xFF0000 } }; var currentWarriorType = 'pilot'; // Ship types system var shipTypes = { fighter: { name: 'FIGHTER', speed: 1.2, armor: 1.0, fireRate: 1.0, color: 0x0080FF }, interceptor: { name: 'INTERCEPTOR', speed: 1.5, armor: 0.8, fireRate: 1.3, color: 0x00FFFF }, cruiser: { name: 'CRUISER', speed: 0.8, armor: 1.5, fireRate: 0.8, color: 0x8000FF } }; var currentShipType = 'fighter'; // Game variables var player; var playerBullets = []; var enemies = []; var enemyBullets = []; var powerUps = []; var bosses = []; var enemySpawnTimer = 0; var enemySpawnInterval = 60; var powerUpSpawnTimer = 0; var bossSpawnScore = 500; var nextBossScore = bossSpawnScore; var dragNode = null; var waveLevel = 1; var levelTargetScore = 0; var levelCompleted = false; // UI Elements var scoreTxt = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreTxt.anchor.set(0, 0); scoreTxt.x = 120; scoreTxt.y = 50; var hpTxt = new Text2('HP: 3', { size: 60, fill: 0xFF0000 }); hpTxt.anchor.set(1, 0); hpTxt.x = -50; hpTxt.y = 50; var levelTxt = new Text2('Level: 1', { size: 60, fill: 0x00FF00 }); levelTxt.anchor.set(0.5, 0); levelTxt.y = 50; function showMenu() { gameState = 'menu'; clearGame(); menuScreen = new MenuScreen(); game.addChild(menuScreen); } function showLevelSelect() { gameState = 'levelSelect'; if (menuScreen) { menuScreen.destroy(); menuScreen = null; } levelSelectScreen = new LevelSelectScreen(); game.addChild(levelSelectScreen); } function showTutorial() { gameState = 'tutorial'; if (menuScreen) { menuScreen.destroy(); menuScreen = null; } storage.gameMode = 'tutorial'; storage.currentLevel = 1; tutorialScreen = new TutorialScreen(); game.addChild(tutorialScreen); } function showGunSelection() { gameState = 'gunSelection'; if (menuScreen) { menuScreen.destroy(); menuScreen = null; } gunSelectionScreen = new GunSelectionScreen(); game.addChild(gunSelectionScreen); } function showWarriorSelection() { gameState = 'warriorSelection'; if (menuScreen) { menuScreen.destroy(); menuScreen = null; } warriorSelectionScreen = new WarriorSelectionScreen(); game.addChild(warriorSelectionScreen); } function showShipSelection() { gameState = 'shipSelection'; if (menuScreen) { menuScreen.destroy(); menuScreen = null; } shipSelectionScreen = new ShipSelectionScreen(); game.addChild(shipSelectionScreen); } function showLeaderboard() { gameState = 'leaderboard'; if (menuScreen) { menuScreen.destroy(); menuScreen = null; } leaderboardScreen = new LeaderboardScreen(); game.addChild(leaderboardScreen); } function showNameEntry(finalScore) { gameState = 'nameEntry'; nameEntryScreen = new NameEntryScreen(finalScore); game.addChild(nameEntryScreen); } function checkHighScore(score) { var leaderboardData = storage.leaderboard || []; // Check if this score qualifies for leaderboard (top 20 or empty leaderboard) if (leaderboardData.length < 20) { return true; } // Sort to find lowest score in leaderboard leaderboardData.sort(function (a, b) { return b.score - a.score; }); return score > leaderboardData[leaderboardData.length - 1].score; } function startGame() { gameState = storage.gameMode === 'story' ? 'story' : 'playing'; if (levelSelectScreen) { levelSelectScreen.destroy(); levelSelectScreen = null; } if (menuScreen) { menuScreen.destroy(); menuScreen = null; } if (storage.gameMode === 'story' && storage.currentLevel <= 5) { storyPanel = new StoryPanel(storage.currentLevel); game.addChild(storyPanel); } else { initializeGameplay(); } } function initializeGameplay() { gameState = 'playing'; // Clear any story panel if (storyPanel) { storyPanel.destroy(); storyPanel = null; } // Reset game variables clearGame(); LK.setScore(0); // Set level parameters setupLevel(storage.currentLevel); // Initialize player player = new Player(); player.x = 1024; player.y = 2400; game.addChild(player); // Add UI elements LK.gui.topLeft.addChild(scoreTxt); LK.gui.topRight.addChild(hpTxt); LK.gui.top.addChild(levelTxt); levelTxt.setText('Level: ' + storage.currentLevel); } function setupLevel(level) { // Set level-specific parameters if (storage.gameMode === 'endless') { levelTargetScore = 0; // No target for endless mode enemySpawnInterval = 60; } else { levelTargetScore = level * 200; // Score needed to complete level enemySpawnInterval = Math.max(20, 80 - level * 5); bossSpawnScore = levelTargetScore * 0.8; nextBossScore = bossSpawnScore; } levelCompleted = false; } function clearGame() { // Clear all game objects for (var i = 0; i < enemies.length; i++) { enemies[i].destroy(); } for (var i = 0; i < enemyBullets.length; i++) { enemyBullets[i].destroy(); } for (var i = 0; i < playerBullets.length; i++) { playerBullets[i].destroy(); } for (var i = 0; i < powerUps.length; i++) { powerUps[i].destroy(); } for (var i = 0; i < bosses.length; i++) { bosses[i].destroy(); } if (player) { player.destroy(); player = null; } // Clear arrays enemies = []; enemyBullets = []; playerBullets = []; powerUps = []; bosses = []; // Remove UI elements if (scoreTxt.parent) scoreTxt.parent.removeChild(scoreTxt); if (hpTxt.parent) hpTxt.parent.removeChild(hpTxt); if (levelTxt.parent) levelTxt.parent.removeChild(levelTxt); // Clean up gun selection screen if (gunSelectionScreen) { gunSelectionScreen.destroy(); gunSelectionScreen = null; } // Clean up warrior selection screen if (warriorSelectionScreen) { warriorSelectionScreen.destroy(); warriorSelectionScreen = null; } // Clean up ship selection screen if (shipSelectionScreen) { shipSelectionScreen.destroy(); shipSelectionScreen = null; } // Clean up leaderboard screen if (leaderboardScreen) { leaderboardScreen.destroy(); leaderboardScreen = null; } // Clean up name entry screen if (nameEntryScreen) { nameEntryScreen.destroy(); nameEntryScreen = null; } } function checkLevelComplete() { if (storage.gameMode !== 'endless' && !levelCompleted && LK.getScore() >= levelTargetScore) { levelCompleted = true; // Update progress if (storage.gameMode === 'story') { storage.storyProgress = Math.max(storage.storyProgress, storage.currentLevel); } storage.unlockedLevels = Math.max(storage.unlockedLevels, storage.currentLevel + 1); // Show level complete message var completeText = new Text2('LEVEL COMPLETE!', { size: 100, fill: 0x00FF00 }); completeText.anchor.set(0.5, 0.5); completeText.x = 1024; completeText.y = 1366; game.addChild(completeText); // Auto-advance after 2 seconds LK.setTimeout(function () { if (storage.gameMode === 'story' && storage.currentLevel < 5) { storage.currentLevel++; startGame(); } else { showMenu(); } }, 2000); } } // Initialize menu on start showMenu(); // Event handlers function handleMove(x, y, obj) { if (dragNode && player) { var moveSpeed = player.speed || 1.0; var targetX = Math.max(40, Math.min(2008, x)); var targetY = Math.max(100, Math.min(2632, y)); // Apply speed multiplier to movement var deltaX = (targetX - dragNode.x) * moveSpeed; var deltaY = (targetY - dragNode.y) * moveSpeed; dragNode.x += deltaX * 0.1; // Smooth movement dragNode.y += deltaY * 0.1; } } game.move = handleMove; game.down = function (x, y, obj) { dragNode = player; handleMove(x, y, obj); }; game.up = function (x, y, obj) { dragNode = null; }; // Main game loop game.update = function () { if (gameState !== 'playing') return; // Update score display scoreTxt.setText('Score: ' + LK.getScore()); hpTxt.setText('HP: ' + player.hp); // Check level completion checkLevelComplete(); // Spawn enemies enemySpawnTimer++; var currentSpawnInterval = Math.max(20, enemySpawnInterval - Math.floor(LK.getScore() / 100) * 5); if (enemySpawnTimer >= currentSpawnInterval) { enemySpawnTimer = 0; var enemy = new Enemy(); enemy.x = Math.random() * 1800 + 124; enemy.y = -50; enemy.speed = 2 + Math.floor(LK.getScore() / 200) + (storage.currentLevel - 1); enemies.push(enemy); game.addChild(enemy); } // Spawn power-ups occasionally powerUpSpawnTimer++; if (powerUpSpawnTimer >= 900 && Math.random() < 0.02) { powerUpSpawnTimer = 0; var powerUp = new PowerUp(); powerUp.x = Math.random() * 1800 + 124; powerUp.y = -50; powerUps.push(powerUp); game.addChild(powerUp); } // Spawn boss at score milestones if (LK.getScore() >= nextBossScore && bosses.length === 0) { var boss = new Boss(); boss.x = 1024; boss.y = -100; bosses.push(boss); game.addChild(boss); nextBossScore += bossSpawnScore; } // Handle player bullets for (var i = playerBullets.length - 1; i >= 0; i--) { var bullet = playerBullets[i]; if (bullet.lastY === undefined) bullet.lastY = bullet.y; // Remove bullets that go off screen if (bullet.lastY >= -50 && bullet.y < -50) { bullet.destroy(); playerBullets.splice(i, 1); continue; } // Check collision with enemies var hitEnemy = false; for (var j = enemies.length - 1; j >= 0; j--) { if (bullet.intersects(enemies[j])) { enemies[j].hp -= bullet.damage || 1; // Visual feedback for damage tween(enemies[j], { tint: 0xFFFFFF }, { duration: 100, onFinish: function onFinish() { if (enemies[j] && enemies[j].hp > 2) { enemies[j].children[0].tint = 0xFF6666; } else if (enemies[j] && enemies[j].hp > 1) { enemies[j].children[0].tint = 0xFFAAAA; } } }); if (enemies[j].hp <= 0) { LK.setScore(LK.getScore() + 10 * enemies[j].maxHp); LK.getSound('enemyHit').play(); enemies[j].destroy(); enemies.splice(j, 1); } bullet.destroy(); playerBullets.splice(i, 1); hitEnemy = true; break; } } if (hitEnemy) continue; // Check collision with bosses for (var k = bosses.length - 1; k >= 0; k--) { if (bullet.intersects(bosses[k])) { bosses[k].hp -= bullet.damage || 1; // Visual feedback for boss damage tween(bosses[k], { tint: 0xFFFFFF }, { duration: 150 }); if (bosses[k].hp <= 0) { LK.setScore(LK.getScore() + 100); LK.getSound('enemyHit').play(); bosses[k].destroy(); bosses.splice(k, 1); } bullet.destroy(); playerBullets.splice(i, 1); break; } } bullet.lastY = bullet.y; } // Handle enemy bullets for (var i = enemyBullets.length - 1; i >= 0; i--) { var bullet = enemyBullets[i]; if (bullet.lastY === undefined) bullet.lastY = bullet.y; // Remove bullets that go off screen if (bullet.lastY <= 2782 && bullet.y > 2782) { bullet.destroy(); enemyBullets.splice(i, 1); continue; } // Check collision with player if (bullet.intersects(player)) { player.hp--; LK.getSound('playerHit').play(); LK.effects.flashObject(player, 0xff0000, 500); if (player.hp <= 0) { var finalScore = LK.getScore(); LK.showGameOver(); LK.setTimeout(function () { if (checkHighScore(finalScore)) { showNameEntry(finalScore); } else { showMenu(); } }, 2000); return; } bullet.destroy(); enemyBullets.splice(i, 1); } bullet.lastY = bullet.y; } // Handle enemies for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; if (enemy.lastY === undefined) enemy.lastY = enemy.y; // Remove enemies that go off screen if (enemy.lastY <= 2782 && enemy.y > 2782) { enemy.destroy(); enemies.splice(i, 1); continue; } // Check collision with player if (enemy.intersects(player)) { player.hp--; LK.getSound('playerHit').play(); LK.effects.flashScreen(0xff0000, 1000); if (player.hp <= 0) { var finalScore = LK.getScore(); LK.showGameOver(); LK.setTimeout(function () { if (checkHighScore(finalScore)) { showNameEntry(finalScore); } else { showMenu(); } }, 2000); return; } enemy.destroy(); enemies.splice(i, 1); } enemy.lastY = enemy.y; } // Handle bosses for (var i = bosses.length - 1; i >= 0; i--) { var boss = bosses[i]; if (boss.lastY === undefined) boss.lastY = boss.y; // Remove bosses that go off screen if (boss.lastY <= 2782 && boss.y > 2782) { boss.destroy(); bosses.splice(i, 1); continue; } // Check collision with player if (boss.intersects(player)) { player.hp--; LK.getSound('playerHit').play(); LK.effects.flashScreen(0xff0000, 1000); if (player.hp <= 0) { var finalScore = LK.getScore(); LK.showGameOver(); LK.setTimeout(function () { if (checkHighScore(finalScore)) { showNameEntry(finalScore); } else { showMenu(); } }, 2000); return; } } boss.lastY = boss.y; } // Handle power-ups for (var i = powerUps.length - 1; i >= 0; i--) { var powerUp = powerUps[i]; if (powerUp.lastY === undefined) powerUp.lastY = powerUp.y; // Remove power-ups that go off screen if (powerUp.lastY <= 2782 && powerUp.y > 2782) { powerUp.destroy(); powerUps.splice(i, 1); continue; } // Check collision with player if (powerUp.intersects(player)) { LK.getSound('powerUpSound').play(); if (powerUp.type === 'rapidFire') { player.rapidFire = true; player.powerUpTimer = 600; // 10 seconds at 60fps } powerUp.destroy(); powerUps.splice(i, 1); } powerUp.lastY = powerUp.y; } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
currentLevel: 1,
unlockedLevels: 1,
gameMode: "story",
storyProgress: 0
});
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1;
self.hp = 10;
self.shootTimer = 0;
self.shootInterval = 30;
self.moveDirection = 1;
self.update = function () {
self.y += self.speed;
self.x += self.moveDirection * 2;
if (self.x <= 100 || self.x >= 1948) {
self.moveDirection *= -1;
}
self.shootTimer++;
if (self.shootTimer >= self.shootInterval) {
self.shootTimer = 0;
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + 60;
enemyBullets.push(bullet);
game.addChild(bullet);
}
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
// Varied health based on current level and randomization
var baseHp = Math.floor(storage.currentLevel / 2) + 1;
self.hp = baseHp + (Math.random() < 0.3 ? Math.floor(Math.random() * 2) + 1 : 0);
self.maxHp = self.hp;
self.shootTimer = 0;
self.shootInterval = 120;
// Visual indicator for health - tint red for high HP enemies
if (self.hp > 2) {
graphics.tint = 0xFF6666;
} else if (self.hp > 1) {
graphics.tint = 0xFFAAAA;
}
self.update = function () {
self.y += self.speed;
self.shootTimer++;
if (self.shootTimer >= self.shootInterval && Math.random() < 0.01) {
self.shootTimer = 0;
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + 30;
enemyBullets.push(bullet);
game.addChild(bullet);
}
// Update tint based on current health
if (self.hp < self.maxHp) {
var healthRatio = self.hp / self.maxHp;
if (healthRatio < 0.3) {
graphics.tint = 0xFF0000;
} else if (healthRatio < 0.7) {
graphics.tint = 0xFF6666;
}
}
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
};
return self;
});
var GunSelectionScreen = Container.expand(function () {
var self = Container.call(this);
var titleText = new Text2('SELECT GUN TYPE', {
size: 100,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 300;
self.addChild(titleText);
// Gun type buttons
var gunButtons = [];
var gunTypeNames = ['BASIC', 'RAPID', 'HEAVY'];
var gunTypeKeys = ['basic', 'rapid', 'heavy'];
var gunColors = [0x00FF00, 0xFF8000, 0xFF0000];
for (var i = 0; i < gunTypeNames.length; i++) {
var gunButton = new Text2(gunTypeNames[i], {
size: 80,
fill: gunColors[i]
});
gunButton.anchor.set(0.5, 0.5);
gunButton.x = 1024;
gunButton.y = 600 + i * 200;
gunButton.gunType = gunTypeKeys[i];
self.addChild(gunButton);
gunButtons.push(gunButton);
// Add gun stats text
var gun = gunTypes[gunTypeKeys[i]];
var statsText = new Text2('Damage: ' + gun.damage + ' | Speed: ' + gun.speedMultiplier + 'x', {
size: 50,
fill: 0xCCCCCC
});
statsText.anchor.set(0.5, 0.5);
statsText.x = 1024;
statsText.y = 650 + i * 200;
self.addChild(statsText);
}
var backButton = new Text2('BACK', {
size: 80,
fill: 0xFF0000
});
backButton.anchor.set(0.5, 0.5);
backButton.x = 1024;
backButton.y = 1400;
self.addChild(backButton);
self.down = function (x, y, obj) {
var clickPos = obj.parent ? self.toLocal(obj.parent.toGlobal(obj.position)) : {
x: x,
y: y
};
// Back button clicked
if (clickPos.y > 1350 && clickPos.y < 1450) {
showMenu();
return;
}
// Check gun buttons
for (var i = 0; i < gunButtons.length; i++) {
var btn = gunButtons[i];
if (Math.abs(clickPos.x - btn.x) < 200 && Math.abs(clickPos.y - btn.y) < 50) {
currentGunType = btn.gunType;
showMenu();
break;
}
}
};
return self;
});
var LeaderboardScreen = Container.expand(function () {
var self = Container.call(this);
var titleText = new Text2('LEADERBOARD', {
size: 100,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 300;
self.addChild(titleText);
// Get leaderboard data from storage
var leaderboardData = storage.leaderboard || [];
// Display top 10 scores
for (var i = 0; i < Math.min(10, leaderboardData.length); i++) {
var entry = leaderboardData[i];
var rankText = new Text2(i + 1 + '. ' + entry.name + ' - ' + entry.score, {
size: 60,
fill: i < 3 ? [0xFFD700, 0xC0C0C0, 0xCD7F32][i] : 0xFFFFFF
});
rankText.anchor.set(0.5, 0.5);
rankText.x = 1024;
rankText.y = 500 + i * 80;
self.addChild(rankText);
}
// Show message if no scores
if (leaderboardData.length === 0) {
var emptyText = new Text2('No scores recorded yet!', {
size: 80,
fill: 0x888888
});
emptyText.anchor.set(0.5, 0.5);
emptyText.x = 1024;
emptyText.y = 800;
self.addChild(emptyText);
}
var backButton = new Text2('BACK', {
size: 80,
fill: 0xFF0000
});
backButton.anchor.set(0.5, 0.5);
backButton.x = 1024;
backButton.y = 2200;
self.addChild(backButton);
self.down = function (x, y, obj) {
var clickPos = obj.parent ? self.toLocal(obj.parent.toGlobal(obj.position)) : {
x: x,
y: y
};
// Back button clicked
if (clickPos.y > 2150 && clickPos.y < 2250) {
showMenu();
}
};
return self;
});
var LevelSelectScreen = Container.expand(function () {
var self = Container.call(this);
var titleText = new Text2('SELECT LEVEL', {
size: 100,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 300;
self.addChild(titleText);
var levelButtons = [];
for (var i = 1; i <= 10; i++) {
var button = new Text2(i.toString(), {
size: 60,
fill: i <= storage.unlockedLevels ? 0xFFFFFF : 0x666666
});
button.anchor.set(0.5, 0.5);
button.x = 400 + (i - 1) % 5 * 300;
button.y = 600 + Math.floor((i - 1) / 5) * 200;
button.levelNumber = i;
self.addChild(button);
levelButtons.push(button);
}
var backButton = new Text2('BACK', {
size: 80,
fill: 0xFF0000
});
backButton.anchor.set(0.5, 0.5);
backButton.x = 1024;
backButton.y = 1400;
self.addChild(backButton);
self.down = function (x, y, obj) {
var clickPos = obj.parent ? self.toLocal(obj.parent.toGlobal(obj.position)) : {
x: x,
y: y
};
if (clickPos.y > 1350 && clickPos.y < 1450) {
// Back button clicked
showMenu();
return;
}
// Check level buttons
for (var i = 0; i < levelButtons.length; i++) {
var btn = levelButtons[i];
if (Math.abs(clickPos.x - btn.x) < 100 && Math.abs(clickPos.y - btn.y) < 50) {
if (btn.levelNumber <= storage.unlockedLevels) {
storage.gameMode = 'level';
storage.currentLevel = btn.levelNumber;
startGame();
}
break;
}
}
};
return self;
});
var MenuScreen = Container.expand(function () {
var self = Container.call(this);
var titleText = new Text2('SPACE DEFENDER', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
self.addChild(titleText);
var storyButton = new Text2('STORY MODE', {
size: 80,
fill: 0x00FF00
});
storyButton.anchor.set(0.5, 0.5);
storyButton.x = 1024;
storyButton.y = 800;
self.addChild(storyButton);
var levelsButton = new Text2('LEVEL SELECT', {
size: 80,
fill: 0x0080FF
});
levelsButton.anchor.set(0.5, 0.5);
levelsButton.x = 1024;
levelsButton.y = 1000;
self.addChild(levelsButton);
var endlessButton = new Text2('ENDLESS MODE', {
size: 80,
fill: 0xFF8000
});
endlessButton.anchor.set(0.5, 0.5);
endlessButton.x = 1024;
endlessButton.y = 1200;
self.addChild(endlessButton);
var tutorialButton = new Text2('TUTORIAL', {
size: 80,
fill: 0xFFFF00
});
tutorialButton.anchor.set(0.5, 0.5);
tutorialButton.x = 1024;
tutorialButton.y = 1400;
self.addChild(tutorialButton);
var gunSelectButton = new Text2('GUN SELECTION', {
size: 80,
fill: 0xFFFFFF
});
gunSelectButton.anchor.set(0.5, 0.5);
gunSelectButton.x = 1024;
gunSelectButton.y = 1600;
self.addChild(gunSelectButton);
var warriorSelectButton = new Text2('WARRIOR SELECTION', {
size: 80,
fill: 0xFFFF00
});
warriorSelectButton.anchor.set(0.5, 0.5);
warriorSelectButton.x = 1024;
warriorSelectButton.y = 1800;
self.addChild(warriorSelectButton);
var shipSelectButton = new Text2('SHIP SELECTION', {
size: 80,
fill: 0xFF00FF
});
shipSelectButton.anchor.set(0.5, 0.5);
shipSelectButton.x = 1024;
shipSelectButton.y = 2000;
self.addChild(shipSelectButton);
var leaderboardButton = new Text2('VIEW LEADERBOARD', {
size: 80,
fill: 0xFFD700
});
leaderboardButton.anchor.set(0.5, 0.5);
leaderboardButton.x = 1024;
leaderboardButton.y = 2200;
self.addChild(leaderboardButton);
self.down = function (x, y, obj) {
var clickPos = obj.parent ? self.toLocal(obj.parent.toGlobal(obj.position)) : {
x: x,
y: y
};
if (clickPos.y > 750 && clickPos.y < 850) {
// Story mode clicked
storage.gameMode = 'story';
storage.currentLevel = storage.storyProgress + 1;
startGame();
} else if (clickPos.y > 950 && clickPos.y < 1050) {
// Level select clicked
showLevelSelect();
} else if (clickPos.y > 1150 && clickPos.y < 1250) {
// Endless mode clicked
storage.gameMode = 'endless';
storage.currentLevel = 1;
startGame();
} else if (clickPos.y > 1350 && clickPos.y < 1450) {
// Tutorial clicked
showTutorial();
} else if (clickPos.y > 1550 && clickPos.y < 1650) {
// Gun selection clicked
showGunSelection();
} else if (clickPos.y > 1750 && clickPos.y < 1850) {
// Warrior selection clicked
showWarriorSelection();
} else if (clickPos.y > 1950 && clickPos.y < 2050) {
// Ship selection clicked
showShipSelection();
} else if (clickPos.y > 2150 && clickPos.y < 2250) {
// Leaderboard clicked
showLeaderboard();
}
};
return self;
});
var NameEntryScreen = Container.expand(function (finalScore) {
var self = Container.call(this);
var titleText = new Text2('NEW HIGH SCORE!', {
size: 100,
fill: 0xFFD700
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
self.addChild(titleText);
var scoreText = new Text2('Score: ' + finalScore, {
size: 80,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0.5);
scoreText.x = 1024;
scoreText.y = 550;
self.addChild(scoreText);
var promptText = new Text2('Enter your name:', {
size: 70,
fill: 0xFFFFFF
});
promptText.anchor.set(0.5, 0.5);
promptText.x = 1024;
promptText.y = 700;
self.addChild(promptText);
// Pre-defined name options for touch interface
var playerName = storage.playerName || 'PLAYER';
var nameOptions = ['ACE', 'HERO', 'STAR', 'NOVA', 'COMET', playerName];
var selectedName = playerName;
var nameButtons = [];
for (var i = 0; i < nameOptions.length; i++) {
var nameButton = new Text2(nameOptions[i], {
size: 60,
fill: nameOptions[i] === selectedName ? 0x00FF00 : 0xFFFFFF
});
nameButton.anchor.set(0.5, 0.5);
nameButton.x = 400 + i % 3 * 400;
nameButton.y = 900 + Math.floor(i / 3) * 120;
nameButton.nameValue = nameOptions[i];
self.addChild(nameButton);
nameButtons.push(nameButton);
}
var saveButton = new Text2('SAVE SCORE', {
size: 80,
fill: 0x00FF00
});
saveButton.anchor.set(0.5, 0.5);
saveButton.x = 1024;
saveButton.y = 1400;
self.addChild(saveButton);
self.down = function (x, y, obj) {
var clickPos = obj.parent ? self.toLocal(obj.parent.toGlobal(obj.position)) : {
x: x,
y: y
};
// Check name buttons
for (var i = 0; i < nameButtons.length; i++) {
var btn = nameButtons[i];
if (Math.abs(clickPos.x - btn.x) < 150 && Math.abs(clickPos.y - btn.y) < 40) {
// Update selection
selectedName = btn.nameValue;
// Update button colors
for (var j = 0; j < nameButtons.length; j++) {
nameButtons[j].style.fill = nameButtons[j].nameValue === selectedName ? 0x00FF00 : 0xFFFFFF;
}
break;
}
}
// Save button clicked
if (clickPos.y > 1350 && clickPos.y < 1450) {
// Save to leaderboard
var leaderboardData = storage.leaderboard || [];
leaderboardData.push({
name: selectedName,
score: finalScore,
level: storage.currentLevel,
mode: storage.gameMode
});
// Sort by score (highest first)
leaderboardData.sort(function (a, b) {
return b.score - a.score;
});
// Keep only top 20 scores
if (leaderboardData.length > 20) {
leaderboardData = leaderboardData.slice(0, 20);
}
storage.leaderboard = leaderboardData;
storage.playerName = selectedName;
// Return to menu
showMenu();
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Apply warrior and ship stats
var warrior = warriorTypes[currentWarriorType];
var ship = shipTypes[currentShipType];
self.hp = Math.floor(warrior.hp * ship.armor);
self.maxHp = self.hp;
self.speed = warrior.speed * ship.speed;
self.shootTimer = 0;
self.shootInterval = Math.floor(15 / ship.fireRate);
self.powerUpTimer = 0;
self.rapidFire = false;
self.gunChangeTimer = 0;
// Tint player based on ship type
graphics.tint = ship.color;
self.update = function () {
self.shootTimer++;
self.gunChangeTimer++;
// Auto-cycle gun types every 5 seconds for tutorial effect
if (self.gunChangeTimer >= 300) {
self.gunChangeTimer = 0;
var gunTypeKeys = Object.keys(gunTypes);
var currentIndex = gunTypeKeys.indexOf(currentGunType);
currentGunType = gunTypeKeys[(currentIndex + 1) % gunTypeKeys.length];
}
var currentGun = gunTypes[currentGunType];
var warrior = warriorTypes[currentWarriorType];
var ship = shipTypes[currentShipType];
var currentShootInterval = self.rapidFire ? 5 : Math.floor(currentGun.interval / ship.fireRate);
if (self.shootTimer >= currentShootInterval) {
self.shootTimer = 0;
var bullet = new PlayerBullet();
bullet.x = self.x;
bullet.y = self.y - 40;
bullet.damage = Math.floor(currentGun.damage * warrior.gunBonus);
bullet.speed = -12 * currentGun.speedMultiplier;
playerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
}
if (self.rapidFire) {
self.powerUpTimer--;
if (self.powerUpTimer <= 0) {
self.rapidFire = false;
}
}
};
return self;
});
var PlayerBullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -12;
self.damage = 1;
self.update = function () {
self.y += self.speed;
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('powerUp', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4;
self.type = 'rapidFire'; // rapidFire, shield
self.update = function () {
self.y += self.speed;
self.rotation += 0.1;
};
return self;
});
var ShipSelectionScreen = Container.expand(function () {
var self = Container.call(this);
var titleText = new Text2('SELECT SHIP TYPE', {
size: 100,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 300;
self.addChild(titleText);
// Ship type buttons
var shipButtons = [];
var shipTypeNames = ['FIGHTER', 'INTERCEPTOR', 'CRUISER'];
var shipTypeKeys = ['fighter', 'interceptor', 'cruiser'];
for (var i = 0; i < shipTypeNames.length; i++) {
var ship = shipTypes[shipTypeKeys[i]];
var shipButton = new Text2(shipTypeNames[i], {
size: 80,
fill: ship.color
});
shipButton.anchor.set(0.5, 0.5);
shipButton.x = 1024;
shipButton.y = 600 + i * 200;
shipButton.shipType = shipTypeKeys[i];
self.addChild(shipButton);
shipButtons.push(shipButton);
// Add ship stats text
var statsText = new Text2('Speed: ' + ship.speed + 'x | Armor: ' + ship.armor + 'x | Fire: ' + ship.fireRate + 'x', {
size: 50,
fill: 0xCCCCCC
});
statsText.anchor.set(0.5, 0.5);
statsText.x = 1024;
statsText.y = 650 + i * 200;
self.addChild(statsText);
}
var backButton = new Text2('BACK', {
size: 80,
fill: 0xFF0000
});
backButton.anchor.set(0.5, 0.5);
backButton.x = 1024;
backButton.y = 1400;
self.addChild(backButton);
self.down = function (x, y, obj) {
var clickPos = obj.parent ? self.toLocal(obj.parent.toGlobal(obj.position)) : {
x: x,
y: y
};
// Back button clicked
if (clickPos.y > 1350 && clickPos.y < 1450) {
showMenu();
return;
}
// Check ship buttons
for (var i = 0; i < shipButtons.length; i++) {
var btn = shipButtons[i];
if (Math.abs(clickPos.x - btn.x) < 200 && Math.abs(clickPos.y - btn.y) < 50) {
currentShipType = btn.shipType;
showMenu();
break;
}
}
};
return self;
});
var StoryPanel = Container.expand(function (levelNumber) {
var self = Container.call(this);
var stories = ["The alien invasion has begun!\nDefend Earth's last outpost.", "Enemy forces are multiplying.\nPrepare for heavier resistance.", "Advanced alien fighters detected.\nUpgrade your weapons!", "The mothership approaches.\nThis is our final stand!", "Victory! Earth is safe.\nBut stay vigilant, commander."];
var storyText = new Text2(stories[Math.min(levelNumber - 1, stories.length - 1)] || "Mission briefing loading...", {
size: 70,
fill: 0xFFFFFF
});
storyText.anchor.set(0.5, 0.5);
storyText.x = 1024;
storyText.y = 800;
self.addChild(storyText);
var continueText = new Text2('TAP TO CONTINUE', {
size: 60,
fill: 0x00FF00
});
continueText.anchor.set(0.5, 0.5);
continueText.x = 1024;
continueText.y = 1200;
self.addChild(continueText);
self.down = function (x, y, obj) {
self.destroy();
initializeGameplay();
};
return self;
});
var TutorialScreen = Container.expand(function () {
var self = Container.call(this);
var tutorialSteps = ["Welcome to Space Defender!\nTap and drag to move your ship.", "Your ship fires automatically.\nDestroy enemies to score points!", "Collect blue power-ups for rapid fire.\nAvoid enemy bullets!", "Different gun types affect your damage:\nBasic, Rapid, and Heavy modes.", "Enemies have different health levels.\nSome take multiple hits to destroy!", "Ready to begin your mission?\nTap to start the game!"];
var currentStep = 0;
var stepText = new Text2(tutorialSteps[0], {
size: 80,
fill: 0xFFFFFF
});
stepText.anchor.set(0.5, 0.5);
stepText.x = 1024;
stepText.y = 800;
self.addChild(stepText);
var nextText = new Text2('TAP TO CONTINUE', {
size: 60,
fill: 0x00FF00
});
nextText.anchor.set(0.5, 0.5);
nextText.x = 1024;
nextText.y = 1200;
self.addChild(nextText);
var skipText = new Text2('SKIP TUTORIAL', {
size: 50,
fill: 0xFF0000
});
skipText.anchor.set(0.5, 0.5);
skipText.x = 1024;
skipText.y = 1400;
self.addChild(skipText);
self.down = function (x, y, obj) {
var clickPos = obj.parent ? self.toLocal(obj.parent.toGlobal(obj.position)) : {
x: x,
y: y
};
// Skip tutorial
if (clickPos.y > 1350 && clickPos.y < 1450) {
self.destroy();
initializeGameplay();
return;
}
// Next step
currentStep++;
if (currentStep >= tutorialSteps.length) {
self.destroy();
initializeGameplay();
} else {
stepText.setText(tutorialSteps[currentStep]);
}
};
return self;
});
var WarriorSelectionScreen = Container.expand(function () {
var self = Container.call(this);
var titleText = new Text2('SELECT WARRIOR', {
size: 100,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 300;
self.addChild(titleText);
// Warrior type buttons
var warriorButtons = [];
var warriorTypeNames = ['ACE PILOT', 'VETERAN', 'COMMANDER'];
var warriorTypeKeys = ['pilot', 'veteran', 'commander'];
for (var i = 0; i < warriorTypeNames.length; i++) {
var warrior = warriorTypes[warriorTypeKeys[i]];
var warriorButton = new Text2(warriorTypeNames[i], {
size: 80,
fill: warrior.color
});
warriorButton.anchor.set(0.5, 0.5);
warriorButton.x = 1024;
warriorButton.y = 600 + i * 200;
warriorButton.warriorType = warriorTypeKeys[i];
self.addChild(warriorButton);
warriorButtons.push(warriorButton);
// Add warrior stats text
var statsText = new Text2('HP: ' + warrior.hp + ' | Speed: ' + warrior.speed + 'x | Gun: ' + warrior.gunBonus + 'x', {
size: 50,
fill: 0xCCCCCC
});
statsText.anchor.set(0.5, 0.5);
statsText.x = 1024;
statsText.y = 650 + i * 200;
self.addChild(statsText);
}
var backButton = new Text2('BACK', {
size: 80,
fill: 0xFF0000
});
backButton.anchor.set(0.5, 0.5);
backButton.x = 1024;
backButton.y = 1400;
self.addChild(backButton);
self.down = function (x, y, obj) {
var clickPos = obj.parent ? self.toLocal(obj.parent.toGlobal(obj.position)) : {
x: x,
y: y
};
// Back button clicked
if (clickPos.y > 1350 && clickPos.y < 1450) {
showMenu();
return;
}
// Check warrior buttons
for (var i = 0; i < warriorButtons.length; i++) {
var btn = warriorButtons[i];
if (Math.abs(clickPos.x - btn.x) < 200 && Math.abs(clickPos.y - btn.y) < 50) {
currentWarriorType = btn.warriorType;
showMenu();
break;
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000033
});
/****
* Game Code
****/
// Game state variables
var gameState = 'menu'; // menu, levelSelect, story, playing, tutorial
var menuScreen = null;
var levelSelectScreen = null;
var storyPanel = null;
var tutorialScreen = null;
var gunSelectionScreen = null;
var warriorSelectionScreen = null;
var shipSelectionScreen = null;
var leaderboardScreen = null;
var nameEntryScreen = null;
// Gun types system
var gunTypes = {
basic: {
damage: 1,
speedMultiplier: 1.0,
interval: 15
},
rapid: {
damage: 1,
speedMultiplier: 1.2,
interval: 8
},
heavy: {
damage: 3,
speedMultiplier: 0.8,
interval: 25
}
};
var currentGunType = 'basic';
// Warrior types system
var warriorTypes = {
pilot: {
name: 'ACE PILOT',
hp: 3,
speed: 1.0,
gunBonus: 1.0,
color: 0x00FF00
},
veteran: {
name: 'VETERAN',
hp: 5,
speed: 0.8,
gunBonus: 1.2,
color: 0xFF8000
},
commander: {
name: 'COMMANDER',
hp: 4,
speed: 1.2,
gunBonus: 1.5,
color: 0xFF0000
}
};
var currentWarriorType = 'pilot';
// Ship types system
var shipTypes = {
fighter: {
name: 'FIGHTER',
speed: 1.2,
armor: 1.0,
fireRate: 1.0,
color: 0x0080FF
},
interceptor: {
name: 'INTERCEPTOR',
speed: 1.5,
armor: 0.8,
fireRate: 1.3,
color: 0x00FFFF
},
cruiser: {
name: 'CRUISER',
speed: 0.8,
armor: 1.5,
fireRate: 0.8,
color: 0x8000FF
}
};
var currentShipType = 'fighter';
// Game variables
var player;
var playerBullets = [];
var enemies = [];
var enemyBullets = [];
var powerUps = [];
var bosses = [];
var enemySpawnTimer = 0;
var enemySpawnInterval = 60;
var powerUpSpawnTimer = 0;
var bossSpawnScore = 500;
var nextBossScore = bossSpawnScore;
var dragNode = null;
var waveLevel = 1;
var levelTargetScore = 0;
var levelCompleted = false;
// UI Elements
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 120;
scoreTxt.y = 50;
var hpTxt = new Text2('HP: 3', {
size: 60,
fill: 0xFF0000
});
hpTxt.anchor.set(1, 0);
hpTxt.x = -50;
hpTxt.y = 50;
var levelTxt = new Text2('Level: 1', {
size: 60,
fill: 0x00FF00
});
levelTxt.anchor.set(0.5, 0);
levelTxt.y = 50;
function showMenu() {
gameState = 'menu';
clearGame();
menuScreen = new MenuScreen();
game.addChild(menuScreen);
}
function showLevelSelect() {
gameState = 'levelSelect';
if (menuScreen) {
menuScreen.destroy();
menuScreen = null;
}
levelSelectScreen = new LevelSelectScreen();
game.addChild(levelSelectScreen);
}
function showTutorial() {
gameState = 'tutorial';
if (menuScreen) {
menuScreen.destroy();
menuScreen = null;
}
storage.gameMode = 'tutorial';
storage.currentLevel = 1;
tutorialScreen = new TutorialScreen();
game.addChild(tutorialScreen);
}
function showGunSelection() {
gameState = 'gunSelection';
if (menuScreen) {
menuScreen.destroy();
menuScreen = null;
}
gunSelectionScreen = new GunSelectionScreen();
game.addChild(gunSelectionScreen);
}
function showWarriorSelection() {
gameState = 'warriorSelection';
if (menuScreen) {
menuScreen.destroy();
menuScreen = null;
}
warriorSelectionScreen = new WarriorSelectionScreen();
game.addChild(warriorSelectionScreen);
}
function showShipSelection() {
gameState = 'shipSelection';
if (menuScreen) {
menuScreen.destroy();
menuScreen = null;
}
shipSelectionScreen = new ShipSelectionScreen();
game.addChild(shipSelectionScreen);
}
function showLeaderboard() {
gameState = 'leaderboard';
if (menuScreen) {
menuScreen.destroy();
menuScreen = null;
}
leaderboardScreen = new LeaderboardScreen();
game.addChild(leaderboardScreen);
}
function showNameEntry(finalScore) {
gameState = 'nameEntry';
nameEntryScreen = new NameEntryScreen(finalScore);
game.addChild(nameEntryScreen);
}
function checkHighScore(score) {
var leaderboardData = storage.leaderboard || [];
// Check if this score qualifies for leaderboard (top 20 or empty leaderboard)
if (leaderboardData.length < 20) {
return true;
}
// Sort to find lowest score in leaderboard
leaderboardData.sort(function (a, b) {
return b.score - a.score;
});
return score > leaderboardData[leaderboardData.length - 1].score;
}
function startGame() {
gameState = storage.gameMode === 'story' ? 'story' : 'playing';
if (levelSelectScreen) {
levelSelectScreen.destroy();
levelSelectScreen = null;
}
if (menuScreen) {
menuScreen.destroy();
menuScreen = null;
}
if (storage.gameMode === 'story' && storage.currentLevel <= 5) {
storyPanel = new StoryPanel(storage.currentLevel);
game.addChild(storyPanel);
} else {
initializeGameplay();
}
}
function initializeGameplay() {
gameState = 'playing';
// Clear any story panel
if (storyPanel) {
storyPanel.destroy();
storyPanel = null;
}
// Reset game variables
clearGame();
LK.setScore(0);
// Set level parameters
setupLevel(storage.currentLevel);
// Initialize player
player = new Player();
player.x = 1024;
player.y = 2400;
game.addChild(player);
// Add UI elements
LK.gui.topLeft.addChild(scoreTxt);
LK.gui.topRight.addChild(hpTxt);
LK.gui.top.addChild(levelTxt);
levelTxt.setText('Level: ' + storage.currentLevel);
}
function setupLevel(level) {
// Set level-specific parameters
if (storage.gameMode === 'endless') {
levelTargetScore = 0; // No target for endless mode
enemySpawnInterval = 60;
} else {
levelTargetScore = level * 200; // Score needed to complete level
enemySpawnInterval = Math.max(20, 80 - level * 5);
bossSpawnScore = levelTargetScore * 0.8;
nextBossScore = bossSpawnScore;
}
levelCompleted = false;
}
function clearGame() {
// Clear all game objects
for (var i = 0; i < enemies.length; i++) {
enemies[i].destroy();
}
for (var i = 0; i < enemyBullets.length; i++) {
enemyBullets[i].destroy();
}
for (var i = 0; i < playerBullets.length; i++) {
playerBullets[i].destroy();
}
for (var i = 0; i < powerUps.length; i++) {
powerUps[i].destroy();
}
for (var i = 0; i < bosses.length; i++) {
bosses[i].destroy();
}
if (player) {
player.destroy();
player = null;
}
// Clear arrays
enemies = [];
enemyBullets = [];
playerBullets = [];
powerUps = [];
bosses = [];
// Remove UI elements
if (scoreTxt.parent) scoreTxt.parent.removeChild(scoreTxt);
if (hpTxt.parent) hpTxt.parent.removeChild(hpTxt);
if (levelTxt.parent) levelTxt.parent.removeChild(levelTxt);
// Clean up gun selection screen
if (gunSelectionScreen) {
gunSelectionScreen.destroy();
gunSelectionScreen = null;
}
// Clean up warrior selection screen
if (warriorSelectionScreen) {
warriorSelectionScreen.destroy();
warriorSelectionScreen = null;
}
// Clean up ship selection screen
if (shipSelectionScreen) {
shipSelectionScreen.destroy();
shipSelectionScreen = null;
}
// Clean up leaderboard screen
if (leaderboardScreen) {
leaderboardScreen.destroy();
leaderboardScreen = null;
}
// Clean up name entry screen
if (nameEntryScreen) {
nameEntryScreen.destroy();
nameEntryScreen = null;
}
}
function checkLevelComplete() {
if (storage.gameMode !== 'endless' && !levelCompleted && LK.getScore() >= levelTargetScore) {
levelCompleted = true;
// Update progress
if (storage.gameMode === 'story') {
storage.storyProgress = Math.max(storage.storyProgress, storage.currentLevel);
}
storage.unlockedLevels = Math.max(storage.unlockedLevels, storage.currentLevel + 1);
// Show level complete message
var completeText = new Text2('LEVEL COMPLETE!', {
size: 100,
fill: 0x00FF00
});
completeText.anchor.set(0.5, 0.5);
completeText.x = 1024;
completeText.y = 1366;
game.addChild(completeText);
// Auto-advance after 2 seconds
LK.setTimeout(function () {
if (storage.gameMode === 'story' && storage.currentLevel < 5) {
storage.currentLevel++;
startGame();
} else {
showMenu();
}
}, 2000);
}
}
// Initialize menu on start
showMenu();
// Event handlers
function handleMove(x, y, obj) {
if (dragNode && player) {
var moveSpeed = player.speed || 1.0;
var targetX = Math.max(40, Math.min(2008, x));
var targetY = Math.max(100, Math.min(2632, y));
// Apply speed multiplier to movement
var deltaX = (targetX - dragNode.x) * moveSpeed;
var deltaY = (targetY - dragNode.y) * moveSpeed;
dragNode.x += deltaX * 0.1; // Smooth movement
dragNode.y += deltaY * 0.1;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
dragNode = player;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Main game loop
game.update = function () {
if (gameState !== 'playing') return;
// Update score display
scoreTxt.setText('Score: ' + LK.getScore());
hpTxt.setText('HP: ' + player.hp);
// Check level completion
checkLevelComplete();
// Spawn enemies
enemySpawnTimer++;
var currentSpawnInterval = Math.max(20, enemySpawnInterval - Math.floor(LK.getScore() / 100) * 5);
if (enemySpawnTimer >= currentSpawnInterval) {
enemySpawnTimer = 0;
var enemy = new Enemy();
enemy.x = Math.random() * 1800 + 124;
enemy.y = -50;
enemy.speed = 2 + Math.floor(LK.getScore() / 200) + (storage.currentLevel - 1);
enemies.push(enemy);
game.addChild(enemy);
}
// Spawn power-ups occasionally
powerUpSpawnTimer++;
if (powerUpSpawnTimer >= 900 && Math.random() < 0.02) {
powerUpSpawnTimer = 0;
var powerUp = new PowerUp();
powerUp.x = Math.random() * 1800 + 124;
powerUp.y = -50;
powerUps.push(powerUp);
game.addChild(powerUp);
}
// Spawn boss at score milestones
if (LK.getScore() >= nextBossScore && bosses.length === 0) {
var boss = new Boss();
boss.x = 1024;
boss.y = -100;
bosses.push(boss);
game.addChild(boss);
nextBossScore += bossSpawnScore;
}
// Handle player bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
if (bullet.lastY === undefined) bullet.lastY = bullet.y;
// Remove bullets that go off screen
if (bullet.lastY >= -50 && bullet.y < -50) {
bullet.destroy();
playerBullets.splice(i, 1);
continue;
}
// Check collision with enemies
var hitEnemy = false;
for (var j = enemies.length - 1; j >= 0; j--) {
if (bullet.intersects(enemies[j])) {
enemies[j].hp -= bullet.damage || 1;
// Visual feedback for damage
tween(enemies[j], {
tint: 0xFFFFFF
}, {
duration: 100,
onFinish: function onFinish() {
if (enemies[j] && enemies[j].hp > 2) {
enemies[j].children[0].tint = 0xFF6666;
} else if (enemies[j] && enemies[j].hp > 1) {
enemies[j].children[0].tint = 0xFFAAAA;
}
}
});
if (enemies[j].hp <= 0) {
LK.setScore(LK.getScore() + 10 * enemies[j].maxHp);
LK.getSound('enemyHit').play();
enemies[j].destroy();
enemies.splice(j, 1);
}
bullet.destroy();
playerBullets.splice(i, 1);
hitEnemy = true;
break;
}
}
if (hitEnemy) continue;
// Check collision with bosses
for (var k = bosses.length - 1; k >= 0; k--) {
if (bullet.intersects(bosses[k])) {
bosses[k].hp -= bullet.damage || 1;
// Visual feedback for boss damage
tween(bosses[k], {
tint: 0xFFFFFF
}, {
duration: 150
});
if (bosses[k].hp <= 0) {
LK.setScore(LK.getScore() + 100);
LK.getSound('enemyHit').play();
bosses[k].destroy();
bosses.splice(k, 1);
}
bullet.destroy();
playerBullets.splice(i, 1);
break;
}
}
bullet.lastY = bullet.y;
}
// Handle enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
if (bullet.lastY === undefined) bullet.lastY = bullet.y;
// Remove bullets that go off screen
if (bullet.lastY <= 2782 && bullet.y > 2782) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check collision with player
if (bullet.intersects(player)) {
player.hp--;
LK.getSound('playerHit').play();
LK.effects.flashObject(player, 0xff0000, 500);
if (player.hp <= 0) {
var finalScore = LK.getScore();
LK.showGameOver();
LK.setTimeout(function () {
if (checkHighScore(finalScore)) {
showNameEntry(finalScore);
} else {
showMenu();
}
}, 2000);
return;
}
bullet.destroy();
enemyBullets.splice(i, 1);
}
bullet.lastY = bullet.y;
}
// Handle enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.lastY === undefined) enemy.lastY = enemy.y;
// Remove enemies that go off screen
if (enemy.lastY <= 2782 && enemy.y > 2782) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
// Check collision with player
if (enemy.intersects(player)) {
player.hp--;
LK.getSound('playerHit').play();
LK.effects.flashScreen(0xff0000, 1000);
if (player.hp <= 0) {
var finalScore = LK.getScore();
LK.showGameOver();
LK.setTimeout(function () {
if (checkHighScore(finalScore)) {
showNameEntry(finalScore);
} else {
showMenu();
}
}, 2000);
return;
}
enemy.destroy();
enemies.splice(i, 1);
}
enemy.lastY = enemy.y;
}
// Handle bosses
for (var i = bosses.length - 1; i >= 0; i--) {
var boss = bosses[i];
if (boss.lastY === undefined) boss.lastY = boss.y;
// Remove bosses that go off screen
if (boss.lastY <= 2782 && boss.y > 2782) {
boss.destroy();
bosses.splice(i, 1);
continue;
}
// Check collision with player
if (boss.intersects(player)) {
player.hp--;
LK.getSound('playerHit').play();
LK.effects.flashScreen(0xff0000, 1000);
if (player.hp <= 0) {
var finalScore = LK.getScore();
LK.showGameOver();
LK.setTimeout(function () {
if (checkHighScore(finalScore)) {
showNameEntry(finalScore);
} else {
showMenu();
}
}, 2000);
return;
}
}
boss.lastY = boss.y;
}
// Handle power-ups
for (var i = powerUps.length - 1; i >= 0; i--) {
var powerUp = powerUps[i];
if (powerUp.lastY === undefined) powerUp.lastY = powerUp.y;
// Remove power-ups that go off screen
if (powerUp.lastY <= 2782 && powerUp.y > 2782) {
powerUp.destroy();
powerUps.splice(i, 1);
continue;
}
// Check collision with player
if (powerUp.intersects(player)) {
LK.getSound('powerUpSound').play();
if (powerUp.type === 'rapidFire') {
player.rapidFire = true;
player.powerUpTimer = 600; // 10 seconds at 60fps
}
powerUp.destroy();
powerUps.splice(i, 1);
}
powerUp.lastY = powerUp.y;
}
};