/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Alien = Container.expand(function () {
var self = Container.call(this);
// Create both movement sprites
var alienMove1 = self.attachAsset('alienMove1', {
anchorX: 0.5,
anchorY: 0.5
});
var alienMove2 = self.attachAsset('alienMove2', {
anchorX: 0.5,
anchorY: 0.5
});
// Initially show first movement sprite, hide second
alienMove2.visible = false;
self.speed = 1;
self.dropDistance = 40;
self.shootChance = 0.0005; // Small chance to shoot each frame
self.animationTimer = 0;
self.animationDuration = 20; // Switch sprites every 20 ticks for smoother movement animation
self.currentSprite = 1; // Track which sprite is currently visible
self.update = function () {
// Move alien down continuously
self.y += 0.3;
// Move alien horizontally
self.x += alienDirection * alienSpeed;
// Handle movement sprite animation
self.animationTimer++;
if (self.animationTimer >= self.animationDuration) {
self.animationTimer = 0;
// Switch between movement sprites
if (self.currentSprite === 1) {
alienMove1.visible = false;
alienMove2.visible = true;
self.currentSprite = 2;
} else {
alienMove1.visible = true;
alienMove2.visible = false;
self.currentSprite = 1;
}
}
// Random shooting
if (Math.random() < self.shootChance) {
createAlienBullet(self.x, self.y);
}
};
return self;
});
var AlienBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('alienBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
};
return self;
});
var AlienType2 = Container.expand(function () {
var self = Container.call(this);
// Create both movement sprites for type 2
var alienMove1 = self.attachAsset('alienType2Move1', {
anchorX: 0.5,
anchorY: 0.5
});
var alienMove2 = self.attachAsset('alienType2Move2', {
anchorX: 0.5,
anchorY: 0.5
});
// Initially show first movement sprite, hide second
alienMove2.visible = false;
self.speed = 1.5; // Faster than type 1
self.dropDistance = 40;
self.shootChance = 0.001; // Higher chance to shoot
self.animationTimer = 0;
self.animationDuration = 15; // Faster animation
self.currentSprite = 1;
self.update = function () {
// Move alien down continuously (faster)
self.y += 0.5;
// Move alien horizontally
self.x += alienDirection * alienSpeed;
// Handle movement sprite animation
self.animationTimer++;
if (self.animationTimer >= self.animationDuration) {
self.animationTimer = 0;
// Switch between movement sprites
if (self.currentSprite === 1) {
alienMove1.visible = false;
alienMove2.visible = true;
self.currentSprite = 2;
} else {
alienMove1.visible = true;
alienMove2.visible = false;
self.currentSprite = 1;
}
}
// Random shooting
if (Math.random() < self.shootChance) {
createAlienBullet(self.x, self.y);
}
};
return self;
});
var AlienType3 = Container.expand(function () {
var self = Container.call(this);
// Create both movement sprites for type 3
var alienMove1 = self.attachAsset('alienType3Move1', {
anchorX: 0.5,
anchorY: 0.5
});
var alienMove2 = self.attachAsset('alienType3Move2', {
anchorX: 0.5,
anchorY: 0.5
});
// Initially show first movement sprite, hide second
alienMove2.visible = false;
self.speed = 0.8; // Slower but tougher
self.dropDistance = 40;
self.shootChance = 0.0015; // Highest chance to shoot
self.animationTimer = 0;
self.animationDuration = 25; // Slower animation
self.currentSprite = 1;
self.health = 2; // Takes 2 hits to destroy
self.update = function () {
// Move alien down continuously (slower)
self.y += 0.2;
// Move alien horizontally
self.x += alienDirection * alienSpeed;
// Handle movement sprite animation
self.animationTimer++;
if (self.animationTimer >= self.animationDuration) {
self.animationTimer = 0;
// Switch between movement sprites
if (self.currentSprite === 1) {
alienMove1.visible = false;
alienMove2.visible = true;
self.currentSprite = 2;
} else {
alienMove1.visible = true;
alienMove2.visible = false;
self.currentSprite = 1;
}
}
// Random shooting
if (Math.random() < self.shootChance) {
createAlienBullet(self.x, self.y);
}
};
return self;
});
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 75;
self.maxHealth = 75;
self.speed = 2;
self.shootCooldown = 0;
self.shootRate = 30;
self.specialAttackCooldown = 0;
self.specialAttackRate = 180;
self.movementPhase = 0;
self.movementTimer = 0;
self.movementDuration = 120;
self.isDefeated = false;
self.currentPhase = 1;
self.phaseTransitionTimer = 0;
self.burstAttackTimer = 0;
self.spiralAngle = 0;
self.waveAttackTimer = 0;
self.update = function () {
if (self.isDefeated) return;
// Determine current phase based on health
var healthPercent = self.health / self.maxHealth;
var newPhase = 1;
if (healthPercent <= 0.66) newPhase = 2;
if (healthPercent <= 0.33) newPhase = 3;
// Phase transition effects
if (newPhase !== self.currentPhase) {
self.currentPhase = newPhase;
self.phaseTransitionTimer = 60; // 1 second transition
LK.effects.flashScreen(0xff0000, 500);
tween(self, {
scaleX: 1.5,
scaleY: 1.5,
tint: 0xff4444
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xffffff
}, {
duration: 300,
easing: tween.easeIn
});
}
});
}
// Skip attacks during phase transition
if (self.phaseTransitionTimer > 0) {
self.phaseTransitionTimer--;
return;
}
// Phase 1: Basic horizontal movement with single shots
if (self.currentPhase === 1) {
// Horizontal movement
self.x += self.speed;
if (self.x <= 150 || self.x >= 1898) {
self.speed *= -1;
}
// Regular shooting
self.shootCooldown--;
if (self.shootCooldown <= 0) {
self.shootCooldown = 45;
createBossBullet(self.x, self.y + 100);
}
}
// Phase 2: Circular movement with burst attacks
else if (self.currentPhase === 2) {
// Circular movement
var angle = LK.ticks / 60 * Math.PI;
self.x = 1024 + Math.cos(angle) * 250;
self.y = 350 + Math.sin(angle) * 80;
// Burst attack - 5 bullets in quick succession
self.burstAttackTimer++;
if (self.burstAttackTimer >= 120) {
// Every 2 seconds
self.burstAttackTimer = 0;
for (var burst = 0; burst < 5; burst++) {
LK.setTimeout(function (burstIndex) {
return function () {
if (!self.isDefeated) {
createBossBullet(self.x + (burstIndex - 2) * 25, self.y + 100);
}
};
}(burst), burst * 100); // 100ms delays between shots
}
}
}
// Phase 3: Aggressive patterns with spiral and wave attacks
else if (self.currentPhase === 3) {
// Aggressive dive and retreat pattern
self.movementTimer++;
if (self.movementTimer < 60) {
self.y += 2; // Dive down
} else if (self.movementTimer < 120) {
self.y -= 1; // Retreat up
} else {
self.movementTimer = 0;
}
// Horizontal aggressive movement
self.x += self.speed * 1.5;
if (self.x <= 100 || self.x >= 1948) {
self.speed *= -1;
}
// Spiral attack pattern
self.spiralAngle += 0.3;
if (LK.ticks % 20 === 0) {
// Every 20 ticks
for (var spiral = 0; spiral < 3; spiral++) {
var spiralX = self.x + Math.cos(self.spiralAngle + spiral * 2.1) * 60;
var spiralY = self.y + 100;
createBossBullet(spiralX, spiralY);
}
}
// Wave attack - sweeping bullets
self.waveAttackTimer++;
if (self.waveAttackTimer >= 180) {
// Every 3 seconds
self.waveAttackTimer = 0;
for (var wave = 0; wave < 7; wave++) {
LK.setTimeout(function (waveIndex) {
return function () {
if (!self.isDefeated) {
var waveX = 200 + waveIndex * 250;
createBossBullet(waveX, self.y + 100);
}
};
}(wave), wave * 50); // 50ms delays for wave effect
}
}
}
};
self.takeDamage = function () {
self.health--;
var healthPercent = self.health / self.maxHealth;
// Flash red when hit
tween(self, {
tint: 0xff0000
}, {
duration: 100,
onFinish: function onFinish() {
tween(self, {
tint: 0xffffff
}, {
duration: 100
});
}
});
// Scale animation when damaged
tween(self, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeIn
});
}
});
if (self.health <= 0) {
self.defeat();
}
};
self.defeat = function () {
self.isDefeated = true;
// Victory animation
tween(self, {
scaleX: 2,
scaleY: 2,
alpha: 0,
rotation: Math.PI * 2
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
LK.setScore(LK.getScore() + 1000);
scoreTxt.setText('Score: ' + LK.getScore());
LK.effects.flashScreen(0x00ff00, 2000);
LK.showYouWin();
}
});
};
return self;
});
var BossBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bossBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.update = function () {
self.y += self.speed;
};
return self;
});
var PlasmaBomb = Container.expand(function () {
var self = Container.call(this);
var bombGraphics = self.attachAsset('plasmaBomb', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -8;
self.explosionRadius = 200;
self.hasExploded = false;
self.update = function () {
self.y += self.speed;
// Pulsing animation
bombGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.3) * 0.3;
bombGraphics.scaleY = 1 + Math.sin(LK.ticks * 0.3) * 0.3;
// Glow effect
bombGraphics.alpha = 0.8 + Math.sin(LK.ticks * 0.5) * 0.2;
};
self.explode = function () {
if (self.hasExploded) return;
self.hasExploded = true;
// Create explosion visual effect
tween(self, {
scaleX: 5,
scaleY: 5,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut
});
// Screen flash for explosion
LK.effects.flashScreen(0x00ffff, 300);
};
return self;
});
var PlayerBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -12;
self.update = function () {
self.y += self.speed;
};
return self;
});
var PlayerShip = Container.expand(function () {
var self = Container.call(this);
var shipGraphics = self.attachAsset('playerShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.shootCooldown = 0;
self.shootRate = 15; // Fire every 15 ticks
self.plasmaBombCooldown = 0;
self.plasmaBombRate = 1200; // 20 seconds at 60 FPS
self.moveLeft = function () {
self.x -= self.speed;
if (self.x < 60) self.x = 60;
};
self.moveRight = function () {
self.x += self.speed;
if (self.x > 1988) self.x = 1988;
};
self.shoot = function () {
if (self.shootCooldown <= 0) {
self.shootCooldown = self.shootRate;
createPlayerBullet();
LK.getSound('shoot').play();
}
};
self.shootPlasmaBomb = function () {
if (self.plasmaBombCooldown <= 0) {
self.plasmaBombCooldown = self.plasmaBombRate;
createPlasmaBomb();
LK.getSound('shoot').play();
}
};
self.update = function () {
// Update plasma bomb cooldown
if (self.plasmaBombCooldown > 0) {
self.plasmaBombCooldown--;
}
};
return self;
});
var Star = Container.expand(function () {
var self = Container.call(this);
var starGraphics = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = Math.random() * 3 + 1; // Random speed between 1-4
self.twinkleTimer = Math.random() * 60; // Random start for twinkling
self.update = function () {
self.y += self.speed;
// Twinkling effect
self.twinkleTimer++;
starGraphics.alpha = 0.3 + Math.sin(self.twinkleTimer * 0.1) * 0.7;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000033
});
/****
* Game Code
****/
var player;
var aliens = [];
var playerBullets = [];
var alienBullets = [];
var alienDirection = 1; // 1 for right, -1 for left
var alienSpeed = 0.5;
var waveNumber = 1;
var aliensPerRow = 8;
var alienRows = 5;
var alienSpacingX = 200;
var alienSpacingY = 100;
var dragNode = null;
var boss = null;
var bossBullets = [];
var plasmaBombs = [];
var isBossWave = false;
var clickCount = 0;
var clickTimer = 0;
var maxClickTime = 120; // 2 seconds at 60 FPS to complete quintuple click
var requiredClicks = 5;
var autoShootTimer = 0;
var autoShootInterval = 60; // 1 second at 60 FPS
var stars = [];
var starSpawnTimer = 0;
var starSpawnRate = 5; // Spawn a star every 5 ticks
// Score display
var scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Wave display
var waveTxt = new Text2('Wave: 1', {
size: 60,
fill: 0xFFFFFF
});
waveTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(waveTxt);
// Initialize player
player = game.addChild(new PlayerShip());
player.x = 1024;
player.y = 2600;
function createPlayerBullet() {
var bullet = new PlayerBullet();
bullet.x = player.x;
bullet.y = player.y - 50;
bullet.lastY = bullet.y;
playerBullets.push(bullet);
game.addChild(bullet);
}
function createAlienBullet(x, y) {
var bullet = new AlienBullet();
bullet.x = x;
bullet.y = y + 40;
bullet.lastY = bullet.y;
alienBullets.push(bullet);
game.addChild(bullet);
}
function createBossBullet(x, y) {
var bullet = new BossBullet();
bullet.x = x;
bullet.y = y;
bullet.lastY = bullet.y;
bossBullets.push(bullet);
game.addChild(bullet);
}
function createPlasmaBomb() {
var bomb = new PlasmaBomb();
bomb.x = player.x;
bomb.y = player.y - 50;
bomb.lastY = bomb.y;
plasmaBombs.push(bomb);
game.addChild(bomb);
}
function createStar() {
var star = new Star();
star.x = Math.random() * 2048; // Random X position across screen width
star.y = -10; // Start just above screen
star.lastY = star.y;
stars.push(star);
game.addChild(star);
}
function spawnWave() {
// Check if it's time for boss fight
if (waveNumber > 10) {
isBossWave = true;
aliens = [];
boss = game.addChild(new Boss());
boss.x = 1024;
boss.y = 300;
waveTxt.setText('BOSS FIGHT!');
return;
}
isBossWave = false;
boss = null;
aliens = [];
var startX = (2048 - (aliensPerRow - 1) * alienSpacingX) / 2;
var startY = 200;
for (var row = 0; row < alienRows; row++) {
for (var col = 0; col < aliensPerRow; col++) {
var alien;
// Create different alien types based on row
if (row < 2) {
alien = new Alien(); // Top rows: basic aliens
} else if (row < 4) {
alien = new AlienType2(); // Middle rows: faster aliens
} else {
alien = new AlienType3(); // Bottom row: tough aliens
}
alien.x = startX + col * alienSpacingX;
alien.y = startY + row * alienSpacingY;
alien.lastY = alien.y;
alien.lastIntersectingPlayer = false;
aliens.push(alien);
game.addChild(alien);
}
}
alienSpeed += 0.2; // Increase speed each wave
waveTxt.setText('Wave: ' + waveNumber);
}
function checkAlienBounds() {
var shouldChangeDirection = false;
// Check if any alien hits the edge
for (var i = 0; i < aliens.length; i++) {
var alien = aliens[i];
if (alien.x <= 50 || alien.x >= 1998) {
shouldChangeDirection = true;
break;
}
}
if (shouldChangeDirection) {
alienDirection *= -1;
}
}
function handleMove(x, y, obj) {
if (dragNode) {
dragNode.x = x;
if (dragNode.x < 60) dragNode.x = 60;
if (dragNode.x > 1988) dragNode.x = 1988;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
dragNode = player;
handleMove(x, y, obj);
// Quintuple click detection for plasma bomb
clickCount++;
clickTimer = maxClickTime; // Reset timer on each click
if (clickCount >= requiredClicks) {
// Quintuple click achieved - activate plasma bomb
player.shootPlasmaBomb();
clickCount = 0; // Reset click count
clickTimer = 0;
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Initialize first wave
spawnWave();
game.update = function () {
// Update click timer for quintuple click detection
if (clickTimer > 0) {
clickTimer--;
if (clickTimer <= 0) {
clickCount = 0; // Reset click count if timer expires
}
}
// Spawn stars for background
starSpawnTimer++;
if (starSpawnTimer >= starSpawnRate) {
starSpawnTimer = 0;
createStar();
}
// Update and cleanup stars
for (var i = stars.length - 1; i >= 0; i--) {
var star = stars[i];
// Remove stars that go off screen
if (star.lastY <= 2752 && star.y > 2752) {
star.destroy();
stars.splice(i, 1);
continue;
}
star.lastY = star.y;
}
// Update autonomous shooting timer
autoShootTimer++;
if (autoShootTimer >= autoShootInterval) {
autoShootTimer = 0;
player.shoot();
}
// Update player shoot cooldown
if (player.shootCooldown > 0) {
player.shootCooldown--;
}
// Update player bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
// Remove bullets that go off screen
if (bullet.lastY >= -20 && bullet.y < -20) {
bullet.destroy();
playerBullets.splice(i, 1);
continue;
}
// Check collision with boss
var hit = false;
if (isBossWave && boss && !boss.isDefeated && bullet.intersects(boss)) {
boss.takeDamage();
LK.getSound('alienHit').play();
LK.setScore(LK.getScore() + 50);
scoreTxt.setText('Score: ' + LK.getScore());
bullet.destroy();
playerBullets.splice(i, 1);
hit = true;
}
// Check collision with aliens
if (!hit) {
for (var j = aliens.length - 1; j >= 0; j--) {
var alien = aliens[j];
if (bullet.intersects(alien)) {
// Handle different alien types
if (alien.health !== undefined) {
// AlienType3 with health system
alien.health--;
LK.getSound('alienHit').play();
LK.effects.flashObject(alien, 0xff0000, 200);
if (alien.health <= 0) {
LK.setScore(LK.getScore() + 30); // More points for tough aliens
alien.destroy();
aliens.splice(j, 1);
} else {
LK.setScore(LK.getScore() + 10); // Points for hitting but not destroying
}
} else {
// Regular aliens and AlienType2
var points = alien.speed > 1 ? 20 : 10; // More points for faster aliens
LK.setScore(LK.getScore() + points);
LK.getSound('alienHit').play();
LK.effects.flashObject(alien, 0xffff00, 200);
alien.destroy();
aliens.splice(j, 1);
}
scoreTxt.setText('Score: ' + LK.getScore());
bullet.destroy();
playerBullets.splice(i, 1);
hit = true;
break;
}
}
}
if (!hit) {
bullet.lastY = bullet.y;
}
}
// Update alien bullets
for (var i = alienBullets.length - 1; i >= 0; i--) {
var bullet = alienBullets[i];
// Remove bullets that go off screen
if (bullet.lastY <= 2752 && bullet.y > 2752) {
bullet.destroy();
alienBullets.splice(i, 1);
continue;
}
// Check collision with player
if (bullet.intersects(player)) {
LK.getSound('playerHit').play();
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
bullet.lastY = bullet.y;
}
// Update boss bullets
for (var i = bossBullets.length - 1; i >= 0; i--) {
var bullet = bossBullets[i];
// Remove bullets that go off screen
if (bullet.lastY <= 2752 && bullet.y > 2752) {
bullet.destroy();
bossBullets.splice(i, 1);
continue;
}
// Check collision with player
if (bullet.intersects(player)) {
LK.getSound('playerHit').play();
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
bullet.lastY = bullet.y;
}
// Check if aliens reach bottom
for (var i = 0; i < aliens.length; i++) {
var alien = aliens[i];
if (alien.lastY < 2400 && alien.y >= 2400) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
alien.lastY = alien.y;
}
// Update plasma bombs
for (var i = plasmaBombs.length - 1; i >= 0; i--) {
var bomb = plasmaBombs[i];
// Remove bombs that go off screen
if (bomb.lastY >= -50 && bomb.y < -50) {
bomb.destroy();
plasmaBombs.splice(i, 1);
continue;
}
// Check collision with aliens for explosion
var shouldExplode = false;
if (!bomb.hasExploded) {
// Check collision with aliens
for (var j = 0; j < aliens.length; j++) {
var alien = aliens[j];
if (bomb.intersects(alien)) {
shouldExplode = true;
break;
}
}
// Check collision with boss
if (isBossWave && boss && !boss.isDefeated && bomb.intersects(boss)) {
shouldExplode = true;
}
}
if (shouldExplode) {
bomb.explode();
// Damage all aliens within explosion radius
for (var j = aliens.length - 1; j >= 0; j--) {
var alien = aliens[j];
var dx = alien.x - bomb.x;
var dy = alien.y - bomb.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= bomb.explosionRadius) {
// Handle different alien types
if (alien.health !== undefined) {
alien.health -= 2; // More damage to tough aliens
LK.getSound('alienHit').play();
LK.effects.flashObject(alien, 0xff0000, 200);
if (alien.health <= 0) {
LK.setScore(LK.getScore() + 30);
alien.destroy();
aliens.splice(j, 1);
} else {
LK.setScore(LK.getScore() + 15);
}
} else {
var points = alien.speed > 1 ? 20 : 10;
LK.setScore(LK.getScore() + points);
LK.getSound('alienHit').play();
LK.effects.flashObject(alien, 0xffff00, 200);
alien.destroy();
aliens.splice(j, 1);
}
}
}
// Damage boss if in range
if (isBossWave && boss && !boss.isDefeated) {
var dx = boss.x - bomb.x;
var dy = boss.y - bomb.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= bomb.explosionRadius) {
boss.takeDamage();
boss.takeDamage(); // Double damage
boss.takeDamage(); // Triple damage from plasma bomb
LK.getSound('alienHit').play();
LK.setScore(LK.getScore() + 150);
}
}
scoreTxt.setText('Score: ' + LK.getScore());
// Remove bomb after explosion
LK.setTimeout(function () {
if (bomb && bomb.parent) {
bomb.destroy();
var bombIndex = plasmaBombs.indexOf(bomb);
if (bombIndex >= 0) {
plasmaBombs.splice(bombIndex, 1);
}
}
}, 500);
}
if (!shouldExplode) {
bomb.lastY = bomb.y;
}
}
// Check alien boundaries for direction change
checkAlienBounds();
// Check if all aliens destroyed (or boss defeated)
if (!isBossWave && aliens.length === 0) {
waveNumber++;
spawnWave();
} else if (isBossWave && boss && boss.isDefeated) {
// Boss defeated - game completed
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Alien = Container.expand(function () {
var self = Container.call(this);
// Create both movement sprites
var alienMove1 = self.attachAsset('alienMove1', {
anchorX: 0.5,
anchorY: 0.5
});
var alienMove2 = self.attachAsset('alienMove2', {
anchorX: 0.5,
anchorY: 0.5
});
// Initially show first movement sprite, hide second
alienMove2.visible = false;
self.speed = 1;
self.dropDistance = 40;
self.shootChance = 0.0005; // Small chance to shoot each frame
self.animationTimer = 0;
self.animationDuration = 20; // Switch sprites every 20 ticks for smoother movement animation
self.currentSprite = 1; // Track which sprite is currently visible
self.update = function () {
// Move alien down continuously
self.y += 0.3;
// Move alien horizontally
self.x += alienDirection * alienSpeed;
// Handle movement sprite animation
self.animationTimer++;
if (self.animationTimer >= self.animationDuration) {
self.animationTimer = 0;
// Switch between movement sprites
if (self.currentSprite === 1) {
alienMove1.visible = false;
alienMove2.visible = true;
self.currentSprite = 2;
} else {
alienMove1.visible = true;
alienMove2.visible = false;
self.currentSprite = 1;
}
}
// Random shooting
if (Math.random() < self.shootChance) {
createAlienBullet(self.x, self.y);
}
};
return self;
});
var AlienBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('alienBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
};
return self;
});
var AlienType2 = Container.expand(function () {
var self = Container.call(this);
// Create both movement sprites for type 2
var alienMove1 = self.attachAsset('alienType2Move1', {
anchorX: 0.5,
anchorY: 0.5
});
var alienMove2 = self.attachAsset('alienType2Move2', {
anchorX: 0.5,
anchorY: 0.5
});
// Initially show first movement sprite, hide second
alienMove2.visible = false;
self.speed = 1.5; // Faster than type 1
self.dropDistance = 40;
self.shootChance = 0.001; // Higher chance to shoot
self.animationTimer = 0;
self.animationDuration = 15; // Faster animation
self.currentSprite = 1;
self.update = function () {
// Move alien down continuously (faster)
self.y += 0.5;
// Move alien horizontally
self.x += alienDirection * alienSpeed;
// Handle movement sprite animation
self.animationTimer++;
if (self.animationTimer >= self.animationDuration) {
self.animationTimer = 0;
// Switch between movement sprites
if (self.currentSprite === 1) {
alienMove1.visible = false;
alienMove2.visible = true;
self.currentSprite = 2;
} else {
alienMove1.visible = true;
alienMove2.visible = false;
self.currentSprite = 1;
}
}
// Random shooting
if (Math.random() < self.shootChance) {
createAlienBullet(self.x, self.y);
}
};
return self;
});
var AlienType3 = Container.expand(function () {
var self = Container.call(this);
// Create both movement sprites for type 3
var alienMove1 = self.attachAsset('alienType3Move1', {
anchorX: 0.5,
anchorY: 0.5
});
var alienMove2 = self.attachAsset('alienType3Move2', {
anchorX: 0.5,
anchorY: 0.5
});
// Initially show first movement sprite, hide second
alienMove2.visible = false;
self.speed = 0.8; // Slower but tougher
self.dropDistance = 40;
self.shootChance = 0.0015; // Highest chance to shoot
self.animationTimer = 0;
self.animationDuration = 25; // Slower animation
self.currentSprite = 1;
self.health = 2; // Takes 2 hits to destroy
self.update = function () {
// Move alien down continuously (slower)
self.y += 0.2;
// Move alien horizontally
self.x += alienDirection * alienSpeed;
// Handle movement sprite animation
self.animationTimer++;
if (self.animationTimer >= self.animationDuration) {
self.animationTimer = 0;
// Switch between movement sprites
if (self.currentSprite === 1) {
alienMove1.visible = false;
alienMove2.visible = true;
self.currentSprite = 2;
} else {
alienMove1.visible = true;
alienMove2.visible = false;
self.currentSprite = 1;
}
}
// Random shooting
if (Math.random() < self.shootChance) {
createAlienBullet(self.x, self.y);
}
};
return self;
});
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 75;
self.maxHealth = 75;
self.speed = 2;
self.shootCooldown = 0;
self.shootRate = 30;
self.specialAttackCooldown = 0;
self.specialAttackRate = 180;
self.movementPhase = 0;
self.movementTimer = 0;
self.movementDuration = 120;
self.isDefeated = false;
self.currentPhase = 1;
self.phaseTransitionTimer = 0;
self.burstAttackTimer = 0;
self.spiralAngle = 0;
self.waveAttackTimer = 0;
self.update = function () {
if (self.isDefeated) return;
// Determine current phase based on health
var healthPercent = self.health / self.maxHealth;
var newPhase = 1;
if (healthPercent <= 0.66) newPhase = 2;
if (healthPercent <= 0.33) newPhase = 3;
// Phase transition effects
if (newPhase !== self.currentPhase) {
self.currentPhase = newPhase;
self.phaseTransitionTimer = 60; // 1 second transition
LK.effects.flashScreen(0xff0000, 500);
tween(self, {
scaleX: 1.5,
scaleY: 1.5,
tint: 0xff4444
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xffffff
}, {
duration: 300,
easing: tween.easeIn
});
}
});
}
// Skip attacks during phase transition
if (self.phaseTransitionTimer > 0) {
self.phaseTransitionTimer--;
return;
}
// Phase 1: Basic horizontal movement with single shots
if (self.currentPhase === 1) {
// Horizontal movement
self.x += self.speed;
if (self.x <= 150 || self.x >= 1898) {
self.speed *= -1;
}
// Regular shooting
self.shootCooldown--;
if (self.shootCooldown <= 0) {
self.shootCooldown = 45;
createBossBullet(self.x, self.y + 100);
}
}
// Phase 2: Circular movement with burst attacks
else if (self.currentPhase === 2) {
// Circular movement
var angle = LK.ticks / 60 * Math.PI;
self.x = 1024 + Math.cos(angle) * 250;
self.y = 350 + Math.sin(angle) * 80;
// Burst attack - 5 bullets in quick succession
self.burstAttackTimer++;
if (self.burstAttackTimer >= 120) {
// Every 2 seconds
self.burstAttackTimer = 0;
for (var burst = 0; burst < 5; burst++) {
LK.setTimeout(function (burstIndex) {
return function () {
if (!self.isDefeated) {
createBossBullet(self.x + (burstIndex - 2) * 25, self.y + 100);
}
};
}(burst), burst * 100); // 100ms delays between shots
}
}
}
// Phase 3: Aggressive patterns with spiral and wave attacks
else if (self.currentPhase === 3) {
// Aggressive dive and retreat pattern
self.movementTimer++;
if (self.movementTimer < 60) {
self.y += 2; // Dive down
} else if (self.movementTimer < 120) {
self.y -= 1; // Retreat up
} else {
self.movementTimer = 0;
}
// Horizontal aggressive movement
self.x += self.speed * 1.5;
if (self.x <= 100 || self.x >= 1948) {
self.speed *= -1;
}
// Spiral attack pattern
self.spiralAngle += 0.3;
if (LK.ticks % 20 === 0) {
// Every 20 ticks
for (var spiral = 0; spiral < 3; spiral++) {
var spiralX = self.x + Math.cos(self.spiralAngle + spiral * 2.1) * 60;
var spiralY = self.y + 100;
createBossBullet(spiralX, spiralY);
}
}
// Wave attack - sweeping bullets
self.waveAttackTimer++;
if (self.waveAttackTimer >= 180) {
// Every 3 seconds
self.waveAttackTimer = 0;
for (var wave = 0; wave < 7; wave++) {
LK.setTimeout(function (waveIndex) {
return function () {
if (!self.isDefeated) {
var waveX = 200 + waveIndex * 250;
createBossBullet(waveX, self.y + 100);
}
};
}(wave), wave * 50); // 50ms delays for wave effect
}
}
}
};
self.takeDamage = function () {
self.health--;
var healthPercent = self.health / self.maxHealth;
// Flash red when hit
tween(self, {
tint: 0xff0000
}, {
duration: 100,
onFinish: function onFinish() {
tween(self, {
tint: 0xffffff
}, {
duration: 100
});
}
});
// Scale animation when damaged
tween(self, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeIn
});
}
});
if (self.health <= 0) {
self.defeat();
}
};
self.defeat = function () {
self.isDefeated = true;
// Victory animation
tween(self, {
scaleX: 2,
scaleY: 2,
alpha: 0,
rotation: Math.PI * 2
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
LK.setScore(LK.getScore() + 1000);
scoreTxt.setText('Score: ' + LK.getScore());
LK.effects.flashScreen(0x00ff00, 2000);
LK.showYouWin();
}
});
};
return self;
});
var BossBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bossBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.update = function () {
self.y += self.speed;
};
return self;
});
var PlasmaBomb = Container.expand(function () {
var self = Container.call(this);
var bombGraphics = self.attachAsset('plasmaBomb', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -8;
self.explosionRadius = 200;
self.hasExploded = false;
self.update = function () {
self.y += self.speed;
// Pulsing animation
bombGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.3) * 0.3;
bombGraphics.scaleY = 1 + Math.sin(LK.ticks * 0.3) * 0.3;
// Glow effect
bombGraphics.alpha = 0.8 + Math.sin(LK.ticks * 0.5) * 0.2;
};
self.explode = function () {
if (self.hasExploded) return;
self.hasExploded = true;
// Create explosion visual effect
tween(self, {
scaleX: 5,
scaleY: 5,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut
});
// Screen flash for explosion
LK.effects.flashScreen(0x00ffff, 300);
};
return self;
});
var PlayerBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -12;
self.update = function () {
self.y += self.speed;
};
return self;
});
var PlayerShip = Container.expand(function () {
var self = Container.call(this);
var shipGraphics = self.attachAsset('playerShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.shootCooldown = 0;
self.shootRate = 15; // Fire every 15 ticks
self.plasmaBombCooldown = 0;
self.plasmaBombRate = 1200; // 20 seconds at 60 FPS
self.moveLeft = function () {
self.x -= self.speed;
if (self.x < 60) self.x = 60;
};
self.moveRight = function () {
self.x += self.speed;
if (self.x > 1988) self.x = 1988;
};
self.shoot = function () {
if (self.shootCooldown <= 0) {
self.shootCooldown = self.shootRate;
createPlayerBullet();
LK.getSound('shoot').play();
}
};
self.shootPlasmaBomb = function () {
if (self.plasmaBombCooldown <= 0) {
self.plasmaBombCooldown = self.plasmaBombRate;
createPlasmaBomb();
LK.getSound('shoot').play();
}
};
self.update = function () {
// Update plasma bomb cooldown
if (self.plasmaBombCooldown > 0) {
self.plasmaBombCooldown--;
}
};
return self;
});
var Star = Container.expand(function () {
var self = Container.call(this);
var starGraphics = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = Math.random() * 3 + 1; // Random speed between 1-4
self.twinkleTimer = Math.random() * 60; // Random start for twinkling
self.update = function () {
self.y += self.speed;
// Twinkling effect
self.twinkleTimer++;
starGraphics.alpha = 0.3 + Math.sin(self.twinkleTimer * 0.1) * 0.7;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000033
});
/****
* Game Code
****/
var player;
var aliens = [];
var playerBullets = [];
var alienBullets = [];
var alienDirection = 1; // 1 for right, -1 for left
var alienSpeed = 0.5;
var waveNumber = 1;
var aliensPerRow = 8;
var alienRows = 5;
var alienSpacingX = 200;
var alienSpacingY = 100;
var dragNode = null;
var boss = null;
var bossBullets = [];
var plasmaBombs = [];
var isBossWave = false;
var clickCount = 0;
var clickTimer = 0;
var maxClickTime = 120; // 2 seconds at 60 FPS to complete quintuple click
var requiredClicks = 5;
var autoShootTimer = 0;
var autoShootInterval = 60; // 1 second at 60 FPS
var stars = [];
var starSpawnTimer = 0;
var starSpawnRate = 5; // Spawn a star every 5 ticks
// Score display
var scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Wave display
var waveTxt = new Text2('Wave: 1', {
size: 60,
fill: 0xFFFFFF
});
waveTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(waveTxt);
// Initialize player
player = game.addChild(new PlayerShip());
player.x = 1024;
player.y = 2600;
function createPlayerBullet() {
var bullet = new PlayerBullet();
bullet.x = player.x;
bullet.y = player.y - 50;
bullet.lastY = bullet.y;
playerBullets.push(bullet);
game.addChild(bullet);
}
function createAlienBullet(x, y) {
var bullet = new AlienBullet();
bullet.x = x;
bullet.y = y + 40;
bullet.lastY = bullet.y;
alienBullets.push(bullet);
game.addChild(bullet);
}
function createBossBullet(x, y) {
var bullet = new BossBullet();
bullet.x = x;
bullet.y = y;
bullet.lastY = bullet.y;
bossBullets.push(bullet);
game.addChild(bullet);
}
function createPlasmaBomb() {
var bomb = new PlasmaBomb();
bomb.x = player.x;
bomb.y = player.y - 50;
bomb.lastY = bomb.y;
plasmaBombs.push(bomb);
game.addChild(bomb);
}
function createStar() {
var star = new Star();
star.x = Math.random() * 2048; // Random X position across screen width
star.y = -10; // Start just above screen
star.lastY = star.y;
stars.push(star);
game.addChild(star);
}
function spawnWave() {
// Check if it's time for boss fight
if (waveNumber > 10) {
isBossWave = true;
aliens = [];
boss = game.addChild(new Boss());
boss.x = 1024;
boss.y = 300;
waveTxt.setText('BOSS FIGHT!');
return;
}
isBossWave = false;
boss = null;
aliens = [];
var startX = (2048 - (aliensPerRow - 1) * alienSpacingX) / 2;
var startY = 200;
for (var row = 0; row < alienRows; row++) {
for (var col = 0; col < aliensPerRow; col++) {
var alien;
// Create different alien types based on row
if (row < 2) {
alien = new Alien(); // Top rows: basic aliens
} else if (row < 4) {
alien = new AlienType2(); // Middle rows: faster aliens
} else {
alien = new AlienType3(); // Bottom row: tough aliens
}
alien.x = startX + col * alienSpacingX;
alien.y = startY + row * alienSpacingY;
alien.lastY = alien.y;
alien.lastIntersectingPlayer = false;
aliens.push(alien);
game.addChild(alien);
}
}
alienSpeed += 0.2; // Increase speed each wave
waveTxt.setText('Wave: ' + waveNumber);
}
function checkAlienBounds() {
var shouldChangeDirection = false;
// Check if any alien hits the edge
for (var i = 0; i < aliens.length; i++) {
var alien = aliens[i];
if (alien.x <= 50 || alien.x >= 1998) {
shouldChangeDirection = true;
break;
}
}
if (shouldChangeDirection) {
alienDirection *= -1;
}
}
function handleMove(x, y, obj) {
if (dragNode) {
dragNode.x = x;
if (dragNode.x < 60) dragNode.x = 60;
if (dragNode.x > 1988) dragNode.x = 1988;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
dragNode = player;
handleMove(x, y, obj);
// Quintuple click detection for plasma bomb
clickCount++;
clickTimer = maxClickTime; // Reset timer on each click
if (clickCount >= requiredClicks) {
// Quintuple click achieved - activate plasma bomb
player.shootPlasmaBomb();
clickCount = 0; // Reset click count
clickTimer = 0;
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Initialize first wave
spawnWave();
game.update = function () {
// Update click timer for quintuple click detection
if (clickTimer > 0) {
clickTimer--;
if (clickTimer <= 0) {
clickCount = 0; // Reset click count if timer expires
}
}
// Spawn stars for background
starSpawnTimer++;
if (starSpawnTimer >= starSpawnRate) {
starSpawnTimer = 0;
createStar();
}
// Update and cleanup stars
for (var i = stars.length - 1; i >= 0; i--) {
var star = stars[i];
// Remove stars that go off screen
if (star.lastY <= 2752 && star.y > 2752) {
star.destroy();
stars.splice(i, 1);
continue;
}
star.lastY = star.y;
}
// Update autonomous shooting timer
autoShootTimer++;
if (autoShootTimer >= autoShootInterval) {
autoShootTimer = 0;
player.shoot();
}
// Update player shoot cooldown
if (player.shootCooldown > 0) {
player.shootCooldown--;
}
// Update player bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
// Remove bullets that go off screen
if (bullet.lastY >= -20 && bullet.y < -20) {
bullet.destroy();
playerBullets.splice(i, 1);
continue;
}
// Check collision with boss
var hit = false;
if (isBossWave && boss && !boss.isDefeated && bullet.intersects(boss)) {
boss.takeDamage();
LK.getSound('alienHit').play();
LK.setScore(LK.getScore() + 50);
scoreTxt.setText('Score: ' + LK.getScore());
bullet.destroy();
playerBullets.splice(i, 1);
hit = true;
}
// Check collision with aliens
if (!hit) {
for (var j = aliens.length - 1; j >= 0; j--) {
var alien = aliens[j];
if (bullet.intersects(alien)) {
// Handle different alien types
if (alien.health !== undefined) {
// AlienType3 with health system
alien.health--;
LK.getSound('alienHit').play();
LK.effects.flashObject(alien, 0xff0000, 200);
if (alien.health <= 0) {
LK.setScore(LK.getScore() + 30); // More points for tough aliens
alien.destroy();
aliens.splice(j, 1);
} else {
LK.setScore(LK.getScore() + 10); // Points for hitting but not destroying
}
} else {
// Regular aliens and AlienType2
var points = alien.speed > 1 ? 20 : 10; // More points for faster aliens
LK.setScore(LK.getScore() + points);
LK.getSound('alienHit').play();
LK.effects.flashObject(alien, 0xffff00, 200);
alien.destroy();
aliens.splice(j, 1);
}
scoreTxt.setText('Score: ' + LK.getScore());
bullet.destroy();
playerBullets.splice(i, 1);
hit = true;
break;
}
}
}
if (!hit) {
bullet.lastY = bullet.y;
}
}
// Update alien bullets
for (var i = alienBullets.length - 1; i >= 0; i--) {
var bullet = alienBullets[i];
// Remove bullets that go off screen
if (bullet.lastY <= 2752 && bullet.y > 2752) {
bullet.destroy();
alienBullets.splice(i, 1);
continue;
}
// Check collision with player
if (bullet.intersects(player)) {
LK.getSound('playerHit').play();
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
bullet.lastY = bullet.y;
}
// Update boss bullets
for (var i = bossBullets.length - 1; i >= 0; i--) {
var bullet = bossBullets[i];
// Remove bullets that go off screen
if (bullet.lastY <= 2752 && bullet.y > 2752) {
bullet.destroy();
bossBullets.splice(i, 1);
continue;
}
// Check collision with player
if (bullet.intersects(player)) {
LK.getSound('playerHit').play();
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
bullet.lastY = bullet.y;
}
// Check if aliens reach bottom
for (var i = 0; i < aliens.length; i++) {
var alien = aliens[i];
if (alien.lastY < 2400 && alien.y >= 2400) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
alien.lastY = alien.y;
}
// Update plasma bombs
for (var i = plasmaBombs.length - 1; i >= 0; i--) {
var bomb = plasmaBombs[i];
// Remove bombs that go off screen
if (bomb.lastY >= -50 && bomb.y < -50) {
bomb.destroy();
plasmaBombs.splice(i, 1);
continue;
}
// Check collision with aliens for explosion
var shouldExplode = false;
if (!bomb.hasExploded) {
// Check collision with aliens
for (var j = 0; j < aliens.length; j++) {
var alien = aliens[j];
if (bomb.intersects(alien)) {
shouldExplode = true;
break;
}
}
// Check collision with boss
if (isBossWave && boss && !boss.isDefeated && bomb.intersects(boss)) {
shouldExplode = true;
}
}
if (shouldExplode) {
bomb.explode();
// Damage all aliens within explosion radius
for (var j = aliens.length - 1; j >= 0; j--) {
var alien = aliens[j];
var dx = alien.x - bomb.x;
var dy = alien.y - bomb.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= bomb.explosionRadius) {
// Handle different alien types
if (alien.health !== undefined) {
alien.health -= 2; // More damage to tough aliens
LK.getSound('alienHit').play();
LK.effects.flashObject(alien, 0xff0000, 200);
if (alien.health <= 0) {
LK.setScore(LK.getScore() + 30);
alien.destroy();
aliens.splice(j, 1);
} else {
LK.setScore(LK.getScore() + 15);
}
} else {
var points = alien.speed > 1 ? 20 : 10;
LK.setScore(LK.getScore() + points);
LK.getSound('alienHit').play();
LK.effects.flashObject(alien, 0xffff00, 200);
alien.destroy();
aliens.splice(j, 1);
}
}
}
// Damage boss if in range
if (isBossWave && boss && !boss.isDefeated) {
var dx = boss.x - bomb.x;
var dy = boss.y - bomb.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= bomb.explosionRadius) {
boss.takeDamage();
boss.takeDamage(); // Double damage
boss.takeDamage(); // Triple damage from plasma bomb
LK.getSound('alienHit').play();
LK.setScore(LK.getScore() + 150);
}
}
scoreTxt.setText('Score: ' + LK.getScore());
// Remove bomb after explosion
LK.setTimeout(function () {
if (bomb && bomb.parent) {
bomb.destroy();
var bombIndex = plasmaBombs.indexOf(bomb);
if (bombIndex >= 0) {
plasmaBombs.splice(bombIndex, 1);
}
}
}, 500);
}
if (!shouldExplode) {
bomb.lastY = bomb.y;
}
}
// Check alien boundaries for direction change
checkAlienBounds();
// Check if all aliens destroyed (or boss defeated)
if (!isBossWave && aliens.length === 0) {
waveNumber++;
spawnWave();
} else if (isBossWave && boss && boss.isDefeated) {
// Boss defeated - game completed
}
};