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