/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { currentLevel: 1, maxLevelReached: 1, playerPowerups: {}, controlsOnRight: false }); /**** * Classes ****/ var Boss = Container.expand(function (type, level) { var self = Container.call(this); self.type = type || 'miniBoss1'; self.level = level || 1; var bossGraphics = self.attachAsset(self.type, { anchorX: 0.5, anchorY: 0.5 }); // Boss stats scale with level // Reduced health for minibosses, normal health for main bosses self.health = self.type.includes('miniBoss') ? 75 * self.level : 100 * self.level; self.maxHealth = self.health; self.speed = 3 + self.level * 0.5; self.shootCooldown = 0; // Longer delay between shots for minibosses self.shootDelay = self.type.includes('miniBoss') ? Math.max(40 - self.level * 5, 15) : Math.max(30 - self.level * 5, 10); self.attackPattern = 0; self.phaseCounter = 0; self.phases = ['entry', 'attack1', 'movement', 'attack2', 'rage']; self.currentPhase = 'entry'; self.phaseTimer = 180; // 3 seconds per phase self.moveDirection = 1; self.targetX = 2048 / 2; self.targetY = 300; // Define movement patterns based on boss type self.patterns = { miniBoss1: { entry: function entry() { if (self.y < self.targetY) { self.y += self.speed; } else { self.changePhase('attack1'); } }, attack1: function attack1() { // Simple straight shots if (self.shootCooldown <= 0) { self.shootBullets(); self.shootCooldown = self.shootDelay * 1.5; // Increased delay for easier gameplay } // Move side to side self.x += self.speed * self.moveDirection; if (self.x > 1800 || self.x < 248) { self.moveDirection *= -1; } }, movement: function movement() { // Circle pattern self.x = self.targetX + Math.cos(self.phaseCounter / 30) * 300; self.y = self.targetY + Math.sin(self.phaseCounter / 30) * 100; }, attack2: function attack2() { // Spread shots if (self.shootCooldown <= 0) { self.shootSpread(5, 0.2); self.shootCooldown = self.shootDelay * 2; } }, rage: function rage() { // Fast movements and frequent attacks self.x += self.speed * 1.5 * self.moveDirection; if (self.x > 1800 || self.x < 248) { self.moveDirection *= -1; } if (self.shootCooldown <= 0) { self.shootSpread(3, 0.3); self.shootCooldown = self.shootDelay / 2; } } }, miniBoss2: { entry: function entry() { if (self.y < self.targetY) { self.y += self.speed; } else { self.changePhase('attack1'); } }, attack1: function attack1() { // Laser attacks if (self.shootCooldown <= 0) { self.shootLaser(); self.shootCooldown = self.shootDelay * 3; } // Slow tracking if (player && self.phaseCounter % 30 === 0) { self.targetX = player.x; } self.x += (self.targetX - self.x) * 0.02; }, movement: function movement() { // Quick dash to one side then the other if (self.phaseCounter < self.phaseTimer / 2) { self.x += self.speed * 2; } else { self.x -= self.speed * 2; } // Keep in bounds self.x = Math.max(200, Math.min(1848, self.x)); }, attack2: function attack2() { // Circular bullet pattern if (self.shootCooldown <= 0) { self.shootCircle(8); self.shootCooldown = self.shootDelay * 2; } }, rage: function rage() { // Erratic movement and mixed attacks self.x = self.targetX + Math.sin(self.phaseCounter / 10) * 300; if (self.shootCooldown <= 0) { if (self.phaseCounter % 120 < 60) { self.shootSpread(7, 0.15); } else { self.shootLaser(); } self.shootCooldown = self.shootDelay; } } }, boss1: { // More complex patterns for main bosses entry: function entry() { if (self.y < self.targetY) { self.y += self.speed; } else { self.changePhase('attack1'); } }, attack1: function attack1() { // Wave pattern with bursts of bullets self.x = self.targetX + Math.sin(self.phaseCounter / 20) * 400; if (self.shootCooldown <= 0) { if (self.phaseCounter % 60 < 30) { self.shootBullets(); } else { self.shootSpread(3, 0.2); } self.shootCooldown = self.shootDelay; } }, movement: function movement() { // Quick charge toward player if (player && self.phaseCounter % 60 === 0) { self.targetX = player.x; self.targetY = player.y - 300; } self.x += (self.targetX - self.x) * 0.05; self.y += (self.targetY - self.y) * 0.05; // Don't get too close to player self.y = Math.min(self.y, 500); }, attack2: function attack2() { // Laser attacks with bullet spray if (self.shootCooldown <= 0) { if (self.phaseCounter % 120 < 60) { self.shootLaser(); } else { self.shootCircle(12); } self.shootCooldown = self.shootDelay * 1.5; } // Slow side-to-side movement self.x += Math.sin(self.phaseCounter / 30) * 5; }, rage: function rage() { // Fast erratic movement with constant attacks self.x = self.targetX + Math.sin(self.phaseCounter / 10) * 300 + Math.cos(self.phaseCounter / 15) * 200; self.y = self.targetY + Math.sin(self.phaseCounter / 12) * 100; if (self.shootCooldown <= 0) { // Alternate between different attack patterns switch (Math.floor(self.phaseCounter / 40) % 3) { case 0: self.shootSpread(5, 0.15); break; case 1: self.shootLaser(); break; case 2: self.shootCircle(8); break; } self.shootCooldown = self.shootDelay * 0.7; } } }, boss2: { // Similar structure, different patterns entry: function entry() { if (self.y < self.targetY) { self.y += self.speed; } else { self.changePhase('attack1'); } }, attack1: function attack1() { // Spiral bullet pattern if (self.shootCooldown <= 0) { self.shootSpiral(self.phaseCounter / 10); self.shootCooldown = Math.max(5, self.shootDelay / 2); } // Slow rotation around center self.x = self.targetX + Math.cos(self.phaseCounter / 60) * 200; self.y = self.targetY + Math.sin(self.phaseCounter / 60) * 100; }, movement: function movement() { // Quick teleport movements if (self.phaseCounter % 60 === 0) { // Flash effect for teleport LK.effects.flashObject(self, 0xFFFFFF, 200); // Choose random position in top third of screen self.targetX = 300 + Math.random() * (2048 - 600); self.targetY = 150 + Math.random() * 250; } // Quick movement to target self.x += (self.targetX - self.x) * 0.1; self.y += (self.targetY - self.y) * 0.1; }, attack2: function attack2() { // Multiple lasers if (self.shootCooldown <= 0) { for (var i = 0; i < 3; i++) { var offset = (i - 1) * 200; self.shootLaser(offset); } self.shootCooldown = self.shootDelay * 2; } // Small jittery movements self.x += (Math.random() - 0.5) * 10; self.y += (Math.random() - 0.5) * 5; // Keep in bounds self.x = Math.max(200, Math.min(1848, self.x)); self.y = Math.max(100, Math.min(500, self.y)); }, rage: function rage() { // Screen-filling attacks and fast movement if (self.shootCooldown <= 0) { // Alternate between massive spreads and laser curtains if (self.phaseCounter % 180 < 90) { self.shootSpread(15, 0.1); } else { for (var i = 0; i < 5; i++) { var offset = (i - 2) * 150; self.shootLaser(offset); } } self.shootCooldown = self.shootDelay * 0.6; } // Aggressive tracking of player if (player) { self.targetX = player.x; self.x += (self.targetX - self.x) * 0.03; } // Vertical bobbing self.y = self.targetY + Math.sin(self.phaseCounter / 20) * 100; } }, boss3: { // More extreme patterns entry: function entry() { if (self.y < self.targetY) { self.y += self.speed; } else { self.changePhase('attack1'); } }, attack1: function attack1() { // 360-degree bullet hell if (self.shootCooldown <= 0) { self.shootCircle(16); self.shootCooldown = self.shootDelay; } // Slow pulsing movement self.x = self.targetX + Math.sin(self.phaseCounter / 30) * 300; self.y = self.targetY + Math.cos(self.phaseCounter / 30) * 100; }, movement: function movement() { // Aggressive dash toward player position then retreat if (self.phaseCounter % 90 < 45 && player) { // Dash toward player self.targetX = player.x; self.targetY = player.y - 150; self.x += (self.targetX - self.x) * 0.08; self.y += (self.targetY - self.y) * 0.08; } else { // Retreat to top self.targetY = 250; self.y += (self.targetY - self.y) * 0.05; } }, attack2: function attack2() { // Multiple attack types simultaneously if (self.shootCooldown <= 0) { // Lasers + bullets combo self.shootLaser(0); self.shootSpread(9, 0.15); self.shootCooldown = self.shootDelay * 1.2; } // Side-to-side sweep self.x += self.speed * 2 * self.moveDirection; if (self.x > 1800 || self.x < 248) { self.moveDirection *= -1; } }, rage: function rage() { // Total bullet hell mode if (self.shootCooldown <= 0) { // Spiral + lasers + directed shots self.shootSpiral(self.phaseCounter / 5); if (self.phaseCounter % 60 === 0) { // Multiple lasers in fan pattern for (var i = -2; i <= 2; i++) { self.shootLaser(i * 120); } } // Direct shots at player if (player && self.phaseCounter % 30 === 0) { self.shootAtPlayer(); } self.shootCooldown = Math.max(3, self.shootDelay / 3); } // Chaotic movement self.x = self.targetX + Math.sin(self.phaseCounter / 10) * 400 + Math.cos(self.phaseCounter / 7) * 200; self.y = self.targetY + Math.sin(self.phaseCounter / 8) * 150; } } }; // Shooting methods self.shootBullets = function () { var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y + 50; bullet.angle = Math.PI / 2; // Straight down bullets.push(bullet); game.addChild(bullet); }; self.shootSpread = function (count, spreadAngle) { var startAngle = Math.PI / 2 - spreadAngle * (count - 1) / 2; for (var i = 0; i < count; i++) { var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y + 50; bullet.angle = startAngle + spreadAngle * i; bullets.push(bullet); game.addChild(bullet); } }; self.shootCircle = function (count) { var angleStep = Math.PI * 2 / count; for (var i = 0; i < count; i++) { var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y; bullet.angle = angleStep * i; bullets.push(bullet); game.addChild(bullet); } }; self.shootSpiral = function (baseAngle) { var count = 8; var angleStep = Math.PI * 2 / count; for (var i = 0; i < count; i++) { var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y; bullet.angle = baseAngle + angleStep * i; bullets.push(bullet); game.addChild(bullet); } }; self.shootLaser = function (xOffset) { xOffset = xOffset || 0; var laser = new EnemyLaser(); laser.x = self.x + xOffset; laser.y = self.y; lasers.push(laser); game.addChild(laser); }; self.shootAtPlayer = function () { if (!player) return; var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y + 50; // Calculate angle to player var dx = player.x - self.x; var dy = player.y - self.y; bullet.angle = Math.atan2(dy, dx); bullets.push(bullet); game.addChild(bullet); }; self.changePhase = function (newPhase) { self.currentPhase = newPhase; self.phaseCounter = 0; // Adjust phase timer based on phase if (newPhase === 'rage') { self.phaseTimer = 360; // Longer rage phase } else { self.phaseTimer = 180; // Normal phases } }; self.takeDamage = function (damage) { self.health -= damage; // Flash boss when hit LK.effects.flashObject(bossGraphics, 0xFFFFFF, 200); LK.getSound('bossHit').play(); // Check for phase change based on health percentage var healthPercent = self.health / self.maxHealth; if (healthPercent <= 0.25 && self.currentPhase !== 'rage') { self.changePhase('rage'); } else if (healthPercent <= 0.5 && self.currentPhase !== 'rage' && self.currentPhase !== 'attack2') { self.changePhase('attack2'); } else if (healthPercent <= 0.75 && self.currentPhase !== 'rage' && self.currentPhase !== 'attack2' && self.currentPhase !== 'movement') { self.changePhase('movement'); } return self.health <= 0; }; self.update = function () { // Update based on current phase if (self.patterns[self.type] && self.patterns[self.type][self.currentPhase]) { self.patterns[self.type][self.currentPhase](); } // Update counters self.phaseCounter++; if (self.shootCooldown > 0) { self.shootCooldown--; } // Check for phase timeout if (self.phaseCounter >= self.phaseTimer && self.currentPhase !== 'rage' && self.health > self.maxHealth * 0.25) { // Cycle to next phase var currentPhaseIndex = self.phases.indexOf(self.currentPhase); var nextPhaseIndex = (currentPhaseIndex + 1) % (self.phases.length - 1); // Skip rage phase self.changePhase(self.phases[nextPhaseIndex]); } }; return self; }); var ControlButton = Container.expand(function (direction) { var self = Container.call(this); // Different color for each direction button var color; switch (direction) { case 'up': color = 0x4285F4; break; // Blue case 'down': color = 0xEA4335; break; // Red case 'left': color = 0xFBBC05; break; // Yellow case 'right': color = 0x34A853; break; // Green default: color = 0xFFFFFF; // White } // Create button shape var buttonGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); // Set button color buttonGraphics.tint = color; // Arrow display (using text as a simple way to show direction) var arrowSymbol; switch (direction) { case 'up': arrowSymbol = '▲'; break; case 'down': arrowSymbol = '▼'; break; case 'left': arrowSymbol = '◀'; break; case 'right': arrowSymbol = '▶'; break; default: arrowSymbol = '•'; } var arrow = new Text2(arrowSymbol, { size: 60, fill: 0xFFFFFF }); arrow.anchor.set(0.5, 0.5); self.addChild(arrow); // Store direction for later use self.direction = direction; // Handle button press self.down = function (x, y, obj) { // Visual feedback - shrink slightly when pressed buttonGraphics.scale.set(1.1); // Start moving in this direction if (player && gameState === 'playing') { movePlayer(self.direction, true); } }; // Handle button release self.up = function (x, y, obj) { // Visual feedback - return to normal size buttonGraphics.scale.set(1.2); // Stop moving in this direction if (player && gameState === 'playing') { movePlayer(self.direction, false); } }; return self; }); var EnemyBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('enemyBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.damage = 1; self.angle = 0; // Direction in radians self.update = function () { self.x += Math.cos(self.angle) * self.speed; self.y += Math.sin(self.angle) * self.speed; // Remove if off screen if (self.y > 2732 + 50 || self.y < -50 || self.x > 2048 + 50 || self.x < -50) { self.shouldRemove = true; } }; return self; }); var EnemyLaser = Container.expand(function () { var self = Container.call(this); var laserGraphics = self.attachAsset('enemyLaser', { anchorX: 0.5, anchorY: 0 }); self.damage = 2; self.duration = 120; // 2 seconds at 60fps self.counter = 0; self.warningCounter = 60; // 1 second warning // Start with low alpha for warning laserGraphics.alpha = 0.3; self.update = function () { self.counter++; if (self.counter < self.warningCounter) { // Warning phase if (self.counter % 10 < 5) { laserGraphics.alpha = 0.5; } else { laserGraphics.alpha = 0.3; } } else if (self.counter === self.warningCounter) { // Activate laser laserGraphics.alpha = 0.8; // Expand width tween(laserGraphics, { width: 80 }, { duration: 100 }); } else if (self.counter >= self.duration) { // Remove laser self.shouldRemove = true; } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 10; self.shootCooldown = 0; self.shootDelay = 15; // 4 shots per second self.health = 100; self.maxHealth = 100; self.invincible = false; self.invincibleTimer = 0; self.shield = null; self.powerups = {}; // Track movement directions self.moving = { up: false, down: false, left: false, right: false }; // Initialize shield (hidden at first) self.createShield = function () { if (self.shield) return; self.shield = self.attachAsset('playerShield', { anchorX: 0.5, anchorY: 0.5 }); self.shield.alpha = 0; }; self.activateShield = function (duration) { if (!self.shield) self.createShield(); self.shield.alpha = 0.7; self.invincible = true; // Fade out shield over duration tween(self.shield, { alpha: 0 }, { duration: duration, onFinish: function onFinish() { self.invincible = false; } }); }; self.takeDamage = function (damage) { if (self.invincible) return false; self.health -= damage; if (self.health <= 0) { self.health = 0; return true; // Player died } // Flash player and make invincible briefly LK.effects.flashObject(playerGraphics, 0xFF0000, 500); self.invincible = true; self.invincibleTimer = 60; // 1 second invincibility LK.getSound('playerHit').play(); return false; // Player alive }; self.shoot = function () { if (self.shootCooldown > 0) return null; var bullet = new PlayerBullet(); bullet.x = self.x; bullet.y = self.y - 40; // Apply powerups if (self.powerups.doubleDamage) { bullet.damage *= 2; bullet.tint = 0xFFFF00; // Yellow for double damage } // Triple shot powerup if (self.powerups.tripleShot) { // Create two additional bullets var bulletLeft = new PlayerBullet(); bulletLeft.x = self.x - 30; bulletLeft.y = self.y - 20; var bulletRight = new PlayerBullet(); bulletRight.x = self.x + 30; bulletRight.y = self.y - 20; self.shootCooldown = self.shootDelay; LK.getSound('playerShoot').play(); return [bullet, bulletLeft, bulletRight]; } self.shootCooldown = self.shootDelay; LK.getSound('playerShoot').play(); return [bullet]; }; self.update = function () { if (self.shootCooldown > 0) { self.shootCooldown--; } if (self.invincible && self.invincibleTimer > 0) { self.invincibleTimer--; if (self.invincibleTimer <= 0) { self.invincible = false; } } // Handle movement based on pressed direction buttons if (gameState === 'playing') { if (self.moving.up) { self.y -= self.speed; } if (self.moving.down) { self.y += self.speed; } if (self.moving.left) { self.x -= self.speed; } if (self.moving.right) { self.x += self.speed; } // Keep player within screen bounds self.x = Math.max(50, Math.min(2048 - 50, self.x)); self.y = Math.max(100, Math.min(2732 - 100, self.y)); } }; self.down = function (x, y, obj) { // This is handled in the game's touch handler }; self.up = function (x, y, obj) { // This is handled in the game's touch handler }; self.createShield(); return self; }); var PlayerBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('playerBullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.7, scaleY: 0.7 }); self.speed = 15; self.damage = 2; // Increased bullet damage to make bosses easier to defeat self.update = function () { self.y -= self.speed; // Remove if off screen if (self.y < -50) { self.shouldRemove = true; } }; return self; }); var PowerUp = Container.expand(function (type) { var self = Container.call(this); self.type = type || 'shield'; var color; switch (self.type) { case 'shield': color = 0x00FFFF; break; case 'doubleDamage': color = 0xFFFF00; break; case 'tripleShot': color = 0xFF00FF; break; default: color = 0xFFFFFF; } var powerupGraphics = self.attachAsset('POWERUP', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1 }); // Set color based on powerup type powerupGraphics.tint = color; self.speed = 3; self.duration = type === 'shield' ? 300 : 600; // Shield lasts 5 seconds, others 10 self.update = function () { self.y += self.speed; // Remove if off screen if (self.y > 2732 + 50) { self.shouldRemove = true; } }; self.applyEffect = function (player) { LK.getSound('powerUp').play(); switch (self.type) { case 'shield': player.activateShield(5000); // 5 seconds break; case 'doubleDamage': player.powerups.doubleDamage = true; // Clear previous timeout if exists if (player.powerups.doubleDamageTimer) { LK.clearTimeout(player.powerups.doubleDamageTimer); } // Set timeout to clear powerup player.powerups.doubleDamageTimer = LK.setTimeout(function () { player.powerups.doubleDamage = false; }, 10000); // 10 seconds break; case 'tripleShot': player.powerups.tripleShot = true; // Clear previous timeout if exists if (player.powerups.tripleShotTimer) { LK.clearTimeout(player.powerups.tripleShotTimer); } // Set timeout to clear powerup player.powerups.tripleShotTimer = LK.setTimeout(function () { player.powerups.tripleShot = false; }, 10000); // 10 seconds break; } self.shouldRemove = true; }; return self; }); var SettingsPanel = Container.expand(function () { var self = Container.call(this); // Semi-transparent black background var background = LK.getAsset('bossHealthBarBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 10, scaleY: 20 }); background.tint = 0x000000; background.alpha = 0.8; self.addChild(background); // Title var titleText = new Text2('SETTINGS', { size: 100, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0); titleText.y = -900; self.addChild(titleText); // Control layout option var controlsText = new Text2('CONTROLS LAYOUT:', { size: 70, fill: 0xFFFFFF }); controlsText.anchor.set(0.5, 0.5); controlsText.y = -700; self.addChild(controlsText); // Left hand option var leftHandButton = LK.getAsset('bullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 1.5 }); leftHandButton.tint = storage.controlsOnRight ? 0x666666 : 0x4285F4; leftHandButton.x = -250; leftHandButton.y = -550; self.addChild(leftHandButton); var leftHandText = new Text2('LEFT HAND', { size: 50, fill: 0xFFFFFF }); leftHandText.anchor.set(0.5, 0.5); leftHandText.x = -250; leftHandText.y = -550; self.addChild(leftHandText); // Right hand option var rightHandButton = LK.getAsset('bullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 1.5 }); rightHandButton.tint = storage.controlsOnRight ? 0x4285F4 : 0x666666; rightHandButton.x = 250; rightHandButton.y = -550; self.addChild(rightHandButton); var rightHandText = new Text2('RIGHT HAND', { size: 50, fill: 0xFFFFFF }); rightHandText.anchor.set(0.5, 0.5); rightHandText.x = 250; rightHandText.y = -550; self.addChild(rightHandText); // No skip tutorial option - removed // Reset to Level 1 button var resetButton = LK.getAsset('bullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 1.5 }); resetButton.tint = 0x34A853; // Green color resetButton.y = 0; self.addChild(resetButton); var resetText = new Text2('RESTART LEVEL 1', { size: 40, fill: 0xFFFFFF }); resetText.anchor.set(0.5, 0.5); resetText.y = 0; self.addChild(resetText); // Close button var closeButton = LK.getAsset('bullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 1.5 }); closeButton.tint = 0xEA4335; closeButton.y = 200; self.addChild(closeButton); var closeText = new Text2('CLOSE', { size: 50, fill: 0xFFFFFF }); closeText.anchor.set(0.5, 0.5); closeText.y = 200; self.addChild(closeText); // Button interaction handlers leftHandButton.interactive = true; leftHandButton.down = function () { if (storage.controlsOnRight) { storage.controlsOnRight = false; leftHandButton.tint = 0x4285F4; rightHandButton.tint = 0x666666; if (typeof repositionControlButtons === 'function') { repositionControlButtons(); } } }; rightHandButton.interactive = true; rightHandButton.down = function () { if (!storage.controlsOnRight) { storage.controlsOnRight = true; rightHandButton.tint = 0x4285F4; leftHandButton.tint = 0x666666; if (typeof repositionControlButtons === 'function') { repositionControlButtons(); } } }; // Reset level button handler resetButton.interactive = true; resetButton.down = function () { // Set current level to 1 storage.currentLevel = 1; currentLevel = 1; // Hide settings panel if (self.parent) { self.parent.removeChild(self); } // Reset game state if (gameState === 'paused') { gameState = 'playing'; } // Clear projectiles and restart level 1 clearProjectiles(); // Remove current boss if exists if (currentBoss && currentBoss.parent) { currentBoss.parent.removeChild(currentBoss); currentBoss = null; } // Flash screen for feedback LK.effects.flashScreen(0xFFFFFF, 500); // Start from level 1 startLevel(); }; // No skip tutorial button handler - removed closeButton.interactive = true; closeButton.down = function () { if (self.parent) { self.parent.removeChild(self); if (gameState === 'paused') { resumeGame(); } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Core game variables // Player, bosses, projectiles, UI elements, and effects // Mini bosses // Main bosses // Boss projectiles // Health bars and UI // Sound effects // Background music var player; var currentBoss; var bullets = []; var lasers = []; var playerBullets = []; var powerups = []; var currentLevel = storage.currentLevel || 1; var gameState = 'tutorial'; // tutorial, playing, bossDead, gameOver var tutorialStep = 0; var lastShootTime = 0; var controlButtons = []; // UI Elements var scoreText; var levelText; var playerHealthBar; var playerHealthBarBg; var bossHealthBar; var bossHealthBarBg; var tutorialText; // Initialize game function initGame() { // Background game.setBackgroundColor(0x111133); // Create player player = new Player(); player.x = 2048 / 2; player.y = 2732 - 200; game.addChild(player); // Initialize UI createUI(); // Always start with tutorial if first level if (currentLevel === 1) { showTutorial(); } else { startLevel(); } // Play background music LK.playMusic('battleMusic'); } function createUI() { // Score and level display scoreText = new Text2('SCORE: 0', { size: 60, fill: 0xFFFFFF }); scoreText.anchor.set(0, 0); LK.gui.top.addChild(scoreText); scoreText.x = 150; scoreText.y = 30; levelText = new Text2('LEVEL ' + currentLevel, { size: 60, fill: 0xFFFFFF }); levelText.anchor.set(1, 0); LK.gui.topRight.addChild(levelText); levelText.x = -50; levelText.y = 30; // Add settings button in top right corner var settingsButton = LK.getAsset('bullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1 }); settingsButton.tint = 0x999999; settingsButton.x = 2048 - 100; // Position in top right settingsButton.y = 100; // Position in top right game.addChild(settingsButton); var settingsText = new Text2('⚙️', { size: 60, fill: 0xFFFFFF }); settingsText.anchor.set(0.5, 0.5); settingsText.x = 2048 - 100; // Match the button position settingsText.y = 100; // Match the button position game.addChild(settingsText); // Make settings button interactive settingsButton.interactive = true; settingsButton.down = function () { if (gameState !== 'paused') { pauseGame(); showSettings(); } }; // Create directional control buttons createControlButtons(); // Player health bar playerHealthBarBg = LK.getAsset('healthBarBackground', { anchorX: 0, anchorY: 0.5 }); playerHealthBar = LK.getAsset('healthBar', { anchorX: 0, anchorY: 0.5 }); playerHealthBarBg.x = 50; playerHealthBarBg.y = 100; playerHealthBar.x = 50; playerHealthBar.y = 100; game.addChild(playerHealthBarBg); game.addChild(playerHealthBar); // Boss health bar (hidden initially) bossHealthBarBg = LK.getAsset('bossHealthBarBackground', { anchorX: 0.5, anchorY: 0.5 }); bossHealthBar = LK.getAsset('bossHealthBar', { anchorX: 0, anchorY: 0.5 }); bossHealthBarBg.x = 2048 / 2; bossHealthBarBg.y = 50; bossHealthBar.x = 2048 / 2 - bossHealthBarBg.width / 2; bossHealthBar.y = 50; bossHealthBarBg.visible = false; bossHealthBar.visible = false; game.addChild(bossHealthBarBg); game.addChild(bossHealthBar); // Tutorial text (hidden initially) tutorialText = new Text2('', { size: 70, fill: 0xFFFFFF }); tutorialText.anchor.set(0.5, 0.5); tutorialText.x = 2048 / 2; tutorialText.y = 2732 / 2; tutorialText.visible = false; game.addChild(tutorialText); } function showTutorial() { gameState = 'tutorial'; tutorialText.visible = true; var tutorialMessages = ["Welcome to Boss Blitz!\n\nUse the arrow buttons to move your ship.", "Tap anywhere to shoot.\n\nDestroy the bosses to progress.", "Collect power-ups dropped by bosses\nto gain special abilities.", "Survive long enough to defeat\nall boss levels.", "Good luck!\n\nTap to begin..."]; tutorialText.setText(tutorialMessages[tutorialStep]); // Make tutorial text pulsate tween(tutorialText, { alpha: 0.7 }, { duration: 1000, easing: tween.sinceOut, onFinish: function onFinish() { tween(tutorialText, { alpha: 1 }, { duration: 1000, easing: tween.sinceIn }); } }); } function advanceTutorial() { tutorialStep++; if (tutorialStep >= 5) { // End tutorial tutorialText.visible = false; // Remove skip tutorial button if it exists if (game.skipTutorialButton) { game.removeChild(game.skipTutorialButton); game.removeChild(game.skipTutorialText); game.skipTutorialButton = null; game.skipTutorialText = null; } startLevel(); } else { showTutorial(); } } function startLevel() { gameState = 'playing'; // Reset player position player.x = 2048 / 2; player.y = 2732 - 200; // Clear any existing projectiles clearProjectiles(); // Create boss based on current level var bossType; if (currentLevel === 1) { bossType = 'miniBoss1'; } else if (currentLevel === 2) { bossType = 'miniBoss2'; } else if (currentLevel === 3) { bossType = 'boss1'; } else if (currentLevel === 4) { bossType = 'boss2'; } else { bossType = 'boss3'; } currentBoss = new Boss(bossType, currentLevel); currentBoss.x = 2048 / 2; currentBoss.y = -200; // Start above screen game.addChild(currentBoss); // Show boss health bar bossHealthBarBg.visible = true; bossHealthBar.visible = true; // Update UI updateScore(); levelText.setText('LEVEL ' + currentLevel); } function updateScore() { scoreText.setText('SCORE: ' + LK.getScore()); } function clearProjectiles() { // Remove all bullets, lasers, and powerups bullets.forEach(function (bullet) { bullet.parent.removeChild(bullet); }); bullets = []; lasers.forEach(function (laser) { laser.parent.removeChild(laser); }); lasers = []; playerBullets.forEach(function (bullet) { bullet.parent.removeChild(bullet); }); playerBullets = []; powerups.forEach(function (powerup) { powerup.parent.removeChild(powerup); }); powerups = []; } function checkCollisions() { // Player bullets hitting boss for (var i = playerBullets.length - 1; i >= 0; i--) { var bullet = playerBullets[i]; if (bullet.intersects(currentBoss)) { // Boss hit var bossDead = currentBoss.takeDamage(bullet.damage); // Remove bullet bullet.parent.removeChild(bullet); playerBullets.splice(i, 1); // Update boss health bar updateBossHealthBar(); // Check if boss defeated if (bossDead) { handleBossDefeat(); } } } // Enemy bullets hitting player for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; if (bullet.intersects(player)) { // Player hit var playerDied = player.takeDamage(bullet.damage); // Remove bullet bullet.parent.removeChild(bullet); bullets.splice(i, 1); // Update health bar updatePlayerHealthBar(); // Check if player died if (playerDied) { handlePlayerDefeat(); } } } // Lasers hitting player for (var i = lasers.length - 1; i >= 0; i--) { var laser = lasers[i]; // Only check active lasers (not in warning phase) if (laser.counter >= laser.warningCounter && laser.intersects(player)) { // Player hit var playerDied = player.takeDamage(laser.damage); // Update health bar updatePlayerHealthBar(); // Check if player died if (playerDied) { handlePlayerDefeat(); } // Don't remove laser on hit, it persists } } // Power-ups being collected by player for (var i = powerups.length - 1; i >= 0; i--) { var powerup = powerups[i]; if (powerup.intersects(player)) { // Apply powerup effect powerup.applyEffect(player); // Remove powerup powerup.parent.removeChild(powerup); powerups.splice(i, 1); } } } function updatePlayerHealthBar() { // Update width of health bar based on player health percentage var healthPercent = player.health / player.maxHealth; playerHealthBar.width = playerHealthBarBg.width * healthPercent; // Change color based on health remaining if (healthPercent > 0.6) { playerHealthBar.tint = 0x34A853; // Green } else if (healthPercent > 0.3) { playerHealthBar.tint = 0xFBBC05; // Yellow } else { playerHealthBar.tint = 0xEA4335; // Red } } function updateBossHealthBar() { // Update width of health bar based on boss health percentage var healthPercent = currentBoss.health / currentBoss.maxHealth; bossHealthBar.width = bossHealthBarBg.width * healthPercent; // Change color based on health remaining if (healthPercent > 0.6) { bossHealthBar.tint = 0xEA4335; // Red } else if (healthPercent > 0.3) { bossHealthBar.tint = 0xFF5733; // Orange } else { bossHealthBar.tint = 0xC70039; // Dark red } } function handleBossDefeat() { gameState = 'bossDead'; // Play defeat sound LK.getSound('bossDefeat').play(); // Flash screen LK.effects.flashScreen(0xFFFFFF, 500); // Create explosion effect at boss position LK.effects.flashObject(currentBoss, 0xFFFFFF, 1000); LK.getSound('explosion').play(); // Remove boss LK.setTimeout(function () { game.removeChild(currentBoss); currentBoss = null; // Hide boss health bar bossHealthBarBg.visible = false; bossHealthBar.visible = false; // Spawn powerups spawnPowerups(); // Award score based on level var scoreIncrease = currentLevel * 1000; LK.setScore(LK.getScore() + scoreIncrease); updateScore(); // Show level complete message tutorialText.setText("LEVEL " + currentLevel + " COMPLETE!\n\nTap to continue..."); tutorialText.visible = true; // Make text appear with effect tutorialText.alpha = 0; tween(tutorialText, { alpha: 1 }, { duration: 500, easing: tween.easeOut }); }, 1000); } function spawnPowerups() { // Spawn 1-3 random powerups var count = Math.floor(Math.random() * 3) + 1; var types = ['shield', 'doubleDamage', 'tripleShot']; for (var i = 0; i < count; i++) { var type = types[Math.floor(Math.random() * types.length)]; var powerup = new PowerUp(type); // Position near where boss was defeated powerup.x = 2048 / 2 + (Math.random() * 400 - 200); powerup.y = 300 + (Math.random() * 200 - 100); game.addChild(powerup); powerups.push(powerup); } } function advanceToNextLevel() { currentLevel++; storage.currentLevel = currentLevel; // Update max level reached if (currentLevel > storage.maxLevelReached) { storage.maxLevelReached = currentLevel; } // Hide tutorial text tutorialText.visible = false; // Start new level startLevel(); } function handlePlayerDefeat() { gameState = 'gameOver'; // Play explosion LK.getSound('explosion').play(); // Flash screen red LK.effects.flashScreen(0xFF0000, 1000); // Show game over message LK.setTimeout(function () { LK.showGameOver(); }, 1500); } function playerShoot() { if (gameState !== 'playing') return; var bullets = player.shoot(); if (bullets) { bullets.forEach(function (bullet) { game.addChild(bullet); playerBullets.push(bullet); }); } } // Event handlers function handleDown(x, y, obj) { // Handle tutorial advancement if (gameState === 'tutorial') { advanceTutorial(); return; } // Handle level advancement after boss defeat if (gameState === 'bossDead' && tutorialText.visible) { advanceToNextLevel(); return; } // In playing state, any tap triggers shooting if (gameState === 'playing') { // Check if the tap is on any of the control buttons var isOnControlButton = false; for (var i = 0; i < controlButtons.length; i++) { if (obj === controlButtons[i]) { isOnControlButton = true; break; } } // Only shoot if not tapping a control button if (!isOnControlButton) { playerShoot(); } } } function handleMove(x, y, obj) { // No longer need to handle dragging movement } function handleUp(x, y, obj) { // No longer need to handle dragging release } game.down = handleDown; game.move = handleMove; game.up = handleUp; // Main game loop game.update = function () { // Skip updates for non-playing states if (gameState === 'tutorial' || gameState === 'gameOver' || gameState === 'paused') { return; } // Auto-shoot for player if (gameState === 'playing' && LK.ticks % 30 === 0) { playerShoot(); } // Update player player.update(); // Update boss if (currentBoss && gameState === 'playing') { currentBoss.update(); } // Update projectiles for (var i = playerBullets.length - 1; i >= 0; i--) { var bullet = playerBullets[i]; bullet.update(); if (bullet.shouldRemove) { bullet.parent.removeChild(bullet); playerBullets.splice(i, 1); } } for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; bullet.update(); if (bullet.shouldRemove) { bullet.parent.removeChild(bullet); bullets.splice(i, 1); } } for (var i = lasers.length - 1; i >= 0; i--) { var laser = lasers[i]; laser.update(); if (laser.shouldRemove) { laser.parent.removeChild(laser); lasers.splice(i, 1); } } for (var i = powerups.length - 1; i >= 0; i--) { var powerup = powerups[i]; powerup.update(); if (powerup.shouldRemove) { powerup.parent.removeChild(powerup); powerups.splice(i, 1); } } // Check for collisions if (gameState === 'playing') { checkCollisions(); } // Update UI elements updatePlayerHealthBar(); if (currentBoss) { updateBossHealthBar(); } }; // Initialize game initGame(); function createControlButtons() { // Create the direction buttons var btnSize = 150; // Increased from 120 to 150 var spacing = 25; // Slightly increased spacing // Create buttons for each direction var upButton = new ControlButton('up'); var downButton = new ControlButton('down'); var leftButton = new ControlButton('left'); var rightButton = new ControlButton('right'); // Position buttons based on user preference var baseX = storage.controlsOnRight ? 2048 * 3 / 4 : 2048 / 4; // Position the buttons in a D-pad layout upButton.x = baseX; upButton.y = 2732 - 3 * btnSize - 2 * spacing; downButton.x = baseX; downButton.y = 2732 - btnSize - spacing; leftButton.x = baseX - btnSize - spacing; leftButton.y = 2732 - 2 * btnSize - 1.5 * spacing; rightButton.x = baseX + btnSize + spacing; rightButton.y = 2732 - 2 * btnSize - 1.5 * spacing; // Add buttons to the game game.addChild(upButton); game.addChild(downButton); game.addChild(leftButton); game.addChild(rightButton); // Store buttons for reference controlButtons = [upButton, downButton, leftButton, rightButton]; } // Function to reposition control buttons when settings change function repositionControlButtons() { if (!controlButtons || controlButtons.length < 4) return; var btnSize = 150; var spacing = 25; var baseX = storage.controlsOnRight ? 2048 * 3 / 4 : 2048 / 4; // Update positions controlButtons[0].x = baseX; // up controlButtons[1].x = baseX; // down controlButtons[2].x = baseX - btnSize - spacing; // left controlButtons[3].x = baseX + btnSize + spacing; // right } // Function to handle player movement based on button presses function movePlayer(direction, isPressed) { if (!player || gameState !== 'playing') return; // Update the movement state for the player player.moving[direction] = isPressed; } // Pause the game and save previous state function pauseGame() { if (gameState !== 'playing') return; // Store previous state var previousState = gameState; gameState = 'paused'; // Store this for when we resume gameState.previousState = previousState; } // Resume the game function resumeGame() { if (gameState !== 'paused') return; // Restore previous state gameState = gameState.previousState || 'playing'; delete gameState.previousState; } // Show settings panel function showSettings() { var settingsPanel = new SettingsPanel(); settingsPanel.x = 2048 / 2; settingsPanel.y = 2732 / 2; game.addChild(settingsPanel); } // Skip tutorial button removed ;
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
currentLevel: 1,
maxLevelReached: 1,
playerPowerups: {},
controlsOnRight: false
});
/****
* Classes
****/
var Boss = Container.expand(function (type, level) {
var self = Container.call(this);
self.type = type || 'miniBoss1';
self.level = level || 1;
var bossGraphics = self.attachAsset(self.type, {
anchorX: 0.5,
anchorY: 0.5
});
// Boss stats scale with level
// Reduced health for minibosses, normal health for main bosses
self.health = self.type.includes('miniBoss') ? 75 * self.level : 100 * self.level;
self.maxHealth = self.health;
self.speed = 3 + self.level * 0.5;
self.shootCooldown = 0;
// Longer delay between shots for minibosses
self.shootDelay = self.type.includes('miniBoss') ? Math.max(40 - self.level * 5, 15) : Math.max(30 - self.level * 5, 10);
self.attackPattern = 0;
self.phaseCounter = 0;
self.phases = ['entry', 'attack1', 'movement', 'attack2', 'rage'];
self.currentPhase = 'entry';
self.phaseTimer = 180; // 3 seconds per phase
self.moveDirection = 1;
self.targetX = 2048 / 2;
self.targetY = 300;
// Define movement patterns based on boss type
self.patterns = {
miniBoss1: {
entry: function entry() {
if (self.y < self.targetY) {
self.y += self.speed;
} else {
self.changePhase('attack1');
}
},
attack1: function attack1() {
// Simple straight shots
if (self.shootCooldown <= 0) {
self.shootBullets();
self.shootCooldown = self.shootDelay * 1.5; // Increased delay for easier gameplay
}
// Move side to side
self.x += self.speed * self.moveDirection;
if (self.x > 1800 || self.x < 248) {
self.moveDirection *= -1;
}
},
movement: function movement() {
// Circle pattern
self.x = self.targetX + Math.cos(self.phaseCounter / 30) * 300;
self.y = self.targetY + Math.sin(self.phaseCounter / 30) * 100;
},
attack2: function attack2() {
// Spread shots
if (self.shootCooldown <= 0) {
self.shootSpread(5, 0.2);
self.shootCooldown = self.shootDelay * 2;
}
},
rage: function rage() {
// Fast movements and frequent attacks
self.x += self.speed * 1.5 * self.moveDirection;
if (self.x > 1800 || self.x < 248) {
self.moveDirection *= -1;
}
if (self.shootCooldown <= 0) {
self.shootSpread(3, 0.3);
self.shootCooldown = self.shootDelay / 2;
}
}
},
miniBoss2: {
entry: function entry() {
if (self.y < self.targetY) {
self.y += self.speed;
} else {
self.changePhase('attack1');
}
},
attack1: function attack1() {
// Laser attacks
if (self.shootCooldown <= 0) {
self.shootLaser();
self.shootCooldown = self.shootDelay * 3;
}
// Slow tracking
if (player && self.phaseCounter % 30 === 0) {
self.targetX = player.x;
}
self.x += (self.targetX - self.x) * 0.02;
},
movement: function movement() {
// Quick dash to one side then the other
if (self.phaseCounter < self.phaseTimer / 2) {
self.x += self.speed * 2;
} else {
self.x -= self.speed * 2;
}
// Keep in bounds
self.x = Math.max(200, Math.min(1848, self.x));
},
attack2: function attack2() {
// Circular bullet pattern
if (self.shootCooldown <= 0) {
self.shootCircle(8);
self.shootCooldown = self.shootDelay * 2;
}
},
rage: function rage() {
// Erratic movement and mixed attacks
self.x = self.targetX + Math.sin(self.phaseCounter / 10) * 300;
if (self.shootCooldown <= 0) {
if (self.phaseCounter % 120 < 60) {
self.shootSpread(7, 0.15);
} else {
self.shootLaser();
}
self.shootCooldown = self.shootDelay;
}
}
},
boss1: {
// More complex patterns for main bosses
entry: function entry() {
if (self.y < self.targetY) {
self.y += self.speed;
} else {
self.changePhase('attack1');
}
},
attack1: function attack1() {
// Wave pattern with bursts of bullets
self.x = self.targetX + Math.sin(self.phaseCounter / 20) * 400;
if (self.shootCooldown <= 0) {
if (self.phaseCounter % 60 < 30) {
self.shootBullets();
} else {
self.shootSpread(3, 0.2);
}
self.shootCooldown = self.shootDelay;
}
},
movement: function movement() {
// Quick charge toward player
if (player && self.phaseCounter % 60 === 0) {
self.targetX = player.x;
self.targetY = player.y - 300;
}
self.x += (self.targetX - self.x) * 0.05;
self.y += (self.targetY - self.y) * 0.05;
// Don't get too close to player
self.y = Math.min(self.y, 500);
},
attack2: function attack2() {
// Laser attacks with bullet spray
if (self.shootCooldown <= 0) {
if (self.phaseCounter % 120 < 60) {
self.shootLaser();
} else {
self.shootCircle(12);
}
self.shootCooldown = self.shootDelay * 1.5;
}
// Slow side-to-side movement
self.x += Math.sin(self.phaseCounter / 30) * 5;
},
rage: function rage() {
// Fast erratic movement with constant attacks
self.x = self.targetX + Math.sin(self.phaseCounter / 10) * 300 + Math.cos(self.phaseCounter / 15) * 200;
self.y = self.targetY + Math.sin(self.phaseCounter / 12) * 100;
if (self.shootCooldown <= 0) {
// Alternate between different attack patterns
switch (Math.floor(self.phaseCounter / 40) % 3) {
case 0:
self.shootSpread(5, 0.15);
break;
case 1:
self.shootLaser();
break;
case 2:
self.shootCircle(8);
break;
}
self.shootCooldown = self.shootDelay * 0.7;
}
}
},
boss2: {
// Similar structure, different patterns
entry: function entry() {
if (self.y < self.targetY) {
self.y += self.speed;
} else {
self.changePhase('attack1');
}
},
attack1: function attack1() {
// Spiral bullet pattern
if (self.shootCooldown <= 0) {
self.shootSpiral(self.phaseCounter / 10);
self.shootCooldown = Math.max(5, self.shootDelay / 2);
}
// Slow rotation around center
self.x = self.targetX + Math.cos(self.phaseCounter / 60) * 200;
self.y = self.targetY + Math.sin(self.phaseCounter / 60) * 100;
},
movement: function movement() {
// Quick teleport movements
if (self.phaseCounter % 60 === 0) {
// Flash effect for teleport
LK.effects.flashObject(self, 0xFFFFFF, 200);
// Choose random position in top third of screen
self.targetX = 300 + Math.random() * (2048 - 600);
self.targetY = 150 + Math.random() * 250;
}
// Quick movement to target
self.x += (self.targetX - self.x) * 0.1;
self.y += (self.targetY - self.y) * 0.1;
},
attack2: function attack2() {
// Multiple lasers
if (self.shootCooldown <= 0) {
for (var i = 0; i < 3; i++) {
var offset = (i - 1) * 200;
self.shootLaser(offset);
}
self.shootCooldown = self.shootDelay * 2;
}
// Small jittery movements
self.x += (Math.random() - 0.5) * 10;
self.y += (Math.random() - 0.5) * 5;
// Keep in bounds
self.x = Math.max(200, Math.min(1848, self.x));
self.y = Math.max(100, Math.min(500, self.y));
},
rage: function rage() {
// Screen-filling attacks and fast movement
if (self.shootCooldown <= 0) {
// Alternate between massive spreads and laser curtains
if (self.phaseCounter % 180 < 90) {
self.shootSpread(15, 0.1);
} else {
for (var i = 0; i < 5; i++) {
var offset = (i - 2) * 150;
self.shootLaser(offset);
}
}
self.shootCooldown = self.shootDelay * 0.6;
}
// Aggressive tracking of player
if (player) {
self.targetX = player.x;
self.x += (self.targetX - self.x) * 0.03;
}
// Vertical bobbing
self.y = self.targetY + Math.sin(self.phaseCounter / 20) * 100;
}
},
boss3: {
// More extreme patterns
entry: function entry() {
if (self.y < self.targetY) {
self.y += self.speed;
} else {
self.changePhase('attack1');
}
},
attack1: function attack1() {
// 360-degree bullet hell
if (self.shootCooldown <= 0) {
self.shootCircle(16);
self.shootCooldown = self.shootDelay;
}
// Slow pulsing movement
self.x = self.targetX + Math.sin(self.phaseCounter / 30) * 300;
self.y = self.targetY + Math.cos(self.phaseCounter / 30) * 100;
},
movement: function movement() {
// Aggressive dash toward player position then retreat
if (self.phaseCounter % 90 < 45 && player) {
// Dash toward player
self.targetX = player.x;
self.targetY = player.y - 150;
self.x += (self.targetX - self.x) * 0.08;
self.y += (self.targetY - self.y) * 0.08;
} else {
// Retreat to top
self.targetY = 250;
self.y += (self.targetY - self.y) * 0.05;
}
},
attack2: function attack2() {
// Multiple attack types simultaneously
if (self.shootCooldown <= 0) {
// Lasers + bullets combo
self.shootLaser(0);
self.shootSpread(9, 0.15);
self.shootCooldown = self.shootDelay * 1.2;
}
// Side-to-side sweep
self.x += self.speed * 2 * self.moveDirection;
if (self.x > 1800 || self.x < 248) {
self.moveDirection *= -1;
}
},
rage: function rage() {
// Total bullet hell mode
if (self.shootCooldown <= 0) {
// Spiral + lasers + directed shots
self.shootSpiral(self.phaseCounter / 5);
if (self.phaseCounter % 60 === 0) {
// Multiple lasers in fan pattern
for (var i = -2; i <= 2; i++) {
self.shootLaser(i * 120);
}
}
// Direct shots at player
if (player && self.phaseCounter % 30 === 0) {
self.shootAtPlayer();
}
self.shootCooldown = Math.max(3, self.shootDelay / 3);
}
// Chaotic movement
self.x = self.targetX + Math.sin(self.phaseCounter / 10) * 400 + Math.cos(self.phaseCounter / 7) * 200;
self.y = self.targetY + Math.sin(self.phaseCounter / 8) * 150;
}
}
};
// Shooting methods
self.shootBullets = function () {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + 50;
bullet.angle = Math.PI / 2; // Straight down
bullets.push(bullet);
game.addChild(bullet);
};
self.shootSpread = function (count, spreadAngle) {
var startAngle = Math.PI / 2 - spreadAngle * (count - 1) / 2;
for (var i = 0; i < count; i++) {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + 50;
bullet.angle = startAngle + spreadAngle * i;
bullets.push(bullet);
game.addChild(bullet);
}
};
self.shootCircle = function (count) {
var angleStep = Math.PI * 2 / count;
for (var i = 0; i < count; i++) {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.angle = angleStep * i;
bullets.push(bullet);
game.addChild(bullet);
}
};
self.shootSpiral = function (baseAngle) {
var count = 8;
var angleStep = Math.PI * 2 / count;
for (var i = 0; i < count; i++) {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.angle = baseAngle + angleStep * i;
bullets.push(bullet);
game.addChild(bullet);
}
};
self.shootLaser = function (xOffset) {
xOffset = xOffset || 0;
var laser = new EnemyLaser();
laser.x = self.x + xOffset;
laser.y = self.y;
lasers.push(laser);
game.addChild(laser);
};
self.shootAtPlayer = function () {
if (!player) return;
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + 50;
// Calculate angle to player
var dx = player.x - self.x;
var dy = player.y - self.y;
bullet.angle = Math.atan2(dy, dx);
bullets.push(bullet);
game.addChild(bullet);
};
self.changePhase = function (newPhase) {
self.currentPhase = newPhase;
self.phaseCounter = 0;
// Adjust phase timer based on phase
if (newPhase === 'rage') {
self.phaseTimer = 360; // Longer rage phase
} else {
self.phaseTimer = 180; // Normal phases
}
};
self.takeDamage = function (damage) {
self.health -= damage;
// Flash boss when hit
LK.effects.flashObject(bossGraphics, 0xFFFFFF, 200);
LK.getSound('bossHit').play();
// Check for phase change based on health percentage
var healthPercent = self.health / self.maxHealth;
if (healthPercent <= 0.25 && self.currentPhase !== 'rage') {
self.changePhase('rage');
} else if (healthPercent <= 0.5 && self.currentPhase !== 'rage' && self.currentPhase !== 'attack2') {
self.changePhase('attack2');
} else if (healthPercent <= 0.75 && self.currentPhase !== 'rage' && self.currentPhase !== 'attack2' && self.currentPhase !== 'movement') {
self.changePhase('movement');
}
return self.health <= 0;
};
self.update = function () {
// Update based on current phase
if (self.patterns[self.type] && self.patterns[self.type][self.currentPhase]) {
self.patterns[self.type][self.currentPhase]();
}
// Update counters
self.phaseCounter++;
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
// Check for phase timeout
if (self.phaseCounter >= self.phaseTimer && self.currentPhase !== 'rage' && self.health > self.maxHealth * 0.25) {
// Cycle to next phase
var currentPhaseIndex = self.phases.indexOf(self.currentPhase);
var nextPhaseIndex = (currentPhaseIndex + 1) % (self.phases.length - 1); // Skip rage phase
self.changePhase(self.phases[nextPhaseIndex]);
}
};
return self;
});
var ControlButton = Container.expand(function (direction) {
var self = Container.call(this);
// Different color for each direction button
var color;
switch (direction) {
case 'up':
color = 0x4285F4;
break;
// Blue
case 'down':
color = 0xEA4335;
break;
// Red
case 'left':
color = 0xFBBC05;
break;
// Yellow
case 'right':
color = 0x34A853;
break;
// Green
default:
color = 0xFFFFFF;
// White
}
// Create button shape
var buttonGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
// Set button color
buttonGraphics.tint = color;
// Arrow display (using text as a simple way to show direction)
var arrowSymbol;
switch (direction) {
case 'up':
arrowSymbol = '▲';
break;
case 'down':
arrowSymbol = '▼';
break;
case 'left':
arrowSymbol = '◀';
break;
case 'right':
arrowSymbol = '▶';
break;
default:
arrowSymbol = '•';
}
var arrow = new Text2(arrowSymbol, {
size: 60,
fill: 0xFFFFFF
});
arrow.anchor.set(0.5, 0.5);
self.addChild(arrow);
// Store direction for later use
self.direction = direction;
// Handle button press
self.down = function (x, y, obj) {
// Visual feedback - shrink slightly when pressed
buttonGraphics.scale.set(1.1);
// Start moving in this direction
if (player && gameState === 'playing') {
movePlayer(self.direction, true);
}
};
// Handle button release
self.up = function (x, y, obj) {
// Visual feedback - return to normal size
buttonGraphics.scale.set(1.2);
// Stop moving in this direction
if (player && gameState === 'playing') {
movePlayer(self.direction, false);
}
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = 1;
self.angle = 0; // Direction in radians
self.update = function () {
self.x += Math.cos(self.angle) * self.speed;
self.y += Math.sin(self.angle) * self.speed;
// Remove if off screen
if (self.y > 2732 + 50 || self.y < -50 || self.x > 2048 + 50 || self.x < -50) {
self.shouldRemove = true;
}
};
return self;
});
var EnemyLaser = Container.expand(function () {
var self = Container.call(this);
var laserGraphics = self.attachAsset('enemyLaser', {
anchorX: 0.5,
anchorY: 0
});
self.damage = 2;
self.duration = 120; // 2 seconds at 60fps
self.counter = 0;
self.warningCounter = 60; // 1 second warning
// Start with low alpha for warning
laserGraphics.alpha = 0.3;
self.update = function () {
self.counter++;
if (self.counter < self.warningCounter) {
// Warning phase
if (self.counter % 10 < 5) {
laserGraphics.alpha = 0.5;
} else {
laserGraphics.alpha = 0.3;
}
} else if (self.counter === self.warningCounter) {
// Activate laser
laserGraphics.alpha = 0.8;
// Expand width
tween(laserGraphics, {
width: 80
}, {
duration: 100
});
} else if (self.counter >= self.duration) {
// Remove laser
self.shouldRemove = true;
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 10;
self.shootCooldown = 0;
self.shootDelay = 15; // 4 shots per second
self.health = 100;
self.maxHealth = 100;
self.invincible = false;
self.invincibleTimer = 0;
self.shield = null;
self.powerups = {};
// Track movement directions
self.moving = {
up: false,
down: false,
left: false,
right: false
};
// Initialize shield (hidden at first)
self.createShield = function () {
if (self.shield) return;
self.shield = self.attachAsset('playerShield', {
anchorX: 0.5,
anchorY: 0.5
});
self.shield.alpha = 0;
};
self.activateShield = function (duration) {
if (!self.shield) self.createShield();
self.shield.alpha = 0.7;
self.invincible = true;
// Fade out shield over duration
tween(self.shield, {
alpha: 0
}, {
duration: duration,
onFinish: function onFinish() {
self.invincible = false;
}
});
};
self.takeDamage = function (damage) {
if (self.invincible) return false;
self.health -= damage;
if (self.health <= 0) {
self.health = 0;
return true; // Player died
}
// Flash player and make invincible briefly
LK.effects.flashObject(playerGraphics, 0xFF0000, 500);
self.invincible = true;
self.invincibleTimer = 60; // 1 second invincibility
LK.getSound('playerHit').play();
return false; // Player alive
};
self.shoot = function () {
if (self.shootCooldown > 0) return null;
var bullet = new PlayerBullet();
bullet.x = self.x;
bullet.y = self.y - 40;
// Apply powerups
if (self.powerups.doubleDamage) {
bullet.damage *= 2;
bullet.tint = 0xFFFF00; // Yellow for double damage
}
// Triple shot powerup
if (self.powerups.tripleShot) {
// Create two additional bullets
var bulletLeft = new PlayerBullet();
bulletLeft.x = self.x - 30;
bulletLeft.y = self.y - 20;
var bulletRight = new PlayerBullet();
bulletRight.x = self.x + 30;
bulletRight.y = self.y - 20;
self.shootCooldown = self.shootDelay;
LK.getSound('playerShoot').play();
return [bullet, bulletLeft, bulletRight];
}
self.shootCooldown = self.shootDelay;
LK.getSound('playerShoot').play();
return [bullet];
};
self.update = function () {
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
if (self.invincible && self.invincibleTimer > 0) {
self.invincibleTimer--;
if (self.invincibleTimer <= 0) {
self.invincible = false;
}
}
// Handle movement based on pressed direction buttons
if (gameState === 'playing') {
if (self.moving.up) {
self.y -= self.speed;
}
if (self.moving.down) {
self.y += self.speed;
}
if (self.moving.left) {
self.x -= self.speed;
}
if (self.moving.right) {
self.x += self.speed;
}
// Keep player within screen bounds
self.x = Math.max(50, Math.min(2048 - 50, self.x));
self.y = Math.max(100, Math.min(2732 - 100, self.y));
}
};
self.down = function (x, y, obj) {
// This is handled in the game's touch handler
};
self.up = function (x, y, obj) {
// This is handled in the game's touch handler
};
self.createShield();
return self;
});
var PlayerBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
self.speed = 15;
self.damage = 2; // Increased bullet damage to make bosses easier to defeat
self.update = function () {
self.y -= self.speed;
// Remove if off screen
if (self.y < -50) {
self.shouldRemove = true;
}
};
return self;
});
var PowerUp = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'shield';
var color;
switch (self.type) {
case 'shield':
color = 0x00FFFF;
break;
case 'doubleDamage':
color = 0xFFFF00;
break;
case 'tripleShot':
color = 0xFF00FF;
break;
default:
color = 0xFFFFFF;
}
var powerupGraphics = self.attachAsset('POWERUP', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1,
scaleY: 1
});
// Set color based on powerup type
powerupGraphics.tint = color;
self.speed = 3;
self.duration = type === 'shield' ? 300 : 600; // Shield lasts 5 seconds, others 10
self.update = function () {
self.y += self.speed;
// Remove if off screen
if (self.y > 2732 + 50) {
self.shouldRemove = true;
}
};
self.applyEffect = function (player) {
LK.getSound('powerUp').play();
switch (self.type) {
case 'shield':
player.activateShield(5000); // 5 seconds
break;
case 'doubleDamage':
player.powerups.doubleDamage = true;
// Clear previous timeout if exists
if (player.powerups.doubleDamageTimer) {
LK.clearTimeout(player.powerups.doubleDamageTimer);
}
// Set timeout to clear powerup
player.powerups.doubleDamageTimer = LK.setTimeout(function () {
player.powerups.doubleDamage = false;
}, 10000); // 10 seconds
break;
case 'tripleShot':
player.powerups.tripleShot = true;
// Clear previous timeout if exists
if (player.powerups.tripleShotTimer) {
LK.clearTimeout(player.powerups.tripleShotTimer);
}
// Set timeout to clear powerup
player.powerups.tripleShotTimer = LK.setTimeout(function () {
player.powerups.tripleShot = false;
}, 10000); // 10 seconds
break;
}
self.shouldRemove = true;
};
return self;
});
var SettingsPanel = Container.expand(function () {
var self = Container.call(this);
// Semi-transparent black background
var background = LK.getAsset('bossHealthBarBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 10,
scaleY: 20
});
background.tint = 0x000000;
background.alpha = 0.8;
self.addChild(background);
// Title
var titleText = new Text2('SETTINGS', {
size: 100,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0);
titleText.y = -900;
self.addChild(titleText);
// Control layout option
var controlsText = new Text2('CONTROLS LAYOUT:', {
size: 70,
fill: 0xFFFFFF
});
controlsText.anchor.set(0.5, 0.5);
controlsText.y = -700;
self.addChild(controlsText);
// Left hand option
var leftHandButton = LK.getAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 1.5
});
leftHandButton.tint = storage.controlsOnRight ? 0x666666 : 0x4285F4;
leftHandButton.x = -250;
leftHandButton.y = -550;
self.addChild(leftHandButton);
var leftHandText = new Text2('LEFT HAND', {
size: 50,
fill: 0xFFFFFF
});
leftHandText.anchor.set(0.5, 0.5);
leftHandText.x = -250;
leftHandText.y = -550;
self.addChild(leftHandText);
// Right hand option
var rightHandButton = LK.getAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 1.5
});
rightHandButton.tint = storage.controlsOnRight ? 0x4285F4 : 0x666666;
rightHandButton.x = 250;
rightHandButton.y = -550;
self.addChild(rightHandButton);
var rightHandText = new Text2('RIGHT HAND', {
size: 50,
fill: 0xFFFFFF
});
rightHandText.anchor.set(0.5, 0.5);
rightHandText.x = 250;
rightHandText.y = -550;
self.addChild(rightHandText);
// No skip tutorial option - removed
// Reset to Level 1 button
var resetButton = LK.getAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 1.5
});
resetButton.tint = 0x34A853; // Green color
resetButton.y = 0;
self.addChild(resetButton);
var resetText = new Text2('RESTART LEVEL 1', {
size: 40,
fill: 0xFFFFFF
});
resetText.anchor.set(0.5, 0.5);
resetText.y = 0;
self.addChild(resetText);
// Close button
var closeButton = LK.getAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 1.5
});
closeButton.tint = 0xEA4335;
closeButton.y = 200;
self.addChild(closeButton);
var closeText = new Text2('CLOSE', {
size: 50,
fill: 0xFFFFFF
});
closeText.anchor.set(0.5, 0.5);
closeText.y = 200;
self.addChild(closeText);
// Button interaction handlers
leftHandButton.interactive = true;
leftHandButton.down = function () {
if (storage.controlsOnRight) {
storage.controlsOnRight = false;
leftHandButton.tint = 0x4285F4;
rightHandButton.tint = 0x666666;
if (typeof repositionControlButtons === 'function') {
repositionControlButtons();
}
}
};
rightHandButton.interactive = true;
rightHandButton.down = function () {
if (!storage.controlsOnRight) {
storage.controlsOnRight = true;
rightHandButton.tint = 0x4285F4;
leftHandButton.tint = 0x666666;
if (typeof repositionControlButtons === 'function') {
repositionControlButtons();
}
}
};
// Reset level button handler
resetButton.interactive = true;
resetButton.down = function () {
// Set current level to 1
storage.currentLevel = 1;
currentLevel = 1;
// Hide settings panel
if (self.parent) {
self.parent.removeChild(self);
}
// Reset game state
if (gameState === 'paused') {
gameState = 'playing';
}
// Clear projectiles and restart level 1
clearProjectiles();
// Remove current boss if exists
if (currentBoss && currentBoss.parent) {
currentBoss.parent.removeChild(currentBoss);
currentBoss = null;
}
// Flash screen for feedback
LK.effects.flashScreen(0xFFFFFF, 500);
// Start from level 1
startLevel();
};
// No skip tutorial button handler - removed
closeButton.interactive = true;
closeButton.down = function () {
if (self.parent) {
self.parent.removeChild(self);
if (gameState === 'paused') {
resumeGame();
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Core game variables
// Player, bosses, projectiles, UI elements, and effects
// Mini bosses
// Main bosses
// Boss projectiles
// Health bars and UI
// Sound effects
// Background music
var player;
var currentBoss;
var bullets = [];
var lasers = [];
var playerBullets = [];
var powerups = [];
var currentLevel = storage.currentLevel || 1;
var gameState = 'tutorial'; // tutorial, playing, bossDead, gameOver
var tutorialStep = 0;
var lastShootTime = 0;
var controlButtons = [];
// UI Elements
var scoreText;
var levelText;
var playerHealthBar;
var playerHealthBarBg;
var bossHealthBar;
var bossHealthBarBg;
var tutorialText;
// Initialize game
function initGame() {
// Background
game.setBackgroundColor(0x111133);
// Create player
player = new Player();
player.x = 2048 / 2;
player.y = 2732 - 200;
game.addChild(player);
// Initialize UI
createUI();
// Always start with tutorial if first level
if (currentLevel === 1) {
showTutorial();
} else {
startLevel();
}
// Play background music
LK.playMusic('battleMusic');
}
function createUI() {
// Score and level display
scoreText = new Text2('SCORE: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
LK.gui.top.addChild(scoreText);
scoreText.x = 150;
scoreText.y = 30;
levelText = new Text2('LEVEL ' + currentLevel, {
size: 60,
fill: 0xFFFFFF
});
levelText.anchor.set(1, 0);
LK.gui.topRight.addChild(levelText);
levelText.x = -50;
levelText.y = 30;
// Add settings button in top right corner
var settingsButton = LK.getAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1,
scaleY: 1
});
settingsButton.tint = 0x999999;
settingsButton.x = 2048 - 100; // Position in top right
settingsButton.y = 100; // Position in top right
game.addChild(settingsButton);
var settingsText = new Text2('⚙️', {
size: 60,
fill: 0xFFFFFF
});
settingsText.anchor.set(0.5, 0.5);
settingsText.x = 2048 - 100; // Match the button position
settingsText.y = 100; // Match the button position
game.addChild(settingsText);
// Make settings button interactive
settingsButton.interactive = true;
settingsButton.down = function () {
if (gameState !== 'paused') {
pauseGame();
showSettings();
}
};
// Create directional control buttons
createControlButtons();
// Player health bar
playerHealthBarBg = LK.getAsset('healthBarBackground', {
anchorX: 0,
anchorY: 0.5
});
playerHealthBar = LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0.5
});
playerHealthBarBg.x = 50;
playerHealthBarBg.y = 100;
playerHealthBar.x = 50;
playerHealthBar.y = 100;
game.addChild(playerHealthBarBg);
game.addChild(playerHealthBar);
// Boss health bar (hidden initially)
bossHealthBarBg = LK.getAsset('bossHealthBarBackground', {
anchorX: 0.5,
anchorY: 0.5
});
bossHealthBar = LK.getAsset('bossHealthBar', {
anchorX: 0,
anchorY: 0.5
});
bossHealthBarBg.x = 2048 / 2;
bossHealthBarBg.y = 50;
bossHealthBar.x = 2048 / 2 - bossHealthBarBg.width / 2;
bossHealthBar.y = 50;
bossHealthBarBg.visible = false;
bossHealthBar.visible = false;
game.addChild(bossHealthBarBg);
game.addChild(bossHealthBar);
// Tutorial text (hidden initially)
tutorialText = new Text2('', {
size: 70,
fill: 0xFFFFFF
});
tutorialText.anchor.set(0.5, 0.5);
tutorialText.x = 2048 / 2;
tutorialText.y = 2732 / 2;
tutorialText.visible = false;
game.addChild(tutorialText);
}
function showTutorial() {
gameState = 'tutorial';
tutorialText.visible = true;
var tutorialMessages = ["Welcome to Boss Blitz!\n\nUse the arrow buttons to move your ship.", "Tap anywhere to shoot.\n\nDestroy the bosses to progress.", "Collect power-ups dropped by bosses\nto gain special abilities.", "Survive long enough to defeat\nall boss levels.", "Good luck!\n\nTap to begin..."];
tutorialText.setText(tutorialMessages[tutorialStep]);
// Make tutorial text pulsate
tween(tutorialText, {
alpha: 0.7
}, {
duration: 1000,
easing: tween.sinceOut,
onFinish: function onFinish() {
tween(tutorialText, {
alpha: 1
}, {
duration: 1000,
easing: tween.sinceIn
});
}
});
}
function advanceTutorial() {
tutorialStep++;
if (tutorialStep >= 5) {
// End tutorial
tutorialText.visible = false;
// Remove skip tutorial button if it exists
if (game.skipTutorialButton) {
game.removeChild(game.skipTutorialButton);
game.removeChild(game.skipTutorialText);
game.skipTutorialButton = null;
game.skipTutorialText = null;
}
startLevel();
} else {
showTutorial();
}
}
function startLevel() {
gameState = 'playing';
// Reset player position
player.x = 2048 / 2;
player.y = 2732 - 200;
// Clear any existing projectiles
clearProjectiles();
// Create boss based on current level
var bossType;
if (currentLevel === 1) {
bossType = 'miniBoss1';
} else if (currentLevel === 2) {
bossType = 'miniBoss2';
} else if (currentLevel === 3) {
bossType = 'boss1';
} else if (currentLevel === 4) {
bossType = 'boss2';
} else {
bossType = 'boss3';
}
currentBoss = new Boss(bossType, currentLevel);
currentBoss.x = 2048 / 2;
currentBoss.y = -200; // Start above screen
game.addChild(currentBoss);
// Show boss health bar
bossHealthBarBg.visible = true;
bossHealthBar.visible = true;
// Update UI
updateScore();
levelText.setText('LEVEL ' + currentLevel);
}
function updateScore() {
scoreText.setText('SCORE: ' + LK.getScore());
}
function clearProjectiles() {
// Remove all bullets, lasers, and powerups
bullets.forEach(function (bullet) {
bullet.parent.removeChild(bullet);
});
bullets = [];
lasers.forEach(function (laser) {
laser.parent.removeChild(laser);
});
lasers = [];
playerBullets.forEach(function (bullet) {
bullet.parent.removeChild(bullet);
});
playerBullets = [];
powerups.forEach(function (powerup) {
powerup.parent.removeChild(powerup);
});
powerups = [];
}
function checkCollisions() {
// Player bullets hitting boss
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
if (bullet.intersects(currentBoss)) {
// Boss hit
var bossDead = currentBoss.takeDamage(bullet.damage);
// Remove bullet
bullet.parent.removeChild(bullet);
playerBullets.splice(i, 1);
// Update boss health bar
updateBossHealthBar();
// Check if boss defeated
if (bossDead) {
handleBossDefeat();
}
}
}
// Enemy bullets hitting player
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
if (bullet.intersects(player)) {
// Player hit
var playerDied = player.takeDamage(bullet.damage);
// Remove bullet
bullet.parent.removeChild(bullet);
bullets.splice(i, 1);
// Update health bar
updatePlayerHealthBar();
// Check if player died
if (playerDied) {
handlePlayerDefeat();
}
}
}
// Lasers hitting player
for (var i = lasers.length - 1; i >= 0; i--) {
var laser = lasers[i];
// Only check active lasers (not in warning phase)
if (laser.counter >= laser.warningCounter && laser.intersects(player)) {
// Player hit
var playerDied = player.takeDamage(laser.damage);
// Update health bar
updatePlayerHealthBar();
// Check if player died
if (playerDied) {
handlePlayerDefeat();
}
// Don't remove laser on hit, it persists
}
}
// Power-ups being collected by player
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
if (powerup.intersects(player)) {
// Apply powerup effect
powerup.applyEffect(player);
// Remove powerup
powerup.parent.removeChild(powerup);
powerups.splice(i, 1);
}
}
}
function updatePlayerHealthBar() {
// Update width of health bar based on player health percentage
var healthPercent = player.health / player.maxHealth;
playerHealthBar.width = playerHealthBarBg.width * healthPercent;
// Change color based on health remaining
if (healthPercent > 0.6) {
playerHealthBar.tint = 0x34A853; // Green
} else if (healthPercent > 0.3) {
playerHealthBar.tint = 0xFBBC05; // Yellow
} else {
playerHealthBar.tint = 0xEA4335; // Red
}
}
function updateBossHealthBar() {
// Update width of health bar based on boss health percentage
var healthPercent = currentBoss.health / currentBoss.maxHealth;
bossHealthBar.width = bossHealthBarBg.width * healthPercent;
// Change color based on health remaining
if (healthPercent > 0.6) {
bossHealthBar.tint = 0xEA4335; // Red
} else if (healthPercent > 0.3) {
bossHealthBar.tint = 0xFF5733; // Orange
} else {
bossHealthBar.tint = 0xC70039; // Dark red
}
}
function handleBossDefeat() {
gameState = 'bossDead';
// Play defeat sound
LK.getSound('bossDefeat').play();
// Flash screen
LK.effects.flashScreen(0xFFFFFF, 500);
// Create explosion effect at boss position
LK.effects.flashObject(currentBoss, 0xFFFFFF, 1000);
LK.getSound('explosion').play();
// Remove boss
LK.setTimeout(function () {
game.removeChild(currentBoss);
currentBoss = null;
// Hide boss health bar
bossHealthBarBg.visible = false;
bossHealthBar.visible = false;
// Spawn powerups
spawnPowerups();
// Award score based on level
var scoreIncrease = currentLevel * 1000;
LK.setScore(LK.getScore() + scoreIncrease);
updateScore();
// Show level complete message
tutorialText.setText("LEVEL " + currentLevel + " COMPLETE!\n\nTap to continue...");
tutorialText.visible = true;
// Make text appear with effect
tutorialText.alpha = 0;
tween(tutorialText, {
alpha: 1
}, {
duration: 500,
easing: tween.easeOut
});
}, 1000);
}
function spawnPowerups() {
// Spawn 1-3 random powerups
var count = Math.floor(Math.random() * 3) + 1;
var types = ['shield', 'doubleDamage', 'tripleShot'];
for (var i = 0; i < count; i++) {
var type = types[Math.floor(Math.random() * types.length)];
var powerup = new PowerUp(type);
// Position near where boss was defeated
powerup.x = 2048 / 2 + (Math.random() * 400 - 200);
powerup.y = 300 + (Math.random() * 200 - 100);
game.addChild(powerup);
powerups.push(powerup);
}
}
function advanceToNextLevel() {
currentLevel++;
storage.currentLevel = currentLevel;
// Update max level reached
if (currentLevel > storage.maxLevelReached) {
storage.maxLevelReached = currentLevel;
}
// Hide tutorial text
tutorialText.visible = false;
// Start new level
startLevel();
}
function handlePlayerDefeat() {
gameState = 'gameOver';
// Play explosion
LK.getSound('explosion').play();
// Flash screen red
LK.effects.flashScreen(0xFF0000, 1000);
// Show game over message
LK.setTimeout(function () {
LK.showGameOver();
}, 1500);
}
function playerShoot() {
if (gameState !== 'playing') return;
var bullets = player.shoot();
if (bullets) {
bullets.forEach(function (bullet) {
game.addChild(bullet);
playerBullets.push(bullet);
});
}
}
// Event handlers
function handleDown(x, y, obj) {
// Handle tutorial advancement
if (gameState === 'tutorial') {
advanceTutorial();
return;
}
// Handle level advancement after boss defeat
if (gameState === 'bossDead' && tutorialText.visible) {
advanceToNextLevel();
return;
}
// In playing state, any tap triggers shooting
if (gameState === 'playing') {
// Check if the tap is on any of the control buttons
var isOnControlButton = false;
for (var i = 0; i < controlButtons.length; i++) {
if (obj === controlButtons[i]) {
isOnControlButton = true;
break;
}
}
// Only shoot if not tapping a control button
if (!isOnControlButton) {
playerShoot();
}
}
}
function handleMove(x, y, obj) {
// No longer need to handle dragging movement
}
function handleUp(x, y, obj) {
// No longer need to handle dragging release
}
game.down = handleDown;
game.move = handleMove;
game.up = handleUp;
// Main game loop
game.update = function () {
// Skip updates for non-playing states
if (gameState === 'tutorial' || gameState === 'gameOver' || gameState === 'paused') {
return;
}
// Auto-shoot for player
if (gameState === 'playing' && LK.ticks % 30 === 0) {
playerShoot();
}
// Update player
player.update();
// Update boss
if (currentBoss && gameState === 'playing') {
currentBoss.update();
}
// Update projectiles
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
bullet.update();
if (bullet.shouldRemove) {
bullet.parent.removeChild(bullet);
playerBullets.splice(i, 1);
}
}
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
bullet.update();
if (bullet.shouldRemove) {
bullet.parent.removeChild(bullet);
bullets.splice(i, 1);
}
}
for (var i = lasers.length - 1; i >= 0; i--) {
var laser = lasers[i];
laser.update();
if (laser.shouldRemove) {
laser.parent.removeChild(laser);
lasers.splice(i, 1);
}
}
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
powerup.update();
if (powerup.shouldRemove) {
powerup.parent.removeChild(powerup);
powerups.splice(i, 1);
}
}
// Check for collisions
if (gameState === 'playing') {
checkCollisions();
}
// Update UI elements
updatePlayerHealthBar();
if (currentBoss) {
updateBossHealthBar();
}
};
// Initialize game
initGame();
function createControlButtons() {
// Create the direction buttons
var btnSize = 150; // Increased from 120 to 150
var spacing = 25; // Slightly increased spacing
// Create buttons for each direction
var upButton = new ControlButton('up');
var downButton = new ControlButton('down');
var leftButton = new ControlButton('left');
var rightButton = new ControlButton('right');
// Position buttons based on user preference
var baseX = storage.controlsOnRight ? 2048 * 3 / 4 : 2048 / 4;
// Position the buttons in a D-pad layout
upButton.x = baseX;
upButton.y = 2732 - 3 * btnSize - 2 * spacing;
downButton.x = baseX;
downButton.y = 2732 - btnSize - spacing;
leftButton.x = baseX - btnSize - spacing;
leftButton.y = 2732 - 2 * btnSize - 1.5 * spacing;
rightButton.x = baseX + btnSize + spacing;
rightButton.y = 2732 - 2 * btnSize - 1.5 * spacing;
// Add buttons to the game
game.addChild(upButton);
game.addChild(downButton);
game.addChild(leftButton);
game.addChild(rightButton);
// Store buttons for reference
controlButtons = [upButton, downButton, leftButton, rightButton];
}
// Function to reposition control buttons when settings change
function repositionControlButtons() {
if (!controlButtons || controlButtons.length < 4) return;
var btnSize = 150;
var spacing = 25;
var baseX = storage.controlsOnRight ? 2048 * 3 / 4 : 2048 / 4;
// Update positions
controlButtons[0].x = baseX; // up
controlButtons[1].x = baseX; // down
controlButtons[2].x = baseX - btnSize - spacing; // left
controlButtons[3].x = baseX + btnSize + spacing; // right
}
// Function to handle player movement based on button presses
function movePlayer(direction, isPressed) {
if (!player || gameState !== 'playing') return;
// Update the movement state for the player
player.moving[direction] = isPressed;
}
// Pause the game and save previous state
function pauseGame() {
if (gameState !== 'playing') return;
// Store previous state
var previousState = gameState;
gameState = 'paused';
// Store this for when we resume
gameState.previousState = previousState;
}
// Resume the game
function resumeGame() {
if (gameState !== 'paused') return;
// Restore previous state
gameState = gameState.previousState || 'playing';
delete gameState.previousState;
}
// Show settings panel
function showSettings() {
var settingsPanel = new SettingsPanel();
settingsPanel.x = 2048 / 2;
settingsPanel.y = 2732 / 2;
game.addChild(settingsPanel);
}
// Skip tutorial button removed
;