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: {}
});
/****
* 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
self.health = 100 * self.level;
self.maxHealth = self.health;
self.speed = 3 + self.level * 0.5;
self.shootCooldown = 0;
self.shootDelay = Math.max(30 - self.level * 5, 10); // Faster shooting at higher levels
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;
}
// 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 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 = {};
// 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;
}
}
};
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
});
self.speed = 15;
self.damage = 1;
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('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
// 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;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Background music
// Sound effects
// Health bars and UI
// Boss projectiles
// Main bosses
// Mini bosses
// Player, bosses, projectiles, UI elements, and effects
// Core game variables
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 dragNode = null;
// 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();
// Start with tutorial if first level
if (currentLevel === 1 && !storage.skipTutorial) {
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;
// 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\nDrag 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;
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;
}
// Start dragging player
if (gameState === 'playing') {
// Check if click is on player
if (obj === player || player.intersects(obj)) {
dragNode = player;
} else {
// Shoot when clicking elsewhere
playerShoot();
}
}
}
function handleMove(x, y, obj) {
if (dragNode && gameState === 'playing') {
// Move player, keeping within screen bounds
dragNode.x = Math.max(50, Math.min(2048 - 50, x));
dragNode.y = Math.max(100, Math.min(2732 - 100, y));
}
}
function handleUp(x, y, obj) {
dragNode = null;
}
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') {
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(); ===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,1157 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+var storage = LK.import("@upit/storage.v1", {
+ currentLevel: 1,
+ maxLevelReached: 1,
+ playerPowerups: {}
+});
+
+/****
+* 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
+ self.health = 100 * self.level;
+ self.maxHealth = self.health;
+ self.speed = 3 + self.level * 0.5;
+ self.shootCooldown = 0;
+ self.shootDelay = Math.max(30 - self.level * 5, 10); // Faster shooting at higher levels
+ 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;
+ }
+ // 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 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 = {};
+ // 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;
+ }
+ }
+ };
+ 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
+ });
+ self.speed = 15;
+ self.damage = 1;
+ 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('playerBullet', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 2,
+ scaleY: 2
+ });
+ // 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;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
backgroundColor: 0x000000
-});
\ No newline at end of file
+});
+
+/****
+* Game Code
+****/
+// Background music
+// Sound effects
+// Health bars and UI
+// Boss projectiles
+// Main bosses
+// Mini bosses
+// Player, bosses, projectiles, UI elements, and effects
+// Core game variables
+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 dragNode = null;
+// 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();
+ // Start with tutorial if first level
+ if (currentLevel === 1 && !storage.skipTutorial) {
+ 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;
+ // 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\nDrag 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;
+ 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;
+ }
+ // Start dragging player
+ if (gameState === 'playing') {
+ // Check if click is on player
+ if (obj === player || player.intersects(obj)) {
+ dragNode = player;
+ } else {
+ // Shoot when clicking elsewhere
+ playerShoot();
+ }
+ }
+}
+function handleMove(x, y, obj) {
+ if (dragNode && gameState === 'playing') {
+ // Move player, keeping within screen bounds
+ dragNode.x = Math.max(50, Math.min(2048 - 50, x));
+ dragNode.y = Math.max(100, Math.min(2732 - 100, y));
+ }
+}
+function handleUp(x, y, obj) {
+ dragNode = null;
+}
+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') {
+ 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();
\ No newline at end of file