/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
self.bossType = Math.random() > 0.5 ? 'alien' : 'robot';
self.phase = 1; // Track boss phase (1-3)
// Initialize boss body first
var bossGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3,
tint: self.bossType === 'alien' ? 0x800080 : 0x444444 // Purple for alien, dark gray for robot
});
// Create the health bar container
self.healthBarContainer = new Container();
self.healthBarContainer.x = 2048 / 2; // Center horizontally
self.healthBarContainer.y = 150; // Fixed position at top
// Create face features based on boss type
if (self.bossType === 'alien') {
// Alien face features - large oval eyes, small mouth
var leftEye = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.25,
scaleY: 0.35,
tint: 0x00FFFF,
// Cyan glowing eyes
x: -70,
y: -40
});
var rightEye = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.25,
scaleY: 0.35,
tint: 0x00FFFF,
// Cyan glowing eyes
x: 70,
y: -40
});
var mouth = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.1,
tint: 0x000000,
// Small mouth
x: 0,
y: 40
});
// Add antenna
var antenna1 = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.08,
scaleY: 0.6,
tint: 0x00FF00,
// Green antenna
x: -50,
y: -120
});
var antenna2 = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.08,
scaleY: 0.6,
tint: 0x00FF00,
// Green antenna
x: 50,
y: -120
});
// Add some unique alien features
var thirdEye = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.15,
scaleY: 0.15,
tint: 0xFF00FF,
x: 0,
y: -70
});
self.addChild(thirdEye);
} else {
// Robot face features - square eyes, rectangular mouth, mechanical look
var leftEye = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2,
tint: 0xFF0000,
// Red eyes
x: -70,
y: -30
});
var rightEye = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2,
tint: 0xFF0000,
// Red eyes
x: 70,
y: -30
});
var mouth = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.1,
tint: 0x666666,
// Metallic mouth
x: 0,
y: 50
});
// Add mechanical parts instead of antenna
var antenna1 = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.15,
scaleY: 0.4,
tint: 0x999999,
// Metal parts
x: -90,
y: -80
});
var antenna2 = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.15,
scaleY: 0.4,
tint: 0x999999,
// Metal parts
x: 90,
y: -80
});
// Add some robot-specific features
var centerPlate = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.15,
tint: 0x333333,
x: 0,
y: 0
});
self.addChild(centerPlate);
}
// Add face features to boss
self.addChild(leftEye);
self.addChild(rightEye);
self.addChild(mouth);
self.addChild(antenna1);
self.addChild(antenna2);
// Configure the health bar container which was initialized earlier
self.healthBarContainer.x = 2048 / 2; // Center horizontally
self.healthBarContainer.y = 150; // Fixed position at top
// Boss health indicator background
var healthBarBg = LK.getAsset('timerBarBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.5,
tint: 0x333333
});
self.healthBarContainer.addChild(healthBarBg);
// Boss health indicator
var healthBar = LK.getAsset('timerBar', {
anchorX: 0,
anchorY: 0.5,
scaleX: 1,
scaleY: 0.5,
tint: 0xFF0000,
// Red health bar
x: -healthBarBg.width / 2,
y: 0,
rotation: 0 // Ensure the bar is straight
});
self.healthBarContainer.addChild(healthBar);
// Add boss name/type label
var bossLabel = new Text2(self.bossType.toUpperCase() + " BOSS", {
size: 40,
fill: 0xFFFFFF
});
bossLabel.anchor.set(0.5, 0.5);
bossLabel.y = -healthBarBg.height;
self.healthBarContainer.addChild(bossLabel);
// Store references to health bar elements
self.healthBar = healthBar;
self.healthBarBg = healthBarBg;
self.maxHealth = 100;
self.health = self.maxHealth;
self.speedX = 5;
self.speedY = 3;
self.attackCooldown = 0;
self.shootInterval = 60; // Frames between attacks
self.update = function (player, weaponEffects, game) {
// Update health bar position with game
if (self.healthBarContainer && game && self.healthBarContainer.parent !== game) {
game.addChild(self.healthBarContainer);
}
// Movement pattern based on phase
self.x += self.speedX;
self.y += self.speedY;
// Bounce off screen edges
if (self.x < bossGraphics.width / 2 || self.x > 2048 - bossGraphics.width / 2) {
self.speedX *= -1;
}
if (self.y < bossGraphics.height / 2 || self.y > 2732 - bossGraphics.height / 2) {
self.speedY *= -1;
}
// Update health bar
self.healthBar.scale.x = self.health / self.maxHealth;
// Check for phase transitions
var healthPercentage = self.health / self.maxHealth;
if (healthPercentage <= 0.66 && self.phase === 1) {
self.phase = 2;
self.transitionToPhase2();
} else if (healthPercentage <= 0.33 && self.phase === 2) {
self.phase = 3;
self.transitionToPhase3();
}
// Helper function to fire a projectile
function fireProjectile(angleOffset) {
if (!game || !weaponEffects) {
return; // Safety check
}
var projectile = new WeaponEffect();
projectile.setEffectType('boss', 1 + self.phase); // More damage in later phases
// Position at boss and aim at player
projectile.x = self.x;
projectile.y = self.y;
// Check if player exists before trying to calculate angle
if (player) {
var angle = Math.atan2(player.y - self.y, player.x - self.x);
projectile.rotation = angle + angleOffset;
} else {
// Default direction if player is null
projectile.rotation = Math.PI / 2 + angleOffset; // Down
}
game.addChild(projectile);
weaponEffects.push(projectile);
}
// Different attack patterns based on phase
if (self.attackCooldown <= 0) {
// Create enemy projectile(s)
if (self.phase === 1) {
// Phase 1: Single projectile
fireProjectile(0);
} else if (self.phase === 2) {
// Phase 2: Two projectiles at slight angles
fireProjectile(-0.2);
fireProjectile(0.2);
} else {
// Phase 3: Three projectiles in spread pattern
fireProjectile(-0.4);
fireProjectile(0);
fireProjectile(0.4);
}
// Reset cooldown - faster in later phases
self.attackCooldown = self.shootInterval;
// Expression change when attacking
mouth.scaleY = 0.3;
// Reset mouth after attack
tween(mouth, {
scaleY: self.bossType === 'alien' ? 0.1 : 0.2
}, {
duration: 300,
easing: tween.easeOut
});
} else {
self.attackCooldown--;
}
// Make boss more aggressive as health decreases
self.shootInterval = Math.max(20, Math.floor(60 * (self.health / self.maxHealth)));
};
self.transitionToPhase2 = function () {
// Visual effect for phase transition
LK.effects.flashScreen(0xFF8800, 500);
// Increase boss speed
self.speedX *= 1.3;
self.speedY *= 1.3;
// Visual changes based on boss type
if (self.bossType === 'alien') {
// Alien gets more aggressive
tween(bossGraphics, {
tint: 0xAA00AA // Deeper purple
}, {
duration: 500,
easing: tween.easeOut
});
// Eyes glow more intensely
tween(leftEye, {
tint: 0x00FFAA,
scaleX: 0.3,
scaleY: 0.4
}, {
duration: 500,
easing: tween.easeOut
});
tween(rightEye, {
tint: 0x00FFAA,
scaleX: 0.3,
scaleY: 0.4
}, {
duration: 500,
easing: tween.easeOut
});
} else {
// Robot gets more mechanical
tween(bossGraphics, {
tint: 0x666666 // Darker metal
}, {
duration: 500,
easing: tween.easeOut
});
// Eyes glow more intensely
tween(leftEye, {
tint: 0xFF6600,
scaleX: 0.25,
scaleY: 0.25
}, {
duration: 500,
easing: tween.easeOut
});
tween(rightEye, {
tint: 0xFF6600,
scaleX: 0.25,
scaleY: 0.25
}, {
duration: 500,
easing: tween.easeOut
});
}
};
self.transitionToPhase3 = function () {
// Visual effect for phase transition
LK.effects.flashScreen(0xFF0000, 500);
// Major speed increase for final phase
self.speedX *= 1.5;
self.speedY *= 1.5;
// Visual changes based on boss type
if (self.bossType === 'alien') {
// Alien becomes enraged
tween(bossGraphics, {
tint: 0xFF00FF // Bright magenta
}, {
duration: 500,
easing: tween.easeOut
});
// Eyes glow intensely
tween(leftEye, {
tint: 0xFFFF00,
scaleX: 0.35,
scaleY: 0.45
}, {
duration: 500,
easing: tween.easeOut
});
tween(rightEye, {
tint: 0xFFFF00,
scaleX: 0.35,
scaleY: 0.45
}, {
duration: 500,
easing: tween.easeOut
});
// Mouth becomes more aggressive
tween(mouth, {
scaleX: 0.6,
scaleY: 0.2
}, {
duration: 500,
easing: tween.easeOut
});
} else {
// Robot enters combat mode
tween(bossGraphics, {
tint: 0x990000 // Dark red
}, {
duration: 500,
easing: tween.easeOut
});
// Eyes glow intensely
tween(leftEye, {
tint: 0xFFFF00,
scaleX: 0.3,
scaleY: 0.3
}, {
duration: 500,
easing: tween.easeOut
});
tween(rightEye, {
tint: 0xFFFF00,
scaleX: 0.3,
scaleY: 0.3
}, {
duration: 500,
easing: tween.easeOut
});
// Mouth becomes more aggressive
tween(mouth, {
scaleX: 0.8,
scaleY: 0.15
}, {
duration: 500,
easing: tween.easeOut
});
}
};
self.takeDamage = function (damage) {
self.health -= damage;
// Update health bar immediately
self.healthBar.scale.x = self.health / self.maxHealth;
// Shake boss when taking damage
var originalX = self.x;
var originalY = self.y;
tween(self, {
x: originalX + (Math.random() * 30 - 15),
y: originalY + (Math.random() * 30 - 15)
}, {
duration: 50,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
x: originalX,
y: originalY
}, {
duration: 50,
easing: tween.easeIn
});
}
});
// Flash red when taking damage
var currentTint = bossGraphics.tint;
tween(bossGraphics, {
tint: 0xFF0000
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(bossGraphics, {
tint: currentTint
}, {
duration: 100,
easing: tween.easeIn
});
}
});
// Return true if boss is defeated
return self.health <= 0;
};
return self;
});
var CollectableCube = Container.expand(function () {
var self = Container.call(this);
var cubeGraphics = self.attachAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00ffff // Cyan color for the cube
});
// Create face features
var leftEye = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2,
tint: 0x000000,
// Black eyes
x: -15,
y: -10
});
var rightEye = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2,
tint: 0x000000,
// Black eyes
x: 15,
y: -10
});
var mouth = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.15,
tint: 0x000000,
// Black mouth
x: 0,
y: 15
});
// Add the face features to the cube
self.addChild(leftEye);
self.addChild(rightEye);
self.addChild(mouth);
// Add point value text
var pointsTxt = new Text2('+10', {
size: 30,
fill: 0xFFFFFF
});
pointsTxt.anchor.set(0.5, 0.5);
pointsTxt.y = -40;
self.addChild(pointsTxt);
self.value = 10;
self.cubeType = 'normal';
self.spawnTime = 0;
self.pulseAnimation = function () {
tween(cubeGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(cubeGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
};
self.randomPosition = function (minX, maxX, minY, maxY) {
self.x = minX + Math.random() * (maxX - minX);
self.y = minY + Math.random() * (maxY - minY);
self.spawnTime = Date.now();
self.pulseAnimation();
};
self.setCubeType = function (type) {
self.cubeType = type;
switch (type) {
case 'silver':
self.value = 20;
pointsTxt.setText('+20');
cubeGraphics.tint = 0xC0C0C0; // Silver
break;
case 'gold':
self.value = 30;
pointsTxt.setText('+30');
cubeGraphics.tint = 0xFFD700; // Gold
break;
case 'diamond':
self.value = 50;
pointsTxt.setText('+50');
cubeGraphics.tint = 0x87CEEB; // Sky blue diamond
// Add sparkle effect
self.sparkleTimer = 0;
break;
case 'platinum':
self.value = 100;
pointsTxt.setText('+100');
cubeGraphics.tint = 0xE5E4E2; // Platinum color
// Add glow effect
self.glowTimer = 0;
break;
case 'normal':
default:
self.value = 10;
pointsTxt.setText('+10');
cubeGraphics.tint = 0x00FFFF; // Cyan
break;
}
// Update the facial expression based on cube type
if (type === 'platinum') {
// Ecstatic expression for platinum cubes
mouth.scaleY = 0.3;
mouth.y = 5;
// Make eyes sparkle
leftEye.tint = 0xFFD700;
rightEye.tint = 0xFFD700;
} else if (type === 'diamond') {
// Very happy expression for diamond cubes
mouth.scaleY = 0.25;
mouth.y = 8;
// Make eyes bright
leftEye.tint = 0x87CEEB;
rightEye.tint = 0x87CEEB;
} else if (type === 'gold') {
// Happy expression for gold cubes
mouth.scaleY = 0.2;
mouth.y = 10;
} else if (type === 'silver') {
// Neutral expression for silver cubes
mouth.scaleY = 0.15;
mouth.y = 15;
} else {
// Normal expression
mouth.scaleY = 0.15;
mouth.y = 15;
}
};
// Add special effects update method
self.updateSpecialEffects = function () {
if (self.cubeType === 'diamond' && self.sparkleTimer !== undefined) {
self.sparkleTimer++;
if (self.sparkleTimer % 20 === 0) {
// Create sparkle effect
var sparkle = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
tint: 0xFFFFFF,
alpha: 1,
x: self.x + (Math.random() - 0.5) * 60,
y: self.y + (Math.random() - 0.5) * 60
});
if (self.parent) {
self.parent.addChild(sparkle);
tween(sparkle, {
alpha: 0,
scaleX: 0.3,
scaleY: 0.3
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
if (sparkle.parent) {
sparkle.parent.removeChild(sparkle);
}
}
});
}
}
} else if (self.cubeType === 'platinum' && self.glowTimer !== undefined) {
self.glowTimer++;
// Pulsing glow effect
var glowIntensity = 0.8 + Math.sin(self.glowTimer * 0.1) * 0.2;
cubeGraphics.alpha = glowIntensity;
}
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xff5555 // Bright red for obstacles
});
// Create angry face for obstacle
var leftEye = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.15,
scaleY: 0.25,
tint: 0xFFFFFF,
// White eyes
x: -40,
y: -5
});
var rightEye = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.15,
scaleY: 0.25,
tint: 0xFFFFFF,
// White eyes
x: 40,
y: -5
});
var leftPupil = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.06,
scaleY: 0.12,
tint: 0x000000,
// Black pupils
x: -40,
y: -2
});
var rightPupil = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.06,
scaleY: 0.12,
tint: 0x000000,
// Black pupils
x: 40,
y: -2
});
var mouth = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.12,
tint: 0x000000,
// Black mouth
x: 0,
y: 10
});
// Add face features to obstacle
self.addChild(leftEye);
self.addChild(rightEye);
self.addChild(leftPupil);
self.addChild(rightPupil);
self.addChild(mouth);
self.speedX = 0;
self.speedY = 0;
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
// Bounce off screen edges
if (self.x < obstacleGraphics.width / 2 || self.x > 2048 - obstacleGraphics.width / 2) {
self.speedX *= -1;
}
if (self.y < obstacleGraphics.height / 2 || self.y > 2732 - obstacleGraphics.height / 2) {
self.speedY *= -1;
}
// Rotate obstacle slightly based on direction for visual effect
var targetRotation = Math.atan2(self.speedY, self.speedX) * 0.2;
self.rotation += (targetRotation - self.rotation) * 0.1;
};
self.setRandomSpeed = function (maxSpeed) {
// Ensure obstacle always moves by setting minimum speed
var minSpeed = maxSpeed * 0.3;
// Generate random speeds between min and max
self.speedX = (Math.random() > 0.5 ? 1 : -1) * (minSpeed + Math.random() * (maxSpeed - minSpeed));
self.speedY = (Math.random() > 0.5 ? 1 : -1) * (minSpeed + Math.random() * (maxSpeed - minSpeed));
// Give each obstacle a unique color variation
obstacleGraphics.tint = 0xff0000 + Math.floor(Math.random() * 0x5555);
};
self.randomPosition = function (minX, maxX, minY, maxY) {
self.x = minX + Math.random() * (maxX - minX);
self.y = minY + Math.random() * (maxY - minY);
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('playerCube', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x3498db // Bright blue for player
});
// Create face for player
var leftEye = LK.getAsset('playerCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2,
tint: 0xFFFFFF,
// White eyes
x: -20,
y: -15
});
var rightEye = LK.getAsset('playerCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2,
tint: 0xFFFFFF,
// White eyes
x: 20,
y: -15
});
var leftPupil = LK.getAsset('playerCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
tint: 0x000000,
// Black pupils
x: -20,
y: -15
});
var rightPupil = LK.getAsset('playerCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
tint: 0x000000,
// Black pupils
x: 20,
y: -15
});
var mouth = LK.getAsset('playerCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.2,
tint: 0x000000,
// Black mouth
x: 0,
y: 15
});
// Add score display on player
var scoreBubble = LK.getAsset('playerCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.5,
tint: 0xFFFFFF,
// White bubble
x: 0,
y: -60
});
// Add face features to player
self.addChild(leftEye);
self.addChild(rightEye);
self.addChild(leftPupil);
self.addChild(rightPupil);
self.addChild(mouth);
self.addChild(scoreBubble);
// Create player score display
var playerScoreTxt = new Text2('0', {
size: 32,
fill: 0x000000
});
playerScoreTxt.anchor.set(0.5, 0.5);
playerScoreTxt.y = -60;
self.addChild(playerScoreTxt);
// Add weapon display
var weaponHolder = new Container();
self.addChild(weaponHolder);
weaponHolder.y = 50;
// Current weapon
self.weapon = new Weapon();
self.weapon.setWeaponType('basic', 1);
weaponHolder.addChild(self.weapon);
// Level indicators
var levelIndicator = new Text2('Lv.1', {
size: 24,
fill: 0xFFFFFF
});
levelIndicator.anchor.set(0.5, 0.5);
levelIndicator.y = 25;
weaponHolder.addChild(levelIndicator);
self.updateScore = function (newScore) {
playerScoreTxt.setText(newScore);
};
self.updateWeapon = function (type, level) {
self.weapon.setWeaponType(type, level);
levelIndicator.setText('Lv.' + level);
// Flash weapon effect
tween(weaponHolder, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300,
easing: tween.elasticOut,
onFinish: function onFinish() {
tween(weaponHolder, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut
});
}
});
};
self.fireWeapon = function (targets, game, weaponEffects) {
if (self.weapon.canFire()) {
// Get weapon stats
var damage = self.weapon.fire();
var type = self.weapon.type;
// Create weapon effect
var effect = new WeaponEffect();
effect.setEffectType(type, damage);
// Position at player
effect.x = self.x;
effect.y = self.y;
// If we have targets, aim at the nearest one
var nearestTarget = null;
var minDistance = Number.MAX_VALUE;
for (var i = 0; i < targets.length; i++) {
var target = targets[i];
if (target) {
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
minDistance = distance;
nearestTarget = target;
}
}
}
if (nearestTarget) {
var angle = Math.atan2(nearestTarget.y - self.y, nearestTarget.x - self.x);
effect.rotation = angle;
} else {
// If no targets, fire forward
effect.rotation = 0;
}
game.addChild(effect);
weaponEffects.push(effect);
// Flash the weapon
self.weapon.alpha = 0.7;
tween(self.weapon, {
alpha: 1
}, {
duration: 200,
easing: tween.easeOut
});
return true;
}
return false;
};
self.speed = 10;
self.targetX = 0;
self.targetY = 0;
self.moving = false;
self.setTarget = function (x, y) {
self.targetX = x;
self.targetY = y;
self.moving = true;
// Animate eyes to look in move direction
var eyeAngle = Math.atan2(y - self.y, x - self.x);
var eyeOffsetX = Math.cos(eyeAngle) * 3;
var eyeOffsetY = Math.sin(eyeAngle) * 3;
tween(leftPupil, {
x: -20 + eyeOffsetX,
y: -15 + eyeOffsetY
}, {
duration: 200,
easing: tween.easeOut
});
tween(rightPupil, {
x: 20 + eyeOffsetX,
y: -15 + eyeOffsetY
}, {
duration: 200,
easing: tween.easeOut
});
};
self.update = function () {
if (self.moving) {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < self.speed) {
self.x = self.targetX;
self.y = self.targetY;
self.moving = false;
} else {
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
}
}
// Update weapon cooldown
self.weapon.update();
};
return self;
});
var TimerBar = Container.expand(function () {
var self = Container.call(this);
var background = self.attachAsset('timerBarBackground', {
anchorX: 0,
anchorY: 0.5,
alpha: 0.7 // Semi-transparent background
});
var bar = self.attachAsset('timerBar', {
anchorX: 0,
anchorY: 0.5
});
// Add clock icon
var clockIcon = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
tint: 0xFFFFFF,
x: -50,
y: 0
});
self.addChild(clockIcon);
// Add timer text
var timerText = new Text2('30s', {
size: 40,
fill: 0xFFFFFF
});
timerText.anchor.set(0, 0.5);
timerText.x = background.width + 20;
timerText.y = 0;
self.addChild(timerText);
self.maxTime = 30000; // 30 seconds in milliseconds
self.timeRemaining = self.maxTime;
self.update = function (delta) {
self.timeRemaining -= delta;
if (self.timeRemaining < 0) {
self.timeRemaining = 0;
}
var percentage = self.timeRemaining / self.maxTime;
bar.scale.x = percentage;
// Update timer text
var secondsLeft = Math.ceil(self.timeRemaining / 1000);
timerText.setText(secondsLeft + 's');
// Change color as time runs out
if (percentage < 0.2) {
bar.tint = 0xe74c3c; // Red
clockIcon.tint = 0xe74c3c;
timerText.setText(secondsLeft + 's', {
fill: 0xe74c3c
});
// Pulse animation when low on time
if (Math.floor(self.timeRemaining / 200) % 2 === 0) {
clockIcon.scale.x = 0.9;
clockIcon.scale.y = 0.9;
} else {
clockIcon.scale.x = 0.8;
clockIcon.scale.y = 0.8;
}
} else if (percentage < 0.5) {
bar.tint = 0xf39c12; // Orange
clockIcon.tint = 0xf39c12;
timerText.setText(secondsLeft + 's', {
fill: 0xf39c12
});
} else {
bar.tint = 0x2ecc71; // Green
clockIcon.tint = 0x2ecc71;
timerText.setText(secondsLeft + 's', {
fill: 0x2ecc71
});
}
};
self.reset = function () {
self.timeRemaining = self.maxTime;
bar.scale.x = 1;
bar.tint = 0x2ecc71;
clockIcon.tint = 0x2ecc71;
timerText.setText(Math.ceil(self.timeRemaining / 1000) + 's', {
fill: 0x2ecc71
});
};
return self;
});
var Weapon = Container.expand(function () {
var self = Container.call(this);
var weaponGraphics = self.attachAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.15,
tint: 0xFFFFFF
});
self.type = 'basic';
self.level = 1;
self.attackSpeed = 1;
self.damage = 1;
self.cooldown = 0;
self.setWeaponType = function (type, level) {
self.type = type;
self.level = level || 1;
switch (type) {
case 'laser':
weaponGraphics.tint = 0xFF0000; // Red laser
self.attackSpeed = 1.5;
self.damage = 1;
break;
case 'plasma':
weaponGraphics.tint = 0x00FF00; // Green plasma
self.attackSpeed = 1;
self.damage = 2;
break;
case 'wave':
weaponGraphics.tint = 0x0000FF; // Blue wave
self.attackSpeed = 0.7;
self.damage = 3;
break;
case 'basic':
default:
weaponGraphics.tint = 0xFFFFFF; // White basic
self.attackSpeed = 1;
self.damage = 1;
break;
}
// Apply level multipliers
self.attackSpeed *= 1 + (self.level - 1) * 0.1;
self.damage *= self.level;
};
self.update = function () {
if (self.cooldown > 0) {
self.cooldown--;
}
};
self.canFire = function () {
return self.cooldown <= 0;
};
self.fire = function () {
// Set cooldown based on attack speed (higher attack speed = lower cooldown)
self.cooldown = Math.round(60 / self.attackSpeed);
return self.damage;
};
return self;
});
var WeaponEffect = Container.expand(function () {
var self = Container.call(this);
var effectGraphics = self.attachAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
self.type = 'basic';
self.damage = 1;
self.speed = 15;
self.lifetime = 60; // frames
self.setEffectType = function (type, damage) {
self.type = type;
self.damage = damage || 1;
switch (type) {
case 'laser':
effectGraphics.tint = 0xFF0000; // Red laser
effectGraphics.scaleX = 0.2;
effectGraphics.scaleY = 0.5;
self.speed = 20;
break;
case 'plasma':
effectGraphics.tint = 0x00FF00; // Green plasma
effectGraphics.scaleX = 0.4;
effectGraphics.scaleY = 0.4;
self.speed = 15;
break;
case 'wave':
effectGraphics.tint = 0x0000FF; // Blue wave
effectGraphics.scaleX = 0.6;
effectGraphics.scaleY = 0.3;
self.speed = 10;
break;
case 'boss':
// Boss projectiles have different appearances based on damage level (boss phase)
var phase = damage;
if (phase === 1) {
effectGraphics.tint = 0xFF00FF; // Purple boss projectile (phase 1)
effectGraphics.scaleX = 0.4;
effectGraphics.scaleY = 0.4;
self.speed = 8;
} else if (phase === 2) {
effectGraphics.tint = 0xFF6600; // Orange boss projectile (phase 2)
effectGraphics.scaleX = 0.5;
effectGraphics.scaleY = 0.5;
self.speed = 10;
} else {
effectGraphics.tint = 0xFF0000; // Red boss projectile (phase 3)
effectGraphics.scaleX = 0.6;
effectGraphics.scaleY = 0.6;
self.speed = 12;
}
break;
case 'basic':
default:
effectGraphics.tint = 0xFFFFFF; // White basic
effectGraphics.scaleX = 0.3;
effectGraphics.scaleY = 0.3;
self.speed = 15;
break;
}
// Pulse animation
tween(effectGraphics, {
alpha: 0.7
}, {
duration: 100,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(effectGraphics, {
alpha: 1
}, {
duration: 100,
easing: tween.easeInOut
});
}
});
};
self.update = function () {
// Move in direction of rotation
self.x += Math.cos(self.rotation) * self.speed;
self.y += Math.sin(self.rotation) * self.speed;
// Update lifetime
self.lifetime--;
// Add trailing effect
if (self.lifetime % 3 === 0) {
var trail = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: effectGraphics.scaleX * 0.7,
scaleY: effectGraphics.scaleY * 0.7,
alpha: 0.5,
tint: effectGraphics.tint,
x: 0,
y: 0
});
trail.x = self.x;
trail.y = self.y;
trail.parent = self.parent;
self.parent.addChild(trail);
tween(trail, {
alpha: 0,
scaleX: trail.scaleX * 0.5,
scaleY: trail.scaleY * 0.5
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (trail.parent) {
trail.parent.removeChild(trail);
}
}
});
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x8e44ad // Vibrant purple background
});
/****
* Game Code
****/
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var PLAYER_SPEED_BASE = 10;
var OBSTACLE_SPEED_BASE = 3;
var CUBE_SPAWN_INTERVAL_BASE = 1500; // ms
var OBSTACLE_SPAWN_INTERVAL = 5000; // ms
var LEVEL_UP_SCORE = 100;
var TIME_BONUS_MAX = 50;
var TIME_BONUS_THRESHOLD = 1000; // ms
var WEAPON_UPGRADE_SCORE = 100; // Score needed for weapon upgrades
// Cube type probabilities
var CUBE_TYPES = {
normal: {
chance: 50,
value: 10
},
silver: {
chance: 25,
value: 20
},
gold: {
chance: 15,
value: 30
},
diamond: {
chance: 8,
value: 50
},
platinum: {
chance: 2,
value: 100
}
};
// Weapon types by level
var WEAPON_UPGRADES = [{
level: 1,
type: 'basic',
name: 'Basic Blaster'
}, {
level: 2,
type: 'laser',
name: 'Laser Beam'
}, {
level: 3,
type: 'plasma',
name: 'Plasma Cannon'
}, {
level: 4,
type: 'wave',
name: 'Wave Disruptor'
}];
// Game variables
var player;
var collectableCubes = [];
var obstacles = [];
var weaponEffects = [];
var timerBar;
var score = 0;
var level = 1;
var weaponLevel = 1;
var lastCubeSpawnTime = 0;
var lastObstacleSpawnTime = 0;
var lastWeaponFireTime = 0;
var cubeSpawnInterval = CUBE_SPAWN_INTERVAL_BASE;
var playerSpeed = PLAYER_SPEED_BASE;
var obstacleSpeed = OBSTACLE_SPEED_BASE;
var lastTime = Date.now();
var gameActive = true;
var highScore = storage.highScore || 0;
var nextWeaponUpgrade = WEAPON_UPGRADE_SCORE;
var boss = null;
var inBossFight = false;
var bossDefeated = false;
var savedCubes = [];
var endingSequenceStarted = false;
var endingSequenceStep = 0;
var endingSequenceTimer = 0;
// UI Elements
var scoreTxt = new Text2('Score: 0', {
size: 70,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
scoreTxt.x = -scoreTxt.width - 20;
scoreTxt.y = 20;
var levelTxt = new Text2('Level: 1', {
size: 70,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(levelTxt);
levelTxt.x = -levelTxt.width - 20;
levelTxt.y = scoreTxt.y + scoreTxt.height + 10;
var highScoreTxt = new Text2('High Score: ' + highScore, {
size: 70,
fill: 0xFFFFFF
});
highScoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(highScoreTxt);
highScoreTxt.x = -highScoreTxt.width - 20;
highScoreTxt.y = levelTxt.y + levelTxt.height + 10;
// Initialize game elements
function initGame() {
// Create colorful background patterns
createBackgroundPatterns();
// Create and position player
player = new Player();
player.x = GAME_WIDTH / 2;
player.y = GAME_HEIGHT / 2;
player.speed = playerSpeed;
game.addChild(player);
// Add a welcome animation
tween(player, {
scaleX: 0,
scaleY: 0
}, {
duration: 0
});
tween(player, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
easing: tween.elasticOut,
onFinish: function onFinish() {
tween(player, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
}
});
// Create timer bar
timerBar = new TimerBar();
timerBar.x = (GAME_WIDTH - 1800) / 2;
timerBar.y = 100;
game.addChild(timerBar);
// Start with a few cubes and obstacles
spawnCollectableCube();
spawnObstacle();
// Play background music
LK.playMusic('gameMusic');
}
function createBackgroundPatterns() {
// Create some decorative background elements
for (var i = 0; i < 20; i++) {
var decoration = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3 + Math.random() * 0.3,
scaleY: 0.3 + Math.random() * 0.3,
alpha: 0.15,
tint: Math.random() * 0xFFFFFF,
x: Math.random() * GAME_WIDTH,
y: Math.random() * GAME_HEIGHT
});
// Place decorations behind game elements
game.addChildAt(decoration, 0);
// Add subtle animation to background elements
var animDuration = 3000 + Math.random() * 5000;
tween(decoration, {
rotation: Math.random() * Math.PI * 2
}, {
duration: animDuration,
easing: tween.easeInOut
});
}
}
function updateGame() {
if (!gameActive) {
return;
}
var currentTime = Date.now();
var delta = currentTime - lastTime;
lastTime = currentTime;
// Check if we're in ending sequence
if (endingSequenceStarted) {
updateEndingSequence();
return;
}
// Check if we're in boss fight
if (inBossFight) {
updateBossFight();
// Only check timer if boss isn't defeated
if (!bossDefeated) {
// Update timer
timerBar.update(delta);
if (timerBar.timeRemaining <= 0) {
gameOver();
return;
}
}
// Continue with collision checks and updates
} else {
// Update timer
timerBar.update(delta);
if (timerBar.timeRemaining <= 0) {
gameOver();
return;
}
// Spawn new cubes
if (currentTime - lastCubeSpawnTime > cubeSpawnInterval) {
spawnCollectableCube();
lastCubeSpawnTime = currentTime;
}
// Spawn new obstacles
if (currentTime - lastObstacleSpawnTime > OBSTACLE_SPAWN_INTERVAL) {
spawnObstacle();
lastObstacleSpawnTime = currentTime;
}
}
// Fire weapon automatically
if (currentTime - lastWeaponFireTime > 1000) {
var targets = inBossFight ? [boss] : obstacles;
if (player.fireWeapon(targets, game, weaponEffects)) {
lastWeaponFireTime = currentTime;
}
}
// Update weapon effects
for (var i = weaponEffects.length - 1; i >= 0; i--) {
var effect = weaponEffects[i];
// Update effect
effect.update();
// Check if effect is expired
if (effect.lifetime <= 0) {
game.removeChild(effect);
weaponEffects.splice(i, 1);
continue;
}
// Skip boss projectile collisions with obstacles
if (effect.type === 'boss') {
continue;
}
// Check for collisions with obstacles
for (var j = 0; j < obstacles.length; j++) {
var obstacle = obstacles[j];
if (effect.intersects(obstacle)) {
// Hit effect
var hitEffect = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
tint: effect.tint,
alpha: 0.8,
x: effect.x,
y: effect.y
});
game.addChild(hitEffect);
// Animate hit effect
tween(hitEffect, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(hitEffect);
}
});
// Remove effect
game.removeChild(effect);
weaponEffects.splice(i, 1);
// Destroy obstacle
game.removeChild(obstacle);
obstacles.splice(j, 1);
// Add score for destroying obstacle
addScore(5 * weaponLevel);
break;
}
}
}
// Check for collisions
if (!endingSequenceStarted) {
checkCollisions();
}
}
function spawnCollectableCube() {
var cube = new CollectableCube();
// Determine cube type based on probabilities and level
var rand = Math.random() * 100;
var cubeType = 'normal';
// Adjust probabilities based on level (higher levels get better cubes)
var platinumChance = CUBE_TYPES.platinum.chance + Math.max(0, level - 5) * 0.5;
var diamondChance = CUBE_TYPES.diamond.chance + Math.max(0, level - 3) * 1;
var goldChance = CUBE_TYPES.gold.chance + level * 1;
var silverChance = CUBE_TYPES.silver.chance + level * 1.5;
if (rand < platinumChance) {
cubeType = 'platinum';
} else if (rand < platinumChance + diamondChance) {
cubeType = 'diamond';
} else if (rand < platinumChance + diamondChance + goldChance) {
cubeType = 'gold';
} else if (rand < platinumChance + diamondChance + goldChance + silverChance) {
cubeType = 'silver';
}
// Set the cube type
cube.setCubeType(cubeType);
cube.randomPosition(100, GAME_WIDTH - 100, 200, GAME_HEIGHT - 200);
// Make sure cube doesn't spawn on obstacles
var validPosition = false;
var maxAttempts = 10;
var attempts = 0;
while (!validPosition && attempts < maxAttempts) {
validPosition = true;
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
var dx = cube.x - obstacle.x;
var dy = cube.y - obstacle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 150) {
validPosition = false;
cube.randomPosition(100, GAME_WIDTH - 100, 200, GAME_HEIGHT - 200);
break;
}
}
attempts++;
}
collectableCubes.push(cube);
game.addChild(cube);
// Animate cube entrance
cube.scale.x = 0;
cube.scale.y = 0;
tween(cube, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.elasticOut
});
}
function spawnObstacle() {
var obstacle = new Obstacle();
obstacle.randomPosition(100, GAME_WIDTH - 100, 200, GAME_HEIGHT - 200);
obstacle.setRandomSpeed(obstacleSpeed);
// Make sure obstacle doesn't spawn on player
var dx = obstacle.x - player.x;
var dy = obstacle.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 200) {
obstacle.x = GAME_WIDTH - obstacle.x;
obstacle.y = GAME_HEIGHT - obstacle.y;
}
obstacles.push(obstacle);
game.addChild(obstacle);
}
function checkCollisions() {
// Check collisions with collectables
for (var i = collectableCubes.length - 1; i >= 0; i--) {
var cube = collectableCubes[i];
// Update special effects for diamond and platinum cubes
if (cube.updateSpecialEffects) {
cube.updateSpecialEffects();
}
if (player.intersects(cube)) {
// Calculate time bonus
var collectionTime = Date.now() - cube.spawnTime;
var timeBonus = 0;
if (collectionTime < TIME_BONUS_THRESHOLD) {
timeBonus = Math.round(TIME_BONUS_MAX * (1 - collectionTime / TIME_BONUS_THRESHOLD));
}
// Special effects for rare cubes
if (cube.cubeType === 'platinum') {
LK.effects.flashScreen(0xFFD700, 500);
// Add extra time bonus for platinum
timerBar.timeRemaining += 2000;
} else if (cube.cubeType === 'diamond') {
LK.effects.flashScreen(0x87CEEB, 300);
// Add small time bonus for diamond
timerBar.timeRemaining += 1000;
}
// Add score and remove cube
addScore(cube.value + timeBonus);
game.removeChild(cube);
collectableCubes.splice(i, 1);
// Play collect sound
LK.getSound('collect').play();
// Check for level up
checkLevelUp();
}
}
// Check collisions with obstacles
for (var j = 0; j < obstacles.length; j++) {
var obstacle = obstacles[j];
if (player.intersects(obstacle)) {
// Player hit an obstacle
LK.getSound('collision').play();
LK.effects.flashScreen(0xe74c3c, 300);
// Reduce timer as penalty
timerBar.timeRemaining -= 3000; // 3 second penalty
// Push player away from obstacle
var dx = player.x - obstacle.x;
var dy = player.y - obstacle.y;
var angle = Math.atan2(dy, dx);
player.x += Math.cos(angle) * 100;
player.y += Math.sin(angle) * 100;
// Keep player within bounds
player.x = Math.max(40, Math.min(GAME_WIDTH - 40, player.x));
player.y = Math.max(40, Math.min(GAME_HEIGHT - 40, player.y));
}
}
}
function addScore(points) {
score += points;
scoreTxt.setText('Score: ' + score);
// Update player's score display
if (player && player.updateScore) {
player.updateScore(score);
}
// Create floating points animation
var floatingPoints = new Text2("+" + points, {
size: 60,
fill: 0x00ff00
});
floatingPoints.anchor.set(0.5, 0.5);
floatingPoints.x = player.x;
floatingPoints.y = player.y - 100;
game.addChild(floatingPoints);
// Animate the floating points
tween(floatingPoints, {
y: floatingPoints.y - 150,
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(floatingPoints);
}
});
// Flash score text
tween(scoreTxt, {
scale: 1.2
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(scoreTxt, {
scale: 1
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
function checkLevelUp() {
// Check if player reached 2500 points to start boss fight
if (score >= 2500 && !inBossFight && !bossDefeated) {
startBossFight();
return;
}
// Check for weapon upgrades first
if (score >= nextWeaponUpgrade) {
// Find next weapon upgrade
var upgrade = null;
for (var i = 0; i < WEAPON_UPGRADES.length; i++) {
if (WEAPON_UPGRADES[i].level > weaponLevel) {
upgrade = WEAPON_UPGRADES[i];
break;
}
}
if (upgrade) {
weaponLevel = upgrade.level;
nextWeaponUpgrade += WEAPON_UPGRADE_SCORE * weaponLevel;
// Update player's weapon
player.updateWeapon(upgrade.type, weaponLevel);
// Show weapon upgrade notification
var upgradeText = new Text2("New Weapon: " + upgrade.name + "!", {
size: 80,
fill: 0xFFD700
});
upgradeText.anchor.set(0.5, 0.5);
upgradeText.x = GAME_WIDTH / 2;
upgradeText.y = GAME_HEIGHT / 2 - 200;
game.addChild(upgradeText);
// Animate the notification
tween(upgradeText, {
y: upgradeText.y - 100,
alpha: 0
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(upgradeText);
}
});
// Visual effect for weapon upgrade
LK.effects.flashScreen(0x00FFFF, 500);
}
}
// Regular level up check
if (score >= level * LEVEL_UP_SCORE) {
level++;
levelTxt.setText('Level: ' + level);
// Increase difficulty
cubeSpawnInterval = Math.max(500, CUBE_SPAWN_INTERVAL_BASE - (level - 1) * 150);
playerSpeed = PLAYER_SPEED_BASE + (level - 1) * 1;
obstacleSpeed = OBSTACLE_SPEED_BASE + (level - 1) * 0.5;
// Update player speed
player.speed = playerSpeed;
// Update obstacle speeds
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].setRandomSpeed(obstacleSpeed);
}
// Visual feedback for level up
LK.effects.flashScreen(0x2ecc71, 500);
// Add time bonus for leveling up
timerBar.timeRemaining += 5000; // 5 second bonus
if (timerBar.timeRemaining > timerBar.maxTime) {
timerBar.timeRemaining = timerBar.maxTime;
}
// Flash level text
tween(levelTxt, {
scale: 1.3
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(levelTxt, {
scale: 1
}, {
duration: 300,
easing: tween.easeIn
});
}
});
}
}
function gameOver() {
gameActive = false;
// Update high score
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreTxt.setText('High Score: ' + highScore);
}
// Show game over
LK.showGameOver();
}
function startBossFight() {
inBossFight = true;
// Show boss intro message
var bossText = new Text2("BOSS FIGHT!", {
size: 120,
fill: 0xFF0000
});
bossText.anchor.set(0.5, 0.5);
bossText.x = GAME_WIDTH / 2;
bossText.y = GAME_HEIGHT / 2;
game.addChild(bossText);
// Create phase indicator
var phaseText = new Text2("PHASE 1", {
size: 60,
fill: 0xFFFFFF
});
phaseText.anchor.set(0.5, 0.5);
phaseText.x = GAME_WIDTH / 2;
phaseText.y = GAME_HEIGHT / 2 + 120;
phaseText.alpha = 0;
game.addChild(phaseText);
// Fade in phase text
tween(phaseText, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeIn,
onFinish: function onFinish() {
tween(phaseText, {
alpha: 0
}, {
duration: 1000,
delay: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(phaseText);
}
});
}
});
// Stop time from decreasing during boss fight
var originalTimeRemaining = timerBar.timeRemaining;
timerBar.timeRemaining = timerBar.maxTime;
// Add dramatic flash effect
LK.effects.flashScreen(0xFF0000, 1000);
// Clear all obstacles
while (obstacles.length > 0) {
var obstacle = obstacles.pop();
game.removeChild(obstacle);
}
// Make collectableCubes scared and run away
for (var i = 0; i < collectableCubes.length; i++) {
var cube = collectableCubes[i];
// Add scared face to cube
if (cube.children[2]) {
// mouth
cube.children[2].scaleY = 0.3; // wide open mouth for fear
cube.children[2].y = 20; // adjust mouth position
}
// Make eyes wide with fear
if (cube.children[0] && cube.children[1]) {
tween(cube.children[0], {
scaleY: 0.3,
y: -15
}, {
duration: 300,
easing: tween.easeOut
});
tween(cube.children[1], {
scaleY: 0.3,
y: -15
}, {
duration: 300,
easing: tween.easeOut
});
}
// Add scared movement
cube.isScared = true;
cube.scaredSpeed = 3 + Math.random() * 2;
cube.originalUpdate = cube.update || function () {};
cube.update = function () {
this.originalUpdate();
if (this.isScared) {
// Run away from center of screen
var dx = this.x - GAME_WIDTH / 2;
var dy = this.y - GAME_HEIGHT / 2;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
this.x += dx / dist * this.scaredSpeed;
this.y += dy / dist * this.scaredSpeed;
}
// Keep inside screen
this.x = Math.max(50, Math.min(GAME_WIDTH - 50, this.x));
this.y = Math.max(50, Math.min(GAME_HEIGHT - 50, this.y));
// Shake with fear
this.rotation = (Math.random() - 0.5) * 0.2;
}
};
}
// Animate boss text then create boss
tween(bossText, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(bossText);
// Create boss - randomly select boss type
boss = new Boss();
boss.x = GAME_WIDTH / 2;
boss.y = 400;
boss.scale.x = 0;
boss.scale.y = 0;
game.addChild(boss);
// Animate boss entrance
tween(boss, {
scaleX: 1,
scaleY: 1
}, {
duration: 1000,
easing: tween.elasticOut
});
// Announce boss type
var bossTypeText = new Text2(boss.bossType.toUpperCase() + " BOSS APPEARS!", {
size: 80,
fill: boss.bossType === 'alien' ? 0x00FFFF : 0xFF6600
});
bossTypeText.anchor.set(0.5, 0.5);
bossTypeText.x = GAME_WIDTH / 2;
bossTypeText.y = GAME_HEIGHT / 2 - 200;
game.addChild(bossTypeText);
// Animate the announcement
tween(bossTypeText, {
y: bossTypeText.y - 100,
alpha: 0
}, {
duration: 2000,
delay: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(bossTypeText);
}
});
}
});
}
function updateBossFight() {
if (!boss) {
return;
}
// Update boss
boss.update(player, weaponEffects, game);
// Check if phase has changed
var lastPhase = boss.lastPhase || 1;
if (boss.phase !== lastPhase) {
// Phase transition
var phaseText = new Text2("PHASE " + boss.phase, {
size: 80,
fill: boss.phase === 2 ? 0xFF8800 : 0xFF0000
});
phaseText.anchor.set(0.5, 0.5);
phaseText.x = GAME_WIDTH / 2;
phaseText.y = GAME_HEIGHT / 2;
game.addChild(phaseText);
// Animate phase announcement
tween(phaseText, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 1500,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(phaseText);
}
});
boss.lastPhase = boss.phase;
}
// Check for weapon effects hitting the boss
for (var i = weaponEffects.length - 1; i >= 0; i--) {
var effect = weaponEffects[i];
// Skip boss projectiles
if (effect.type === 'boss') {
continue;
}
// Check if weapon hit boss
if (effect.intersects(boss)) {
// Create hit effect
var hitEffect = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
tint: effect.tint,
alpha: 0.8,
x: effect.x,
y: effect.y
});
game.addChild(hitEffect);
// Animate hit effect
tween(hitEffect, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (hitEffect.parent) {
game.removeChild(hitEffect);
}
}
});
// Remove weapon effect
game.removeChild(effect);
weaponEffects.splice(i, 1);
// Damage boss
var bossDefeated = boss.takeDamage(effect.damage);
// Add score based on current phase (more points in later phases)
addScore(5 * boss.phase);
// Check if boss is defeated
if (bossDefeated) {
defeatBoss();
break;
}
}
}
// Check for boss projectiles hitting player
for (var i = weaponEffects.length - 1; i >= 0; i--) {
var effect = weaponEffects[i];
// Only check boss projectiles
if (effect.type !== 'boss') {
continue;
}
// Check if projectile hit player
if (effect.intersects(player)) {
// Create hit effect
var hitEffect = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
tint: effect.tint,
alpha: 0.8,
x: effect.x,
y: effect.y
});
game.addChild(hitEffect);
// Animate hit effect
tween(hitEffect, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (hitEffect.parent) {
game.removeChild(hitEffect);
}
}
});
// Remove projectile
game.removeChild(effect);
weaponEffects.splice(i, 1);
// Flash screen
LK.effects.flashScreen(0xFF0000, 300);
// Reduce timer as penalty, more in later phases
timerBar.timeRemaining -= 1000 * boss.phase; // More penalty in higher phases
break;
}
}
// Update cubes that are watching the battle
for (var i = 0; i < savedCubes.length; i++) {
if (savedCubes[i].watching && savedCubes[i].update) {
savedCubes[i].update();
}
}
}
function defeatBoss() {
bossDefeated = true;
// Create explosion effect
for (var i = 0; i < 20; i++) {
var explosionPart = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
tint: 0xFF0000,
alpha: 0.8,
x: boss.x,
y: boss.y
});
game.addChild(explosionPart);
// Random direction
var angle = Math.random() * Math.PI * 2;
var distance = 100 + Math.random() * 200;
// Animate explosion parts
tween(explosionPart, {
x: explosionPart.x + Math.cos(angle) * distance,
y: explosionPart.y + Math.sin(angle) * distance,
alpha: 0,
rotation: Math.random() * Math.PI * 4
}, {
duration: 1000 + Math.random() * 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(explosionPart);
}
});
}
// Flash screen
LK.effects.flashScreen(0xFFFFFF, 1000);
// Remove boss
game.removeChild(boss);
boss = null;
// Show victory message
var victoryText = new Text2("BOSS DEFEATED!", {
size: 100,
fill: 0xFFD700
});
victoryText.anchor.set(0.5, 0.5);
victoryText.x = GAME_WIDTH / 2;
victoryText.y = GAME_HEIGHT / 2;
game.addChild(victoryText);
// Add score bonus
addScore(500);
// Animate victory text
tween(victoryText, {
y: victoryText.y - 100,
alpha: 0
}, {
duration: 3000,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(victoryText);
startEndingSequence();
}
});
// Add extra time to the timer
timerBar.timeRemaining += 30000; // 30 seconds bonus
if (timerBar.timeRemaining > timerBar.maxTime) {
timerBar.timeRemaining = timerBar.maxTime;
}
}
function startEndingSequence() {
endingSequenceStarted = true;
endingSequenceStep = 0;
endingSequenceTimer = 0;
// Create a group that will watch the player
var watchingGroup = new Container();
watchingGroup.x = GAME_WIDTH / 2;
watchingGroup.y = GAME_HEIGHT / 2 - 500;
game.addChild(watchingGroup);
// Clear all existing cubes and obstacles
while (collectableCubes.length > 0) {
var cube = collectableCubes.pop();
game.removeChild(cube);
}
while (obstacles.length > 0) {
var obstacle = obstacles.pop();
game.removeChild(obstacle);
}
// Spawn cubes of all types, some watching
for (var i = 0; i < 20; i++) {
var cube = spawnSavedCube(i % 3);
// Position the first 5 cubes in a watching group
if (i < 5) {
cube.watching = true;
cube.originalX = cube.x;
cube.originalY = cube.y;
// Position in semicircle
var angle = i / 5 * Math.PI;
var radius = 200;
cube.x = watchingGroup.x + Math.cos(angle) * radius - GAME_WIDTH / 2;
cube.y = watchingGroup.y + Math.sin(angle) * radius - 100;
// Make cube happy
if (cube.children[2]) {
// mouth
tween(cube.children[2], {
scaleY: 0.25,
y: 10
}, {
duration: 300,
easing: tween.easeOut
});
}
// Add special watching behavior
cube.originalUpdate = cube.update || function () {};
cube.update = function () {
this.originalUpdate();
// Always look at player
if (player) {
var dx = player.x - this.x;
var dy = player.y - this.y;
var angle = Math.atan2(dy, dx);
// Move eyes to look at player
if (this.children[0] && this.children[1]) {
var eyeOffsetX = Math.cos(angle) * 5;
var eyeOffsetY = Math.sin(angle) * 5;
this.children[0].x = -15 + eyeOffsetX;
this.children[0].y = -10 + eyeOffsetY;
this.children[1].x = 15 + eyeOffsetX;
this.children[1].y = -10 + eyeOffsetY;
}
}
};
}
}
// Update player
player.updateWeapon('wave', 5);
}
function spawnSavedCube(typeIndex) {
var cube = new CollectableCube();
var types = ['normal', 'silver', 'gold', 'diamond', 'platinum'];
var type = types[typeIndex % types.length];
cube.setCubeType(type);
cube.randomPosition(100, GAME_WIDTH - 100, 200, GAME_HEIGHT - 200);
// Make cubes move around
cube.speedX = (Math.random() - 0.5) * 5;
cube.speedY = (Math.random() - 0.5) * 5;
cube.update = function () {
this.x += this.speedX;
this.y += this.speedY;
// Bounce off screen edges
if (this.x < 50 || this.x > GAME_WIDTH - 50) {
this.speedX *= -1;
}
if (this.y < 50 || this.y > GAME_HEIGHT - 50) {
this.speedY *= -1;
}
};
savedCubes.push(cube);
game.addChild(cube);
// Animate cube entrance
cube.scale.x = 0;
cube.scale.y = 0;
tween(cube, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.elasticOut
});
return cube;
}
function updateEndingSequence() {
endingSequenceTimer++;
// Update saved cubes
for (var i = 0; i < savedCubes.length; i++) {
if (savedCubes[i].update) {
savedCubes[i].update();
}
}
// Progress through ending sequence steps
if (endingSequenceTimer === 120) {
// 2 seconds
endingSequenceStep = 1;
// Spawn enemies
for (var i = 0; i < 5; i++) {
spawnObstacle();
}
// Show message
var messageText = new Text2("The cubes have been saved!", {
size: 80,
fill: 0x00FFFF
});
messageText.anchor.set(0.5, 0.5);
messageText.x = GAME_WIDTH / 2;
messageText.y = GAME_HEIGHT / 2 - 400;
game.addChild(messageText);
// Animate message
tween(messageText, {
alpha: 0
}, {
duration: 3000,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(messageText);
}
});
}
if (endingSequenceTimer === 360) {
// 6 seconds
endingSequenceStep = 2;
// Create alien
var alien = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 4,
tint: 0x00FF00
});
alien.x = GAME_WIDTH / 2;
alien.y = -200;
game.addChild(alien);
// Add alien face
var leftEye = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
tint: 0x000000,
x: -50,
y: -50
});
var rightEye = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
tint: 0x000000,
x: 50,
y: -50
});
var mouth = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.1,
tint: 0x000000,
x: 0,
y: 30
});
alien.addChild(leftEye);
alien.addChild(rightEye);
alien.addChild(mouth);
// Animate alien entrance
tween(alien, {
y: 400
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Show final message
var finalText = new Text2("To be continued...", {
size: 120,
fill: 0xFFFFFF
});
finalText.anchor.set(0.5, 0.5);
finalText.x = GAME_WIDTH / 2;
finalText.y = GAME_HEIGHT / 2 + 300;
finalText.alpha = 0;
game.addChild(finalText);
// Animate final message
tween(finalText, {
alpha: 1
}, {
duration: 2000,
easing: tween.easeIn,
onFinish: function onFinish() {
// Wait 3 seconds then show game win
LK.setTimeout(function () {
LK.showYouWin();
}, 3000);
}
});
}
});
}
}
function resetGame() {
// Reset game variables
score = 0;
level = 1;
weaponLevel = 1;
nextWeaponUpgrade = WEAPON_UPGRADE_SCORE;
cubeSpawnInterval = CUBE_SPAWN_INTERVAL_BASE;
playerSpeed = PLAYER_SPEED_BASE;
obstacleSpeed = OBSTACLE_SPEED_BASE;
lastTime = Date.now();
lastCubeSpawnTime = 0;
lastObstacleSpawnTime = 0;
lastWeaponFireTime = 0;
gameActive = true;
inBossFight = false;
bossDefeated = false;
endingSequenceStarted = false;
endingSequenceStep = 0;
endingSequenceTimer = 0;
// Clear all game elements
while (collectableCubes.length > 0) {
var cube = collectableCubes.pop();
game.removeChild(cube);
}
while (obstacles.length > 0) {
var obstacle = obstacles.pop();
game.removeChild(obstacle);
}
while (weaponEffects.length > 0) {
var effect = weaponEffects.pop();
game.removeChild(effect);
}
while (savedCubes.length > 0) {
var savedCube = savedCubes.pop();
game.removeChild(savedCube);
}
if (boss) {
game.removeChild(boss);
boss = null;
}
if (player) {
game.removeChild(player);
}
if (timerBar) {
game.removeChild(timerBar);
}
// Reset UI
scoreTxt.setText('Score: 0');
levelTxt.setText('Level: 1');
highScoreTxt.setText('High Score: ' + highScore);
// Initialize game again
initGame();
}
// Event handlers
game.down = function (x, y, obj) {
if (gameActive) {
player.setTarget(x, y);
}
};
game.move = function (x, y, obj) {
if (gameActive && obj.down) {
player.setTarget(x, y);
}
};
game.update = function () {
updateGame();
// Update player
if (player) {
player.update();
}
// Update obstacles
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].update();
}
// Update weapon effects
for (var i = 0; i < weaponEffects.length; i++) {
if (weaponEffects[i].update) {
weaponEffects[i].update();
}
}
// Update saved cubes
for (var i = 0; i < savedCubes.length; i++) {
if (savedCubes[i].update) {
savedCubes[i].update();
}
}
};
// Initialize the game
initGame(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
self.bossType = Math.random() > 0.5 ? 'alien' : 'robot';
self.phase = 1; // Track boss phase (1-3)
// Initialize boss body first
var bossGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3,
tint: self.bossType === 'alien' ? 0x800080 : 0x444444 // Purple for alien, dark gray for robot
});
// Create the health bar container
self.healthBarContainer = new Container();
self.healthBarContainer.x = 2048 / 2; // Center horizontally
self.healthBarContainer.y = 150; // Fixed position at top
// Create face features based on boss type
if (self.bossType === 'alien') {
// Alien face features - large oval eyes, small mouth
var leftEye = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.25,
scaleY: 0.35,
tint: 0x00FFFF,
// Cyan glowing eyes
x: -70,
y: -40
});
var rightEye = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.25,
scaleY: 0.35,
tint: 0x00FFFF,
// Cyan glowing eyes
x: 70,
y: -40
});
var mouth = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.1,
tint: 0x000000,
// Small mouth
x: 0,
y: 40
});
// Add antenna
var antenna1 = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.08,
scaleY: 0.6,
tint: 0x00FF00,
// Green antenna
x: -50,
y: -120
});
var antenna2 = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.08,
scaleY: 0.6,
tint: 0x00FF00,
// Green antenna
x: 50,
y: -120
});
// Add some unique alien features
var thirdEye = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.15,
scaleY: 0.15,
tint: 0xFF00FF,
x: 0,
y: -70
});
self.addChild(thirdEye);
} else {
// Robot face features - square eyes, rectangular mouth, mechanical look
var leftEye = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2,
tint: 0xFF0000,
// Red eyes
x: -70,
y: -30
});
var rightEye = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2,
tint: 0xFF0000,
// Red eyes
x: 70,
y: -30
});
var mouth = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.1,
tint: 0x666666,
// Metallic mouth
x: 0,
y: 50
});
// Add mechanical parts instead of antenna
var antenna1 = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.15,
scaleY: 0.4,
tint: 0x999999,
// Metal parts
x: -90,
y: -80
});
var antenna2 = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.15,
scaleY: 0.4,
tint: 0x999999,
// Metal parts
x: 90,
y: -80
});
// Add some robot-specific features
var centerPlate = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.15,
tint: 0x333333,
x: 0,
y: 0
});
self.addChild(centerPlate);
}
// Add face features to boss
self.addChild(leftEye);
self.addChild(rightEye);
self.addChild(mouth);
self.addChild(antenna1);
self.addChild(antenna2);
// Configure the health bar container which was initialized earlier
self.healthBarContainer.x = 2048 / 2; // Center horizontally
self.healthBarContainer.y = 150; // Fixed position at top
// Boss health indicator background
var healthBarBg = LK.getAsset('timerBarBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.5,
tint: 0x333333
});
self.healthBarContainer.addChild(healthBarBg);
// Boss health indicator
var healthBar = LK.getAsset('timerBar', {
anchorX: 0,
anchorY: 0.5,
scaleX: 1,
scaleY: 0.5,
tint: 0xFF0000,
// Red health bar
x: -healthBarBg.width / 2,
y: 0,
rotation: 0 // Ensure the bar is straight
});
self.healthBarContainer.addChild(healthBar);
// Add boss name/type label
var bossLabel = new Text2(self.bossType.toUpperCase() + " BOSS", {
size: 40,
fill: 0xFFFFFF
});
bossLabel.anchor.set(0.5, 0.5);
bossLabel.y = -healthBarBg.height;
self.healthBarContainer.addChild(bossLabel);
// Store references to health bar elements
self.healthBar = healthBar;
self.healthBarBg = healthBarBg;
self.maxHealth = 100;
self.health = self.maxHealth;
self.speedX = 5;
self.speedY = 3;
self.attackCooldown = 0;
self.shootInterval = 60; // Frames between attacks
self.update = function (player, weaponEffects, game) {
// Update health bar position with game
if (self.healthBarContainer && game && self.healthBarContainer.parent !== game) {
game.addChild(self.healthBarContainer);
}
// Movement pattern based on phase
self.x += self.speedX;
self.y += self.speedY;
// Bounce off screen edges
if (self.x < bossGraphics.width / 2 || self.x > 2048 - bossGraphics.width / 2) {
self.speedX *= -1;
}
if (self.y < bossGraphics.height / 2 || self.y > 2732 - bossGraphics.height / 2) {
self.speedY *= -1;
}
// Update health bar
self.healthBar.scale.x = self.health / self.maxHealth;
// Check for phase transitions
var healthPercentage = self.health / self.maxHealth;
if (healthPercentage <= 0.66 && self.phase === 1) {
self.phase = 2;
self.transitionToPhase2();
} else if (healthPercentage <= 0.33 && self.phase === 2) {
self.phase = 3;
self.transitionToPhase3();
}
// Helper function to fire a projectile
function fireProjectile(angleOffset) {
if (!game || !weaponEffects) {
return; // Safety check
}
var projectile = new WeaponEffect();
projectile.setEffectType('boss', 1 + self.phase); // More damage in later phases
// Position at boss and aim at player
projectile.x = self.x;
projectile.y = self.y;
// Check if player exists before trying to calculate angle
if (player) {
var angle = Math.atan2(player.y - self.y, player.x - self.x);
projectile.rotation = angle + angleOffset;
} else {
// Default direction if player is null
projectile.rotation = Math.PI / 2 + angleOffset; // Down
}
game.addChild(projectile);
weaponEffects.push(projectile);
}
// Different attack patterns based on phase
if (self.attackCooldown <= 0) {
// Create enemy projectile(s)
if (self.phase === 1) {
// Phase 1: Single projectile
fireProjectile(0);
} else if (self.phase === 2) {
// Phase 2: Two projectiles at slight angles
fireProjectile(-0.2);
fireProjectile(0.2);
} else {
// Phase 3: Three projectiles in spread pattern
fireProjectile(-0.4);
fireProjectile(0);
fireProjectile(0.4);
}
// Reset cooldown - faster in later phases
self.attackCooldown = self.shootInterval;
// Expression change when attacking
mouth.scaleY = 0.3;
// Reset mouth after attack
tween(mouth, {
scaleY: self.bossType === 'alien' ? 0.1 : 0.2
}, {
duration: 300,
easing: tween.easeOut
});
} else {
self.attackCooldown--;
}
// Make boss more aggressive as health decreases
self.shootInterval = Math.max(20, Math.floor(60 * (self.health / self.maxHealth)));
};
self.transitionToPhase2 = function () {
// Visual effect for phase transition
LK.effects.flashScreen(0xFF8800, 500);
// Increase boss speed
self.speedX *= 1.3;
self.speedY *= 1.3;
// Visual changes based on boss type
if (self.bossType === 'alien') {
// Alien gets more aggressive
tween(bossGraphics, {
tint: 0xAA00AA // Deeper purple
}, {
duration: 500,
easing: tween.easeOut
});
// Eyes glow more intensely
tween(leftEye, {
tint: 0x00FFAA,
scaleX: 0.3,
scaleY: 0.4
}, {
duration: 500,
easing: tween.easeOut
});
tween(rightEye, {
tint: 0x00FFAA,
scaleX: 0.3,
scaleY: 0.4
}, {
duration: 500,
easing: tween.easeOut
});
} else {
// Robot gets more mechanical
tween(bossGraphics, {
tint: 0x666666 // Darker metal
}, {
duration: 500,
easing: tween.easeOut
});
// Eyes glow more intensely
tween(leftEye, {
tint: 0xFF6600,
scaleX: 0.25,
scaleY: 0.25
}, {
duration: 500,
easing: tween.easeOut
});
tween(rightEye, {
tint: 0xFF6600,
scaleX: 0.25,
scaleY: 0.25
}, {
duration: 500,
easing: tween.easeOut
});
}
};
self.transitionToPhase3 = function () {
// Visual effect for phase transition
LK.effects.flashScreen(0xFF0000, 500);
// Major speed increase for final phase
self.speedX *= 1.5;
self.speedY *= 1.5;
// Visual changes based on boss type
if (self.bossType === 'alien') {
// Alien becomes enraged
tween(bossGraphics, {
tint: 0xFF00FF // Bright magenta
}, {
duration: 500,
easing: tween.easeOut
});
// Eyes glow intensely
tween(leftEye, {
tint: 0xFFFF00,
scaleX: 0.35,
scaleY: 0.45
}, {
duration: 500,
easing: tween.easeOut
});
tween(rightEye, {
tint: 0xFFFF00,
scaleX: 0.35,
scaleY: 0.45
}, {
duration: 500,
easing: tween.easeOut
});
// Mouth becomes more aggressive
tween(mouth, {
scaleX: 0.6,
scaleY: 0.2
}, {
duration: 500,
easing: tween.easeOut
});
} else {
// Robot enters combat mode
tween(bossGraphics, {
tint: 0x990000 // Dark red
}, {
duration: 500,
easing: tween.easeOut
});
// Eyes glow intensely
tween(leftEye, {
tint: 0xFFFF00,
scaleX: 0.3,
scaleY: 0.3
}, {
duration: 500,
easing: tween.easeOut
});
tween(rightEye, {
tint: 0xFFFF00,
scaleX: 0.3,
scaleY: 0.3
}, {
duration: 500,
easing: tween.easeOut
});
// Mouth becomes more aggressive
tween(mouth, {
scaleX: 0.8,
scaleY: 0.15
}, {
duration: 500,
easing: tween.easeOut
});
}
};
self.takeDamage = function (damage) {
self.health -= damage;
// Update health bar immediately
self.healthBar.scale.x = self.health / self.maxHealth;
// Shake boss when taking damage
var originalX = self.x;
var originalY = self.y;
tween(self, {
x: originalX + (Math.random() * 30 - 15),
y: originalY + (Math.random() * 30 - 15)
}, {
duration: 50,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
x: originalX,
y: originalY
}, {
duration: 50,
easing: tween.easeIn
});
}
});
// Flash red when taking damage
var currentTint = bossGraphics.tint;
tween(bossGraphics, {
tint: 0xFF0000
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(bossGraphics, {
tint: currentTint
}, {
duration: 100,
easing: tween.easeIn
});
}
});
// Return true if boss is defeated
return self.health <= 0;
};
return self;
});
var CollectableCube = Container.expand(function () {
var self = Container.call(this);
var cubeGraphics = self.attachAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00ffff // Cyan color for the cube
});
// Create face features
var leftEye = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2,
tint: 0x000000,
// Black eyes
x: -15,
y: -10
});
var rightEye = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2,
tint: 0x000000,
// Black eyes
x: 15,
y: -10
});
var mouth = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.15,
tint: 0x000000,
// Black mouth
x: 0,
y: 15
});
// Add the face features to the cube
self.addChild(leftEye);
self.addChild(rightEye);
self.addChild(mouth);
// Add point value text
var pointsTxt = new Text2('+10', {
size: 30,
fill: 0xFFFFFF
});
pointsTxt.anchor.set(0.5, 0.5);
pointsTxt.y = -40;
self.addChild(pointsTxt);
self.value = 10;
self.cubeType = 'normal';
self.spawnTime = 0;
self.pulseAnimation = function () {
tween(cubeGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(cubeGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
};
self.randomPosition = function (minX, maxX, minY, maxY) {
self.x = minX + Math.random() * (maxX - minX);
self.y = minY + Math.random() * (maxY - minY);
self.spawnTime = Date.now();
self.pulseAnimation();
};
self.setCubeType = function (type) {
self.cubeType = type;
switch (type) {
case 'silver':
self.value = 20;
pointsTxt.setText('+20');
cubeGraphics.tint = 0xC0C0C0; // Silver
break;
case 'gold':
self.value = 30;
pointsTxt.setText('+30');
cubeGraphics.tint = 0xFFD700; // Gold
break;
case 'diamond':
self.value = 50;
pointsTxt.setText('+50');
cubeGraphics.tint = 0x87CEEB; // Sky blue diamond
// Add sparkle effect
self.sparkleTimer = 0;
break;
case 'platinum':
self.value = 100;
pointsTxt.setText('+100');
cubeGraphics.tint = 0xE5E4E2; // Platinum color
// Add glow effect
self.glowTimer = 0;
break;
case 'normal':
default:
self.value = 10;
pointsTxt.setText('+10');
cubeGraphics.tint = 0x00FFFF; // Cyan
break;
}
// Update the facial expression based on cube type
if (type === 'platinum') {
// Ecstatic expression for platinum cubes
mouth.scaleY = 0.3;
mouth.y = 5;
// Make eyes sparkle
leftEye.tint = 0xFFD700;
rightEye.tint = 0xFFD700;
} else if (type === 'diamond') {
// Very happy expression for diamond cubes
mouth.scaleY = 0.25;
mouth.y = 8;
// Make eyes bright
leftEye.tint = 0x87CEEB;
rightEye.tint = 0x87CEEB;
} else if (type === 'gold') {
// Happy expression for gold cubes
mouth.scaleY = 0.2;
mouth.y = 10;
} else if (type === 'silver') {
// Neutral expression for silver cubes
mouth.scaleY = 0.15;
mouth.y = 15;
} else {
// Normal expression
mouth.scaleY = 0.15;
mouth.y = 15;
}
};
// Add special effects update method
self.updateSpecialEffects = function () {
if (self.cubeType === 'diamond' && self.sparkleTimer !== undefined) {
self.sparkleTimer++;
if (self.sparkleTimer % 20 === 0) {
// Create sparkle effect
var sparkle = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
tint: 0xFFFFFF,
alpha: 1,
x: self.x + (Math.random() - 0.5) * 60,
y: self.y + (Math.random() - 0.5) * 60
});
if (self.parent) {
self.parent.addChild(sparkle);
tween(sparkle, {
alpha: 0,
scaleX: 0.3,
scaleY: 0.3
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
if (sparkle.parent) {
sparkle.parent.removeChild(sparkle);
}
}
});
}
}
} else if (self.cubeType === 'platinum' && self.glowTimer !== undefined) {
self.glowTimer++;
// Pulsing glow effect
var glowIntensity = 0.8 + Math.sin(self.glowTimer * 0.1) * 0.2;
cubeGraphics.alpha = glowIntensity;
}
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xff5555 // Bright red for obstacles
});
// Create angry face for obstacle
var leftEye = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.15,
scaleY: 0.25,
tint: 0xFFFFFF,
// White eyes
x: -40,
y: -5
});
var rightEye = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.15,
scaleY: 0.25,
tint: 0xFFFFFF,
// White eyes
x: 40,
y: -5
});
var leftPupil = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.06,
scaleY: 0.12,
tint: 0x000000,
// Black pupils
x: -40,
y: -2
});
var rightPupil = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.06,
scaleY: 0.12,
tint: 0x000000,
// Black pupils
x: 40,
y: -2
});
var mouth = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.12,
tint: 0x000000,
// Black mouth
x: 0,
y: 10
});
// Add face features to obstacle
self.addChild(leftEye);
self.addChild(rightEye);
self.addChild(leftPupil);
self.addChild(rightPupil);
self.addChild(mouth);
self.speedX = 0;
self.speedY = 0;
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
// Bounce off screen edges
if (self.x < obstacleGraphics.width / 2 || self.x > 2048 - obstacleGraphics.width / 2) {
self.speedX *= -1;
}
if (self.y < obstacleGraphics.height / 2 || self.y > 2732 - obstacleGraphics.height / 2) {
self.speedY *= -1;
}
// Rotate obstacle slightly based on direction for visual effect
var targetRotation = Math.atan2(self.speedY, self.speedX) * 0.2;
self.rotation += (targetRotation - self.rotation) * 0.1;
};
self.setRandomSpeed = function (maxSpeed) {
// Ensure obstacle always moves by setting minimum speed
var minSpeed = maxSpeed * 0.3;
// Generate random speeds between min and max
self.speedX = (Math.random() > 0.5 ? 1 : -1) * (minSpeed + Math.random() * (maxSpeed - minSpeed));
self.speedY = (Math.random() > 0.5 ? 1 : -1) * (minSpeed + Math.random() * (maxSpeed - minSpeed));
// Give each obstacle a unique color variation
obstacleGraphics.tint = 0xff0000 + Math.floor(Math.random() * 0x5555);
};
self.randomPosition = function (minX, maxX, minY, maxY) {
self.x = minX + Math.random() * (maxX - minX);
self.y = minY + Math.random() * (maxY - minY);
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('playerCube', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x3498db // Bright blue for player
});
// Create face for player
var leftEye = LK.getAsset('playerCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2,
tint: 0xFFFFFF,
// White eyes
x: -20,
y: -15
});
var rightEye = LK.getAsset('playerCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2,
tint: 0xFFFFFF,
// White eyes
x: 20,
y: -15
});
var leftPupil = LK.getAsset('playerCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
tint: 0x000000,
// Black pupils
x: -20,
y: -15
});
var rightPupil = LK.getAsset('playerCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
tint: 0x000000,
// Black pupils
x: 20,
y: -15
});
var mouth = LK.getAsset('playerCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.2,
tint: 0x000000,
// Black mouth
x: 0,
y: 15
});
// Add score display on player
var scoreBubble = LK.getAsset('playerCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.5,
tint: 0xFFFFFF,
// White bubble
x: 0,
y: -60
});
// Add face features to player
self.addChild(leftEye);
self.addChild(rightEye);
self.addChild(leftPupil);
self.addChild(rightPupil);
self.addChild(mouth);
self.addChild(scoreBubble);
// Create player score display
var playerScoreTxt = new Text2('0', {
size: 32,
fill: 0x000000
});
playerScoreTxt.anchor.set(0.5, 0.5);
playerScoreTxt.y = -60;
self.addChild(playerScoreTxt);
// Add weapon display
var weaponHolder = new Container();
self.addChild(weaponHolder);
weaponHolder.y = 50;
// Current weapon
self.weapon = new Weapon();
self.weapon.setWeaponType('basic', 1);
weaponHolder.addChild(self.weapon);
// Level indicators
var levelIndicator = new Text2('Lv.1', {
size: 24,
fill: 0xFFFFFF
});
levelIndicator.anchor.set(0.5, 0.5);
levelIndicator.y = 25;
weaponHolder.addChild(levelIndicator);
self.updateScore = function (newScore) {
playerScoreTxt.setText(newScore);
};
self.updateWeapon = function (type, level) {
self.weapon.setWeaponType(type, level);
levelIndicator.setText('Lv.' + level);
// Flash weapon effect
tween(weaponHolder, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300,
easing: tween.elasticOut,
onFinish: function onFinish() {
tween(weaponHolder, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut
});
}
});
};
self.fireWeapon = function (targets, game, weaponEffects) {
if (self.weapon.canFire()) {
// Get weapon stats
var damage = self.weapon.fire();
var type = self.weapon.type;
// Create weapon effect
var effect = new WeaponEffect();
effect.setEffectType(type, damage);
// Position at player
effect.x = self.x;
effect.y = self.y;
// If we have targets, aim at the nearest one
var nearestTarget = null;
var minDistance = Number.MAX_VALUE;
for (var i = 0; i < targets.length; i++) {
var target = targets[i];
if (target) {
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
minDistance = distance;
nearestTarget = target;
}
}
}
if (nearestTarget) {
var angle = Math.atan2(nearestTarget.y - self.y, nearestTarget.x - self.x);
effect.rotation = angle;
} else {
// If no targets, fire forward
effect.rotation = 0;
}
game.addChild(effect);
weaponEffects.push(effect);
// Flash the weapon
self.weapon.alpha = 0.7;
tween(self.weapon, {
alpha: 1
}, {
duration: 200,
easing: tween.easeOut
});
return true;
}
return false;
};
self.speed = 10;
self.targetX = 0;
self.targetY = 0;
self.moving = false;
self.setTarget = function (x, y) {
self.targetX = x;
self.targetY = y;
self.moving = true;
// Animate eyes to look in move direction
var eyeAngle = Math.atan2(y - self.y, x - self.x);
var eyeOffsetX = Math.cos(eyeAngle) * 3;
var eyeOffsetY = Math.sin(eyeAngle) * 3;
tween(leftPupil, {
x: -20 + eyeOffsetX,
y: -15 + eyeOffsetY
}, {
duration: 200,
easing: tween.easeOut
});
tween(rightPupil, {
x: 20 + eyeOffsetX,
y: -15 + eyeOffsetY
}, {
duration: 200,
easing: tween.easeOut
});
};
self.update = function () {
if (self.moving) {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < self.speed) {
self.x = self.targetX;
self.y = self.targetY;
self.moving = false;
} else {
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
}
}
// Update weapon cooldown
self.weapon.update();
};
return self;
});
var TimerBar = Container.expand(function () {
var self = Container.call(this);
var background = self.attachAsset('timerBarBackground', {
anchorX: 0,
anchorY: 0.5,
alpha: 0.7 // Semi-transparent background
});
var bar = self.attachAsset('timerBar', {
anchorX: 0,
anchorY: 0.5
});
// Add clock icon
var clockIcon = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
tint: 0xFFFFFF,
x: -50,
y: 0
});
self.addChild(clockIcon);
// Add timer text
var timerText = new Text2('30s', {
size: 40,
fill: 0xFFFFFF
});
timerText.anchor.set(0, 0.5);
timerText.x = background.width + 20;
timerText.y = 0;
self.addChild(timerText);
self.maxTime = 30000; // 30 seconds in milliseconds
self.timeRemaining = self.maxTime;
self.update = function (delta) {
self.timeRemaining -= delta;
if (self.timeRemaining < 0) {
self.timeRemaining = 0;
}
var percentage = self.timeRemaining / self.maxTime;
bar.scale.x = percentage;
// Update timer text
var secondsLeft = Math.ceil(self.timeRemaining / 1000);
timerText.setText(secondsLeft + 's');
// Change color as time runs out
if (percentage < 0.2) {
bar.tint = 0xe74c3c; // Red
clockIcon.tint = 0xe74c3c;
timerText.setText(secondsLeft + 's', {
fill: 0xe74c3c
});
// Pulse animation when low on time
if (Math.floor(self.timeRemaining / 200) % 2 === 0) {
clockIcon.scale.x = 0.9;
clockIcon.scale.y = 0.9;
} else {
clockIcon.scale.x = 0.8;
clockIcon.scale.y = 0.8;
}
} else if (percentage < 0.5) {
bar.tint = 0xf39c12; // Orange
clockIcon.tint = 0xf39c12;
timerText.setText(secondsLeft + 's', {
fill: 0xf39c12
});
} else {
bar.tint = 0x2ecc71; // Green
clockIcon.tint = 0x2ecc71;
timerText.setText(secondsLeft + 's', {
fill: 0x2ecc71
});
}
};
self.reset = function () {
self.timeRemaining = self.maxTime;
bar.scale.x = 1;
bar.tint = 0x2ecc71;
clockIcon.tint = 0x2ecc71;
timerText.setText(Math.ceil(self.timeRemaining / 1000) + 's', {
fill: 0x2ecc71
});
};
return self;
});
var Weapon = Container.expand(function () {
var self = Container.call(this);
var weaponGraphics = self.attachAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.15,
tint: 0xFFFFFF
});
self.type = 'basic';
self.level = 1;
self.attackSpeed = 1;
self.damage = 1;
self.cooldown = 0;
self.setWeaponType = function (type, level) {
self.type = type;
self.level = level || 1;
switch (type) {
case 'laser':
weaponGraphics.tint = 0xFF0000; // Red laser
self.attackSpeed = 1.5;
self.damage = 1;
break;
case 'plasma':
weaponGraphics.tint = 0x00FF00; // Green plasma
self.attackSpeed = 1;
self.damage = 2;
break;
case 'wave':
weaponGraphics.tint = 0x0000FF; // Blue wave
self.attackSpeed = 0.7;
self.damage = 3;
break;
case 'basic':
default:
weaponGraphics.tint = 0xFFFFFF; // White basic
self.attackSpeed = 1;
self.damage = 1;
break;
}
// Apply level multipliers
self.attackSpeed *= 1 + (self.level - 1) * 0.1;
self.damage *= self.level;
};
self.update = function () {
if (self.cooldown > 0) {
self.cooldown--;
}
};
self.canFire = function () {
return self.cooldown <= 0;
};
self.fire = function () {
// Set cooldown based on attack speed (higher attack speed = lower cooldown)
self.cooldown = Math.round(60 / self.attackSpeed);
return self.damage;
};
return self;
});
var WeaponEffect = Container.expand(function () {
var self = Container.call(this);
var effectGraphics = self.attachAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
self.type = 'basic';
self.damage = 1;
self.speed = 15;
self.lifetime = 60; // frames
self.setEffectType = function (type, damage) {
self.type = type;
self.damage = damage || 1;
switch (type) {
case 'laser':
effectGraphics.tint = 0xFF0000; // Red laser
effectGraphics.scaleX = 0.2;
effectGraphics.scaleY = 0.5;
self.speed = 20;
break;
case 'plasma':
effectGraphics.tint = 0x00FF00; // Green plasma
effectGraphics.scaleX = 0.4;
effectGraphics.scaleY = 0.4;
self.speed = 15;
break;
case 'wave':
effectGraphics.tint = 0x0000FF; // Blue wave
effectGraphics.scaleX = 0.6;
effectGraphics.scaleY = 0.3;
self.speed = 10;
break;
case 'boss':
// Boss projectiles have different appearances based on damage level (boss phase)
var phase = damage;
if (phase === 1) {
effectGraphics.tint = 0xFF00FF; // Purple boss projectile (phase 1)
effectGraphics.scaleX = 0.4;
effectGraphics.scaleY = 0.4;
self.speed = 8;
} else if (phase === 2) {
effectGraphics.tint = 0xFF6600; // Orange boss projectile (phase 2)
effectGraphics.scaleX = 0.5;
effectGraphics.scaleY = 0.5;
self.speed = 10;
} else {
effectGraphics.tint = 0xFF0000; // Red boss projectile (phase 3)
effectGraphics.scaleX = 0.6;
effectGraphics.scaleY = 0.6;
self.speed = 12;
}
break;
case 'basic':
default:
effectGraphics.tint = 0xFFFFFF; // White basic
effectGraphics.scaleX = 0.3;
effectGraphics.scaleY = 0.3;
self.speed = 15;
break;
}
// Pulse animation
tween(effectGraphics, {
alpha: 0.7
}, {
duration: 100,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(effectGraphics, {
alpha: 1
}, {
duration: 100,
easing: tween.easeInOut
});
}
});
};
self.update = function () {
// Move in direction of rotation
self.x += Math.cos(self.rotation) * self.speed;
self.y += Math.sin(self.rotation) * self.speed;
// Update lifetime
self.lifetime--;
// Add trailing effect
if (self.lifetime % 3 === 0) {
var trail = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: effectGraphics.scaleX * 0.7,
scaleY: effectGraphics.scaleY * 0.7,
alpha: 0.5,
tint: effectGraphics.tint,
x: 0,
y: 0
});
trail.x = self.x;
trail.y = self.y;
trail.parent = self.parent;
self.parent.addChild(trail);
tween(trail, {
alpha: 0,
scaleX: trail.scaleX * 0.5,
scaleY: trail.scaleY * 0.5
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (trail.parent) {
trail.parent.removeChild(trail);
}
}
});
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x8e44ad // Vibrant purple background
});
/****
* Game Code
****/
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var PLAYER_SPEED_BASE = 10;
var OBSTACLE_SPEED_BASE = 3;
var CUBE_SPAWN_INTERVAL_BASE = 1500; // ms
var OBSTACLE_SPAWN_INTERVAL = 5000; // ms
var LEVEL_UP_SCORE = 100;
var TIME_BONUS_MAX = 50;
var TIME_BONUS_THRESHOLD = 1000; // ms
var WEAPON_UPGRADE_SCORE = 100; // Score needed for weapon upgrades
// Cube type probabilities
var CUBE_TYPES = {
normal: {
chance: 50,
value: 10
},
silver: {
chance: 25,
value: 20
},
gold: {
chance: 15,
value: 30
},
diamond: {
chance: 8,
value: 50
},
platinum: {
chance: 2,
value: 100
}
};
// Weapon types by level
var WEAPON_UPGRADES = [{
level: 1,
type: 'basic',
name: 'Basic Blaster'
}, {
level: 2,
type: 'laser',
name: 'Laser Beam'
}, {
level: 3,
type: 'plasma',
name: 'Plasma Cannon'
}, {
level: 4,
type: 'wave',
name: 'Wave Disruptor'
}];
// Game variables
var player;
var collectableCubes = [];
var obstacles = [];
var weaponEffects = [];
var timerBar;
var score = 0;
var level = 1;
var weaponLevel = 1;
var lastCubeSpawnTime = 0;
var lastObstacleSpawnTime = 0;
var lastWeaponFireTime = 0;
var cubeSpawnInterval = CUBE_SPAWN_INTERVAL_BASE;
var playerSpeed = PLAYER_SPEED_BASE;
var obstacleSpeed = OBSTACLE_SPEED_BASE;
var lastTime = Date.now();
var gameActive = true;
var highScore = storage.highScore || 0;
var nextWeaponUpgrade = WEAPON_UPGRADE_SCORE;
var boss = null;
var inBossFight = false;
var bossDefeated = false;
var savedCubes = [];
var endingSequenceStarted = false;
var endingSequenceStep = 0;
var endingSequenceTimer = 0;
// UI Elements
var scoreTxt = new Text2('Score: 0', {
size: 70,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
scoreTxt.x = -scoreTxt.width - 20;
scoreTxt.y = 20;
var levelTxt = new Text2('Level: 1', {
size: 70,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(levelTxt);
levelTxt.x = -levelTxt.width - 20;
levelTxt.y = scoreTxt.y + scoreTxt.height + 10;
var highScoreTxt = new Text2('High Score: ' + highScore, {
size: 70,
fill: 0xFFFFFF
});
highScoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(highScoreTxt);
highScoreTxt.x = -highScoreTxt.width - 20;
highScoreTxt.y = levelTxt.y + levelTxt.height + 10;
// Initialize game elements
function initGame() {
// Create colorful background patterns
createBackgroundPatterns();
// Create and position player
player = new Player();
player.x = GAME_WIDTH / 2;
player.y = GAME_HEIGHT / 2;
player.speed = playerSpeed;
game.addChild(player);
// Add a welcome animation
tween(player, {
scaleX: 0,
scaleY: 0
}, {
duration: 0
});
tween(player, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
easing: tween.elasticOut,
onFinish: function onFinish() {
tween(player, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
}
});
// Create timer bar
timerBar = new TimerBar();
timerBar.x = (GAME_WIDTH - 1800) / 2;
timerBar.y = 100;
game.addChild(timerBar);
// Start with a few cubes and obstacles
spawnCollectableCube();
spawnObstacle();
// Play background music
LK.playMusic('gameMusic');
}
function createBackgroundPatterns() {
// Create some decorative background elements
for (var i = 0; i < 20; i++) {
var decoration = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3 + Math.random() * 0.3,
scaleY: 0.3 + Math.random() * 0.3,
alpha: 0.15,
tint: Math.random() * 0xFFFFFF,
x: Math.random() * GAME_WIDTH,
y: Math.random() * GAME_HEIGHT
});
// Place decorations behind game elements
game.addChildAt(decoration, 0);
// Add subtle animation to background elements
var animDuration = 3000 + Math.random() * 5000;
tween(decoration, {
rotation: Math.random() * Math.PI * 2
}, {
duration: animDuration,
easing: tween.easeInOut
});
}
}
function updateGame() {
if (!gameActive) {
return;
}
var currentTime = Date.now();
var delta = currentTime - lastTime;
lastTime = currentTime;
// Check if we're in ending sequence
if (endingSequenceStarted) {
updateEndingSequence();
return;
}
// Check if we're in boss fight
if (inBossFight) {
updateBossFight();
// Only check timer if boss isn't defeated
if (!bossDefeated) {
// Update timer
timerBar.update(delta);
if (timerBar.timeRemaining <= 0) {
gameOver();
return;
}
}
// Continue with collision checks and updates
} else {
// Update timer
timerBar.update(delta);
if (timerBar.timeRemaining <= 0) {
gameOver();
return;
}
// Spawn new cubes
if (currentTime - lastCubeSpawnTime > cubeSpawnInterval) {
spawnCollectableCube();
lastCubeSpawnTime = currentTime;
}
// Spawn new obstacles
if (currentTime - lastObstacleSpawnTime > OBSTACLE_SPAWN_INTERVAL) {
spawnObstacle();
lastObstacleSpawnTime = currentTime;
}
}
// Fire weapon automatically
if (currentTime - lastWeaponFireTime > 1000) {
var targets = inBossFight ? [boss] : obstacles;
if (player.fireWeapon(targets, game, weaponEffects)) {
lastWeaponFireTime = currentTime;
}
}
// Update weapon effects
for (var i = weaponEffects.length - 1; i >= 0; i--) {
var effect = weaponEffects[i];
// Update effect
effect.update();
// Check if effect is expired
if (effect.lifetime <= 0) {
game.removeChild(effect);
weaponEffects.splice(i, 1);
continue;
}
// Skip boss projectile collisions with obstacles
if (effect.type === 'boss') {
continue;
}
// Check for collisions with obstacles
for (var j = 0; j < obstacles.length; j++) {
var obstacle = obstacles[j];
if (effect.intersects(obstacle)) {
// Hit effect
var hitEffect = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
tint: effect.tint,
alpha: 0.8,
x: effect.x,
y: effect.y
});
game.addChild(hitEffect);
// Animate hit effect
tween(hitEffect, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(hitEffect);
}
});
// Remove effect
game.removeChild(effect);
weaponEffects.splice(i, 1);
// Destroy obstacle
game.removeChild(obstacle);
obstacles.splice(j, 1);
// Add score for destroying obstacle
addScore(5 * weaponLevel);
break;
}
}
}
// Check for collisions
if (!endingSequenceStarted) {
checkCollisions();
}
}
function spawnCollectableCube() {
var cube = new CollectableCube();
// Determine cube type based on probabilities and level
var rand = Math.random() * 100;
var cubeType = 'normal';
// Adjust probabilities based on level (higher levels get better cubes)
var platinumChance = CUBE_TYPES.platinum.chance + Math.max(0, level - 5) * 0.5;
var diamondChance = CUBE_TYPES.diamond.chance + Math.max(0, level - 3) * 1;
var goldChance = CUBE_TYPES.gold.chance + level * 1;
var silverChance = CUBE_TYPES.silver.chance + level * 1.5;
if (rand < platinumChance) {
cubeType = 'platinum';
} else if (rand < platinumChance + diamondChance) {
cubeType = 'diamond';
} else if (rand < platinumChance + diamondChance + goldChance) {
cubeType = 'gold';
} else if (rand < platinumChance + diamondChance + goldChance + silverChance) {
cubeType = 'silver';
}
// Set the cube type
cube.setCubeType(cubeType);
cube.randomPosition(100, GAME_WIDTH - 100, 200, GAME_HEIGHT - 200);
// Make sure cube doesn't spawn on obstacles
var validPosition = false;
var maxAttempts = 10;
var attempts = 0;
while (!validPosition && attempts < maxAttempts) {
validPosition = true;
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
var dx = cube.x - obstacle.x;
var dy = cube.y - obstacle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 150) {
validPosition = false;
cube.randomPosition(100, GAME_WIDTH - 100, 200, GAME_HEIGHT - 200);
break;
}
}
attempts++;
}
collectableCubes.push(cube);
game.addChild(cube);
// Animate cube entrance
cube.scale.x = 0;
cube.scale.y = 0;
tween(cube, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.elasticOut
});
}
function spawnObstacle() {
var obstacle = new Obstacle();
obstacle.randomPosition(100, GAME_WIDTH - 100, 200, GAME_HEIGHT - 200);
obstacle.setRandomSpeed(obstacleSpeed);
// Make sure obstacle doesn't spawn on player
var dx = obstacle.x - player.x;
var dy = obstacle.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 200) {
obstacle.x = GAME_WIDTH - obstacle.x;
obstacle.y = GAME_HEIGHT - obstacle.y;
}
obstacles.push(obstacle);
game.addChild(obstacle);
}
function checkCollisions() {
// Check collisions with collectables
for (var i = collectableCubes.length - 1; i >= 0; i--) {
var cube = collectableCubes[i];
// Update special effects for diamond and platinum cubes
if (cube.updateSpecialEffects) {
cube.updateSpecialEffects();
}
if (player.intersects(cube)) {
// Calculate time bonus
var collectionTime = Date.now() - cube.spawnTime;
var timeBonus = 0;
if (collectionTime < TIME_BONUS_THRESHOLD) {
timeBonus = Math.round(TIME_BONUS_MAX * (1 - collectionTime / TIME_BONUS_THRESHOLD));
}
// Special effects for rare cubes
if (cube.cubeType === 'platinum') {
LK.effects.flashScreen(0xFFD700, 500);
// Add extra time bonus for platinum
timerBar.timeRemaining += 2000;
} else if (cube.cubeType === 'diamond') {
LK.effects.flashScreen(0x87CEEB, 300);
// Add small time bonus for diamond
timerBar.timeRemaining += 1000;
}
// Add score and remove cube
addScore(cube.value + timeBonus);
game.removeChild(cube);
collectableCubes.splice(i, 1);
// Play collect sound
LK.getSound('collect').play();
// Check for level up
checkLevelUp();
}
}
// Check collisions with obstacles
for (var j = 0; j < obstacles.length; j++) {
var obstacle = obstacles[j];
if (player.intersects(obstacle)) {
// Player hit an obstacle
LK.getSound('collision').play();
LK.effects.flashScreen(0xe74c3c, 300);
// Reduce timer as penalty
timerBar.timeRemaining -= 3000; // 3 second penalty
// Push player away from obstacle
var dx = player.x - obstacle.x;
var dy = player.y - obstacle.y;
var angle = Math.atan2(dy, dx);
player.x += Math.cos(angle) * 100;
player.y += Math.sin(angle) * 100;
// Keep player within bounds
player.x = Math.max(40, Math.min(GAME_WIDTH - 40, player.x));
player.y = Math.max(40, Math.min(GAME_HEIGHT - 40, player.y));
}
}
}
function addScore(points) {
score += points;
scoreTxt.setText('Score: ' + score);
// Update player's score display
if (player && player.updateScore) {
player.updateScore(score);
}
// Create floating points animation
var floatingPoints = new Text2("+" + points, {
size: 60,
fill: 0x00ff00
});
floatingPoints.anchor.set(0.5, 0.5);
floatingPoints.x = player.x;
floatingPoints.y = player.y - 100;
game.addChild(floatingPoints);
// Animate the floating points
tween(floatingPoints, {
y: floatingPoints.y - 150,
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(floatingPoints);
}
});
// Flash score text
tween(scoreTxt, {
scale: 1.2
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(scoreTxt, {
scale: 1
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
function checkLevelUp() {
// Check if player reached 2500 points to start boss fight
if (score >= 2500 && !inBossFight && !bossDefeated) {
startBossFight();
return;
}
// Check for weapon upgrades first
if (score >= nextWeaponUpgrade) {
// Find next weapon upgrade
var upgrade = null;
for (var i = 0; i < WEAPON_UPGRADES.length; i++) {
if (WEAPON_UPGRADES[i].level > weaponLevel) {
upgrade = WEAPON_UPGRADES[i];
break;
}
}
if (upgrade) {
weaponLevel = upgrade.level;
nextWeaponUpgrade += WEAPON_UPGRADE_SCORE * weaponLevel;
// Update player's weapon
player.updateWeapon(upgrade.type, weaponLevel);
// Show weapon upgrade notification
var upgradeText = new Text2("New Weapon: " + upgrade.name + "!", {
size: 80,
fill: 0xFFD700
});
upgradeText.anchor.set(0.5, 0.5);
upgradeText.x = GAME_WIDTH / 2;
upgradeText.y = GAME_HEIGHT / 2 - 200;
game.addChild(upgradeText);
// Animate the notification
tween(upgradeText, {
y: upgradeText.y - 100,
alpha: 0
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(upgradeText);
}
});
// Visual effect for weapon upgrade
LK.effects.flashScreen(0x00FFFF, 500);
}
}
// Regular level up check
if (score >= level * LEVEL_UP_SCORE) {
level++;
levelTxt.setText('Level: ' + level);
// Increase difficulty
cubeSpawnInterval = Math.max(500, CUBE_SPAWN_INTERVAL_BASE - (level - 1) * 150);
playerSpeed = PLAYER_SPEED_BASE + (level - 1) * 1;
obstacleSpeed = OBSTACLE_SPEED_BASE + (level - 1) * 0.5;
// Update player speed
player.speed = playerSpeed;
// Update obstacle speeds
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].setRandomSpeed(obstacleSpeed);
}
// Visual feedback for level up
LK.effects.flashScreen(0x2ecc71, 500);
// Add time bonus for leveling up
timerBar.timeRemaining += 5000; // 5 second bonus
if (timerBar.timeRemaining > timerBar.maxTime) {
timerBar.timeRemaining = timerBar.maxTime;
}
// Flash level text
tween(levelTxt, {
scale: 1.3
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(levelTxt, {
scale: 1
}, {
duration: 300,
easing: tween.easeIn
});
}
});
}
}
function gameOver() {
gameActive = false;
// Update high score
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreTxt.setText('High Score: ' + highScore);
}
// Show game over
LK.showGameOver();
}
function startBossFight() {
inBossFight = true;
// Show boss intro message
var bossText = new Text2("BOSS FIGHT!", {
size: 120,
fill: 0xFF0000
});
bossText.anchor.set(0.5, 0.5);
bossText.x = GAME_WIDTH / 2;
bossText.y = GAME_HEIGHT / 2;
game.addChild(bossText);
// Create phase indicator
var phaseText = new Text2("PHASE 1", {
size: 60,
fill: 0xFFFFFF
});
phaseText.anchor.set(0.5, 0.5);
phaseText.x = GAME_WIDTH / 2;
phaseText.y = GAME_HEIGHT / 2 + 120;
phaseText.alpha = 0;
game.addChild(phaseText);
// Fade in phase text
tween(phaseText, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeIn,
onFinish: function onFinish() {
tween(phaseText, {
alpha: 0
}, {
duration: 1000,
delay: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(phaseText);
}
});
}
});
// Stop time from decreasing during boss fight
var originalTimeRemaining = timerBar.timeRemaining;
timerBar.timeRemaining = timerBar.maxTime;
// Add dramatic flash effect
LK.effects.flashScreen(0xFF0000, 1000);
// Clear all obstacles
while (obstacles.length > 0) {
var obstacle = obstacles.pop();
game.removeChild(obstacle);
}
// Make collectableCubes scared and run away
for (var i = 0; i < collectableCubes.length; i++) {
var cube = collectableCubes[i];
// Add scared face to cube
if (cube.children[2]) {
// mouth
cube.children[2].scaleY = 0.3; // wide open mouth for fear
cube.children[2].y = 20; // adjust mouth position
}
// Make eyes wide with fear
if (cube.children[0] && cube.children[1]) {
tween(cube.children[0], {
scaleY: 0.3,
y: -15
}, {
duration: 300,
easing: tween.easeOut
});
tween(cube.children[1], {
scaleY: 0.3,
y: -15
}, {
duration: 300,
easing: tween.easeOut
});
}
// Add scared movement
cube.isScared = true;
cube.scaredSpeed = 3 + Math.random() * 2;
cube.originalUpdate = cube.update || function () {};
cube.update = function () {
this.originalUpdate();
if (this.isScared) {
// Run away from center of screen
var dx = this.x - GAME_WIDTH / 2;
var dy = this.y - GAME_HEIGHT / 2;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
this.x += dx / dist * this.scaredSpeed;
this.y += dy / dist * this.scaredSpeed;
}
// Keep inside screen
this.x = Math.max(50, Math.min(GAME_WIDTH - 50, this.x));
this.y = Math.max(50, Math.min(GAME_HEIGHT - 50, this.y));
// Shake with fear
this.rotation = (Math.random() - 0.5) * 0.2;
}
};
}
// Animate boss text then create boss
tween(bossText, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(bossText);
// Create boss - randomly select boss type
boss = new Boss();
boss.x = GAME_WIDTH / 2;
boss.y = 400;
boss.scale.x = 0;
boss.scale.y = 0;
game.addChild(boss);
// Animate boss entrance
tween(boss, {
scaleX: 1,
scaleY: 1
}, {
duration: 1000,
easing: tween.elasticOut
});
// Announce boss type
var bossTypeText = new Text2(boss.bossType.toUpperCase() + " BOSS APPEARS!", {
size: 80,
fill: boss.bossType === 'alien' ? 0x00FFFF : 0xFF6600
});
bossTypeText.anchor.set(0.5, 0.5);
bossTypeText.x = GAME_WIDTH / 2;
bossTypeText.y = GAME_HEIGHT / 2 - 200;
game.addChild(bossTypeText);
// Animate the announcement
tween(bossTypeText, {
y: bossTypeText.y - 100,
alpha: 0
}, {
duration: 2000,
delay: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(bossTypeText);
}
});
}
});
}
function updateBossFight() {
if (!boss) {
return;
}
// Update boss
boss.update(player, weaponEffects, game);
// Check if phase has changed
var lastPhase = boss.lastPhase || 1;
if (boss.phase !== lastPhase) {
// Phase transition
var phaseText = new Text2("PHASE " + boss.phase, {
size: 80,
fill: boss.phase === 2 ? 0xFF8800 : 0xFF0000
});
phaseText.anchor.set(0.5, 0.5);
phaseText.x = GAME_WIDTH / 2;
phaseText.y = GAME_HEIGHT / 2;
game.addChild(phaseText);
// Animate phase announcement
tween(phaseText, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 1500,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(phaseText);
}
});
boss.lastPhase = boss.phase;
}
// Check for weapon effects hitting the boss
for (var i = weaponEffects.length - 1; i >= 0; i--) {
var effect = weaponEffects[i];
// Skip boss projectiles
if (effect.type === 'boss') {
continue;
}
// Check if weapon hit boss
if (effect.intersects(boss)) {
// Create hit effect
var hitEffect = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
tint: effect.tint,
alpha: 0.8,
x: effect.x,
y: effect.y
});
game.addChild(hitEffect);
// Animate hit effect
tween(hitEffect, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (hitEffect.parent) {
game.removeChild(hitEffect);
}
}
});
// Remove weapon effect
game.removeChild(effect);
weaponEffects.splice(i, 1);
// Damage boss
var bossDefeated = boss.takeDamage(effect.damage);
// Add score based on current phase (more points in later phases)
addScore(5 * boss.phase);
// Check if boss is defeated
if (bossDefeated) {
defeatBoss();
break;
}
}
}
// Check for boss projectiles hitting player
for (var i = weaponEffects.length - 1; i >= 0; i--) {
var effect = weaponEffects[i];
// Only check boss projectiles
if (effect.type !== 'boss') {
continue;
}
// Check if projectile hit player
if (effect.intersects(player)) {
// Create hit effect
var hitEffect = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
tint: effect.tint,
alpha: 0.8,
x: effect.x,
y: effect.y
});
game.addChild(hitEffect);
// Animate hit effect
tween(hitEffect, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (hitEffect.parent) {
game.removeChild(hitEffect);
}
}
});
// Remove projectile
game.removeChild(effect);
weaponEffects.splice(i, 1);
// Flash screen
LK.effects.flashScreen(0xFF0000, 300);
// Reduce timer as penalty, more in later phases
timerBar.timeRemaining -= 1000 * boss.phase; // More penalty in higher phases
break;
}
}
// Update cubes that are watching the battle
for (var i = 0; i < savedCubes.length; i++) {
if (savedCubes[i].watching && savedCubes[i].update) {
savedCubes[i].update();
}
}
}
function defeatBoss() {
bossDefeated = true;
// Create explosion effect
for (var i = 0; i < 20; i++) {
var explosionPart = LK.getAsset('collectableCube', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
tint: 0xFF0000,
alpha: 0.8,
x: boss.x,
y: boss.y
});
game.addChild(explosionPart);
// Random direction
var angle = Math.random() * Math.PI * 2;
var distance = 100 + Math.random() * 200;
// Animate explosion parts
tween(explosionPart, {
x: explosionPart.x + Math.cos(angle) * distance,
y: explosionPart.y + Math.sin(angle) * distance,
alpha: 0,
rotation: Math.random() * Math.PI * 4
}, {
duration: 1000 + Math.random() * 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(explosionPart);
}
});
}
// Flash screen
LK.effects.flashScreen(0xFFFFFF, 1000);
// Remove boss
game.removeChild(boss);
boss = null;
// Show victory message
var victoryText = new Text2("BOSS DEFEATED!", {
size: 100,
fill: 0xFFD700
});
victoryText.anchor.set(0.5, 0.5);
victoryText.x = GAME_WIDTH / 2;
victoryText.y = GAME_HEIGHT / 2;
game.addChild(victoryText);
// Add score bonus
addScore(500);
// Animate victory text
tween(victoryText, {
y: victoryText.y - 100,
alpha: 0
}, {
duration: 3000,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(victoryText);
startEndingSequence();
}
});
// Add extra time to the timer
timerBar.timeRemaining += 30000; // 30 seconds bonus
if (timerBar.timeRemaining > timerBar.maxTime) {
timerBar.timeRemaining = timerBar.maxTime;
}
}
function startEndingSequence() {
endingSequenceStarted = true;
endingSequenceStep = 0;
endingSequenceTimer = 0;
// Create a group that will watch the player
var watchingGroup = new Container();
watchingGroup.x = GAME_WIDTH / 2;
watchingGroup.y = GAME_HEIGHT / 2 - 500;
game.addChild(watchingGroup);
// Clear all existing cubes and obstacles
while (collectableCubes.length > 0) {
var cube = collectableCubes.pop();
game.removeChild(cube);
}
while (obstacles.length > 0) {
var obstacle = obstacles.pop();
game.removeChild(obstacle);
}
// Spawn cubes of all types, some watching
for (var i = 0; i < 20; i++) {
var cube = spawnSavedCube(i % 3);
// Position the first 5 cubes in a watching group
if (i < 5) {
cube.watching = true;
cube.originalX = cube.x;
cube.originalY = cube.y;
// Position in semicircle
var angle = i / 5 * Math.PI;
var radius = 200;
cube.x = watchingGroup.x + Math.cos(angle) * radius - GAME_WIDTH / 2;
cube.y = watchingGroup.y + Math.sin(angle) * radius - 100;
// Make cube happy
if (cube.children[2]) {
// mouth
tween(cube.children[2], {
scaleY: 0.25,
y: 10
}, {
duration: 300,
easing: tween.easeOut
});
}
// Add special watching behavior
cube.originalUpdate = cube.update || function () {};
cube.update = function () {
this.originalUpdate();
// Always look at player
if (player) {
var dx = player.x - this.x;
var dy = player.y - this.y;
var angle = Math.atan2(dy, dx);
// Move eyes to look at player
if (this.children[0] && this.children[1]) {
var eyeOffsetX = Math.cos(angle) * 5;
var eyeOffsetY = Math.sin(angle) * 5;
this.children[0].x = -15 + eyeOffsetX;
this.children[0].y = -10 + eyeOffsetY;
this.children[1].x = 15 + eyeOffsetX;
this.children[1].y = -10 + eyeOffsetY;
}
}
};
}
}
// Update player
player.updateWeapon('wave', 5);
}
function spawnSavedCube(typeIndex) {
var cube = new CollectableCube();
var types = ['normal', 'silver', 'gold', 'diamond', 'platinum'];
var type = types[typeIndex % types.length];
cube.setCubeType(type);
cube.randomPosition(100, GAME_WIDTH - 100, 200, GAME_HEIGHT - 200);
// Make cubes move around
cube.speedX = (Math.random() - 0.5) * 5;
cube.speedY = (Math.random() - 0.5) * 5;
cube.update = function () {
this.x += this.speedX;
this.y += this.speedY;
// Bounce off screen edges
if (this.x < 50 || this.x > GAME_WIDTH - 50) {
this.speedX *= -1;
}
if (this.y < 50 || this.y > GAME_HEIGHT - 50) {
this.speedY *= -1;
}
};
savedCubes.push(cube);
game.addChild(cube);
// Animate cube entrance
cube.scale.x = 0;
cube.scale.y = 0;
tween(cube, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.elasticOut
});
return cube;
}
function updateEndingSequence() {
endingSequenceTimer++;
// Update saved cubes
for (var i = 0; i < savedCubes.length; i++) {
if (savedCubes[i].update) {
savedCubes[i].update();
}
}
// Progress through ending sequence steps
if (endingSequenceTimer === 120) {
// 2 seconds
endingSequenceStep = 1;
// Spawn enemies
for (var i = 0; i < 5; i++) {
spawnObstacle();
}
// Show message
var messageText = new Text2("The cubes have been saved!", {
size: 80,
fill: 0x00FFFF
});
messageText.anchor.set(0.5, 0.5);
messageText.x = GAME_WIDTH / 2;
messageText.y = GAME_HEIGHT / 2 - 400;
game.addChild(messageText);
// Animate message
tween(messageText, {
alpha: 0
}, {
duration: 3000,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(messageText);
}
});
}
if (endingSequenceTimer === 360) {
// 6 seconds
endingSequenceStep = 2;
// Create alien
var alien = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 4,
tint: 0x00FF00
});
alien.x = GAME_WIDTH / 2;
alien.y = -200;
game.addChild(alien);
// Add alien face
var leftEye = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
tint: 0x000000,
x: -50,
y: -50
});
var rightEye = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
tint: 0x000000,
x: 50,
y: -50
});
var mouth = LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.1,
tint: 0x000000,
x: 0,
y: 30
});
alien.addChild(leftEye);
alien.addChild(rightEye);
alien.addChild(mouth);
// Animate alien entrance
tween(alien, {
y: 400
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Show final message
var finalText = new Text2("To be continued...", {
size: 120,
fill: 0xFFFFFF
});
finalText.anchor.set(0.5, 0.5);
finalText.x = GAME_WIDTH / 2;
finalText.y = GAME_HEIGHT / 2 + 300;
finalText.alpha = 0;
game.addChild(finalText);
// Animate final message
tween(finalText, {
alpha: 1
}, {
duration: 2000,
easing: tween.easeIn,
onFinish: function onFinish() {
// Wait 3 seconds then show game win
LK.setTimeout(function () {
LK.showYouWin();
}, 3000);
}
});
}
});
}
}
function resetGame() {
// Reset game variables
score = 0;
level = 1;
weaponLevel = 1;
nextWeaponUpgrade = WEAPON_UPGRADE_SCORE;
cubeSpawnInterval = CUBE_SPAWN_INTERVAL_BASE;
playerSpeed = PLAYER_SPEED_BASE;
obstacleSpeed = OBSTACLE_SPEED_BASE;
lastTime = Date.now();
lastCubeSpawnTime = 0;
lastObstacleSpawnTime = 0;
lastWeaponFireTime = 0;
gameActive = true;
inBossFight = false;
bossDefeated = false;
endingSequenceStarted = false;
endingSequenceStep = 0;
endingSequenceTimer = 0;
// Clear all game elements
while (collectableCubes.length > 0) {
var cube = collectableCubes.pop();
game.removeChild(cube);
}
while (obstacles.length > 0) {
var obstacle = obstacles.pop();
game.removeChild(obstacle);
}
while (weaponEffects.length > 0) {
var effect = weaponEffects.pop();
game.removeChild(effect);
}
while (savedCubes.length > 0) {
var savedCube = savedCubes.pop();
game.removeChild(savedCube);
}
if (boss) {
game.removeChild(boss);
boss = null;
}
if (player) {
game.removeChild(player);
}
if (timerBar) {
game.removeChild(timerBar);
}
// Reset UI
scoreTxt.setText('Score: 0');
levelTxt.setText('Level: 1');
highScoreTxt.setText('High Score: ' + highScore);
// Initialize game again
initGame();
}
// Event handlers
game.down = function (x, y, obj) {
if (gameActive) {
player.setTarget(x, y);
}
};
game.move = function (x, y, obj) {
if (gameActive && obj.down) {
player.setTarget(x, y);
}
};
game.update = function () {
updateGame();
// Update player
if (player) {
player.update();
}
// Update obstacles
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].update();
}
// Update weapon effects
for (var i = 0; i < weaponEffects.length; i++) {
if (weaponEffects[i].update) {
weaponEffects[i].update();
}
}
// Update saved cubes
for (var i = 0; i < savedCubes.length; i++) {
if (savedCubes[i].update) {
savedCubes[i].update();
}
}
};
// Initialize the game
initGame();