User prompt
Use POWERUP as powerup drops
User prompt
Make a go back to level 1 button under settings
User prompt
Too big asset... i said not too big
User prompt
Make player bullet asset a lot bigger but not too big and make minibosses easier
User prompt
Make controls default to left
User prompt
Remove skip tutorial and play tutorial no matter what
User prompt
Put settings in top right and just do tutorial without skip stuff
User prompt
I meant the skip tutorial button not settings put it back and move skip tutorial button (also it skipped tutorial and I didn't even press it)
User prompt
Put it in top left for that button
User prompt
Instead of skip tutorial in settings what about a button for it during tutorial
User prompt
Make a left hand or right hand selector in a settings tab that pauses game for left or right hand placing for move buttons and other settings. ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Make up/down/left/right buttons bigger
User prompt
Make arrow buttons to move instead of dragging
Code edit (1 edits merged)
Please save this source code
User prompt
Boss Blitz: Escalating Challenge
User prompt
Make bosses after 2 mini-bosses for first 2 levels and then the other ones are bosses. Make bosses and mini-bosswes get harder every time
Initial prompt
Space shooter game
/**** * 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
;