/****
* 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
;