/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
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 BossShip = Container.expand(function () {
var self = Container.call(this);
// Main body
var shipBody = self.attachAsset('bossShipBody', {
anchorX: 0.5,
anchorY: 0.5
});
// Wings
var wings = self.attachAsset('bossShipWings', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -20
});
// Core
var core = self.attachAsset('bossShipCore', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0
});
// Left turret
var leftTurret = self.attachAsset('bossShipTurrets', {
anchorX: 0.5,
anchorY: 0.5,
x: -80,
y: -10
});
// Right turret
var rightTurret = self.attachAsset('bossShipTurrets', {
anchorX: 0.5,
anchorY: 0.5,
x: 80,
y: -10
});
self.maxHealth = 100;
self.health = self.maxHealth;
self.speed = 1;
self.shootTimer = 0;
self.shootInterval = 30;
self.phase = 1;
self.moveTimer = 0;
self.moveDirection = 1;
self.phaseChangeTimer = 0;
// Entrance animation
self.targetY = 300;
self.y = -200;
tween(self, {
y: self.targetY
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
self.entryComplete = true;
}
});
self.update = function () {
if (!self.entryComplete) return;
// Movement pattern
self.moveTimer++;
if (self.moveTimer % 120 === 0) {
self.moveDirection *= -1;
}
self.x += self.moveDirection * self.speed;
// Keep boss within screen bounds
self.x = Math.max(150, Math.min(1898, self.x));
// Phase transitions based on health
if (self.health <= self.maxHealth * 0.66 && self.phase === 1) {
self.phase = 2;
self.shootInterval = 20;
self.speed = 1.5;
LK.effects.flashObject(self, 0xff0000, 500);
} else if (self.health <= self.maxHealth * 0.33 && self.phase === 2) {
self.phase = 3;
self.shootInterval = 15;
self.speed = 2;
LK.effects.flashObject(self, 0xff0000, 800);
}
// Shooting patterns
self.shootTimer++;
if (self.shootTimer >= self.shootInterval) {
self.shootTimer = 0;
if (self.phase === 1) {
// Single shot toward player
var bullet = new BossBullet();
bullet.x = self.x;
bullet.y = self.y + 75;
bullet.lastY = bullet.y;
bossBullets.push(bullet);
game.addChild(bullet);
} else if (self.phase === 2) {
// Triple spread shot
for (var shotIndex = 0; shotIndex < 3; shotIndex++) {
var bullet = new BossBullet();
bullet.x = self.x + (shotIndex - 1) * 50;
bullet.y = self.y + 75;
bullet.lastY = bullet.y;
bossBullets.push(bullet);
game.addChild(bullet);
}
} else if (self.phase === 3) {
// Five shot spread
for (var shotIndex = 0; shotIndex < 5; shotIndex++) {
var bullet = new BossBullet();
bullet.x = self.x + (shotIndex - 2) * 40;
bullet.y = self.y + 75;
bullet.lastY = bullet.y;
bossBullets.push(bullet);
game.addChild(bullet);
}
}
}
// Health-based visual effects
if (self.health < self.maxHealth * 0.5) {
// Damage effects for low health
if (Math.random() < 0.02) {
LK.effects.flashObject(core, 0xff0000, 100);
}
}
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
};
return self;
});
var EnemyShip = Container.expand(function () {
var self = Container.call(this);
// Main body
var shipBody = self.attachAsset('enemyShipBody', {
anchorX: 0.5,
anchorY: 0.5
});
// Wings
var wings = self.attachAsset('enemyShipWings', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -15
});
// Core/cockpit
var core = self.attachAsset('enemyShipCore', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0
});
// Thruster
var thruster = self.attachAsset('enemyShipThruster', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 30
});
self.speed = 3;
self.health = 1;
self.shootTimer = 0;
self.shootInterval = 60 + Math.random() * 120; // Random shooting interval
self.update = function () {
self.y += self.speed;
// Enemy shooting logic
self.shootTimer++;
if (self.shootTimer >= self.shootInterval) {
self.shootTimer = 0;
self.shootInterval = 60 + Math.random() * 120; // Reset with new random interval
// Create enemy bullet
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + 40;
bullet.lastY = bullet.y;
enemyBullets.push(bullet);
game.addChild(bullet);
}
};
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 = -18;
self.update = function () {
self.y += self.speed;
};
return self;
});
var PlayerShip = Container.expand(function () {
var self = Container.call(this);
// Main body
var shipBody = self.attachAsset('playerShipBody', {
anchorX: 0.5,
anchorY: 0.5
});
// Wings
var leftWing = self.attachAsset('playerShipWings', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 20
});
// Cockpit
var cockpit = self.attachAsset('playerShipCockpit', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -15
});
// Engine thrusters
var leftEngine = self.attachAsset('playerShipEngines', {
anchorX: 0.5,
anchorY: 0.5,
x: -25,
y: 35
});
var rightEngine = self.attachAsset('playerShipEngines', {
anchorX: 0.5,
anchorY: 0.5,
x: 25,
y: 35
});
// Shield visual effect (initially hidden)
var shieldGraphics = self.attachAsset('shieldEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
alpha: 0
});
self.shieldGraphics = shieldGraphics;
self.shootTimer = 0;
self.shootInterval = 15; // Shoot every 15 ticks (4 times per second)
self.update = function () {
// Auto-shooting logic
self.shootTimer++;
var currentShootInterval = playerUpgrades.rapidFire ? Math.floor(self.shootInterval / 2) : self.shootInterval;
if (self.shootTimer >= currentShootInterval) {
self.shootTimer = 0;
if (playerUpgrades.tripleShot) {
// Triple shot
for (var shotIndex = 0; shotIndex < 3; shotIndex++) {
var bullet = new PlayerBullet();
bullet.x = self.x + (shotIndex - 1) * 30; // Spread shots
bullet.y = self.y - 50;
bullet.lastY = bullet.y;
playerBullets.push(bullet);
game.addChild(bullet);
}
} else {
// Single shot
var bullet = new PlayerBullet();
bullet.x = self.x;
bullet.y = self.y - 50;
bullet.lastY = bullet.y;
playerBullets.push(bullet);
game.addChild(bullet);
}
LK.getSound('shoot').play();
}
};
return self;
});
var Upgrade = Container.expand(function (type) {
var self = Container.call(this);
self.type = type;
self.speed = 4;
var upgradeGraphics = self.attachAsset('upgrade' + type, {
anchorX: 0.5,
anchorY: 0.5
});
// Pulsing animation
tween(upgradeGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(upgradeGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Restart the pulsing animation
if (self.parent) {
tween(upgradeGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: arguments.callee.caller
});
}
}
});
}
});
self.update = function () {
self.y += self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000011
});
/****
* Game Code
****/
// Game variables
var playerBullets = [];
var enemyBullets = [];
var bossBullets = [];
var enemies = [];
var upgrades = [];
var boss = null;
var bossActive = false;
var enemySpawnTimer = 0;
var enemySpawnInterval = 120; // Start with 2 seconds between spawns
var upgradeSpawnTimer = 0;
var upgradeSpawnInterval = 600; // 10 seconds between upgrades
var dragNode = null;
var difficultyTimer = 0;
var nextBossScore = 500; // First boss at 500 points
// Upgrade system
var playerUpgrades = {
rapidFire: false,
rapidFireTimer: 0,
shield: false,
shieldTimer: 0,
tripleShot: false,
tripleShotTimer: 0
};
// Create player ship
var player = game.addChild(new PlayerShip());
player.x = 1024; // Center horizontally
player.y = 2400; // Near bottom of screen
// Create game title
var titleTxt = new Text2('SPACE INVADERS 2.0', {
size: 80,
fill: 0x00ff00
});
titleTxt.anchor.set(0.5, 0);
titleTxt.x = 0;
titleTxt.y = 20;
LK.gui.top.addChild(titleTxt);
// Create score display
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
scoreTxt.y = 120;
LK.gui.top.addChild(scoreTxt);
scoreTxt.setText(LK.getScore());
// Boss health bar (initially hidden)
var bossHealthBarBg = new Text2('', {
size: 40,
fill: 0x333333
});
bossHealthBarBg.anchor.set(0.5, 0);
bossHealthBarBg.y = 200;
LK.gui.top.addChild(bossHealthBarBg);
var bossHealthBar = new Text2('', {
size: 40,
fill: 0xff0000
});
bossHealthBar.anchor.set(0.5, 0);
bossHealthBar.y = 200;
LK.gui.top.addChild(bossHealthBar);
var bossNameTxt = new Text2('', {
size: 60,
fill: 0xff0000
});
bossNameTxt.anchor.set(0.5, 0);
bossNameTxt.y = 250;
LK.gui.top.addChild(bossNameTxt);
// Hide boss UI initially
bossHealthBarBg.alpha = 0;
bossHealthBar.alpha = 0;
bossNameTxt.alpha = 0;
// Touch/drag controls
function handleMove(x, y, obj) {
if (dragNode) {
// Keep player within screen bounds
dragNode.x = Math.max(40, Math.min(2008, x));
dragNode.y = Math.max(100, Math.min(2632, y));
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
dragNode = player;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
};
game.update = function () {
// Boss spawning logic
if (!bossActive && LK.getScore() >= nextBossScore) {
bossActive = true;
boss = new BossShip();
boss.x = 1024;
boss.y = -200;
game.addChild(boss);
LK.getSound('bossSpawn').play();
// Show boss UI
bossNameTxt.setText('DESTROYER COMMAND SHIP');
tween(bossHealthBarBg, {
alpha: 1
}, {
duration: 500
});
tween(bossHealthBar, {
alpha: 1
}, {
duration: 500
});
tween(bossNameTxt, {
alpha: 1
}, {
duration: 500
});
// Clear regular enemies during boss fight
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
enemies.splice(i, 1);
}
}
// Update boss health bar
if (bossActive && boss) {
var healthPercent = boss.health / boss.maxHealth;
var barWidth = Math.floor(healthPercent * 20);
var bgWidth = 20;
bossHealthBarBg.setText('░'.repeat(bgWidth));
bossHealthBar.setText('█'.repeat(barWidth));
}
// Update difficulty over time (only when boss is not active)
if (!bossActive) {
difficultyTimer++;
if (difficultyTimer % 600 == 0) {
// Every 10 seconds
enemySpawnInterval = Math.max(30, enemySpawnInterval - 10); // Faster spawning, minimum 0.5 seconds
}
}
// Update upgrade timers
if (playerUpgrades.rapidFire) {
playerUpgrades.rapidFireTimer--;
if (playerUpgrades.rapidFireTimer <= 0) {
playerUpgrades.rapidFire = false;
// Flash effect when upgrade expires
LK.effects.flashObject(player, 0x00ff00, 300);
}
}
if (playerUpgrades.shield) {
playerUpgrades.shieldTimer--;
if (playerUpgrades.shieldTimer <= 0) {
playerUpgrades.shield = false;
player.alpha = 1.0; // Reset transparency
// Hide shield visual effect
tween.stop(player.shieldGraphics);
tween(player.shieldGraphics, {
alpha: 0
}, {
duration: 300,
easing: tween.easeOut
});
}
}
if (playerUpgrades.tripleShot) {
playerUpgrades.tripleShotTimer--;
if (playerUpgrades.tripleShotTimer <= 0) {
playerUpgrades.tripleShot = false;
// Flash effect when upgrade expires
LK.effects.flashObject(player, 0xff0080, 300);
}
}
// Spawn upgrades
upgradeSpawnTimer++;
if (upgradeSpawnTimer >= upgradeSpawnInterval) {
upgradeSpawnTimer = 0;
var upgradeTypes = ['RapidFire', 'Shield', 'TripleShot'];
var randomType = upgradeTypes[Math.floor(Math.random() * upgradeTypes.length)];
var upgrade = new Upgrade(randomType);
upgrade.x = 100 + Math.random() * 1848;
upgrade.y = -50;
upgrade.lastY = upgrade.y;
upgrades.push(upgrade);
game.addChild(upgrade);
}
// Spawn enemies (only when boss is not active)
if (!bossActive) {
enemySpawnTimer++;
if (enemySpawnTimer >= enemySpawnInterval) {
enemySpawnTimer = 0;
var enemy = new EnemyShip();
enemy.x = 100 + Math.random() * 1848; // Random x position within screen
enemy.y = -50; // Start above screen
enemy.lastY = enemy.y;
enemy.speed = 2 + Math.random() * 3 + LK.getScore() / 50; // Increase speed with score
enemies.push(enemy);
game.addChild(enemy);
}
}
// Update and check player bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
if (bullet.lastY === undefined) bullet.lastY = bullet.y;
// Remove bullets that go off screen (increased range)
if (bullet.lastY >= -200 && bullet.y < -200) {
bullet.destroy();
playerBullets.splice(i, 1);
continue;
}
var hitTarget = false;
// Check collision with regular enemies (always check, regardless of boss state)
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
// Enemy destroyed
LK.setScore(LK.getScore() + 25);
scoreTxt.setText(LK.getScore());
// Visual effect
LK.effects.flashObject(enemy, 0xffffff, 200);
// Clean up
bullet.destroy();
enemy.destroy();
playerBullets.splice(i, 1);
enemies.splice(j, 1);
LK.getSound('enemyDestroy').play();
hitTarget = true;
break;
}
}
// Only check boss collision if no enemy was hit and boss exists
if (!hitTarget && bossActive && boss && bullet.intersects(boss)) {
boss.health -= 5;
LK.setScore(LK.getScore() + 15);
scoreTxt.setText(LK.getScore());
// Visual effect
LK.effects.flashObject(boss, 0xffffff, 150);
// Check if boss is defeated
if (boss.health <= 0) {
LK.setScore(LK.getScore() + 200); // Bonus points for boss kill
scoreTxt.setText(LK.getScore());
LK.effects.flashObject(boss, 0xffffff, 1000);
LK.getSound('bossDestroy').play();
boss.destroy();
boss = null;
bossActive = false;
nextBossScore = LK.getScore() + 1000; // Next boss after 1000 more points
// Hide boss UI
tween(bossHealthBarBg, {
alpha: 0
}, {
duration: 500
});
tween(bossHealthBar, {
alpha: 0
}, {
duration: 500
});
tween(bossNameTxt, {
alpha: 0
}, {
duration: 500
});
}
bullet.destroy();
playerBullets.splice(i, 1);
hitTarget = true;
}
// Update bullet tracking for next frame
if (!hitTarget) {
bullet.lastY = bullet.y;
}
// Update and check boss bullets
for (var i = bossBullets.length - 1; i >= 0; i--) {
var bullet = bossBullets[i];
if (bullet.lastY === undefined) bullet.lastY = bullet.y;
// Remove bullets that go off screen
if (bullet.lastY <= 2757 && bullet.y > 2757) {
bullet.destroy();
bossBullets.splice(i, 1);
continue;
}
var hitTarget = false;
// Check collision with enemy ships (boss bullets can destroy smaller ships)
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
// Enemy destroyed by boss bullet
LK.effects.flashObject(enemy, 0xffffff, 200);
bullet.destroy();
enemy.destroy();
bossBullets.splice(i, 1);
enemies.splice(j, 1);
LK.getSound('enemyDestroy').play();
hitTarget = true;
break;
}
}
if (!hitTarget) {
// Check collision with player
if (bullet.intersects(player)) {
if (!playerUpgrades.shield) {
LK.effects.flashScreen(0xff0000, 1000);
LK.getSound('playerHit').play();
LK.showGameOver();
return;
} else {
// Shield absorbs the hit
bullet.destroy();
bossBullets.splice(i, 1);
LK.effects.flashObject(player, 0x0080ff, 200);
continue;
}
}
}
if (!hitTarget) {
bullet.lastY = bullet.y;
}
}
}
// Update and check enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
if (bullet.lastY === undefined) bullet.lastY = bullet.y;
// Remove bullets that go off screen
if (bullet.lastY <= 2757 && bullet.y > 2757) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check collision with player
if (bullet.intersects(player)) {
if (!playerUpgrades.shield) {
LK.effects.flashScreen(0xff0000, 1000);
LK.getSound('playerHit').play();
LK.showGameOver();
return;
} else {
// Shield absorbs the hit
bullet.destroy();
enemyBullets.splice(i, 1);
LK.effects.flashObject(player, 0x0080ff, 200);
continue;
}
}
bullet.lastY = bullet.y;
}
// Update and check upgrades
for (var i = upgrades.length - 1; i >= 0; i--) {
var upgrade = upgrades[i];
if (upgrade.lastY === undefined) upgrade.lastY = upgrade.y;
// Remove upgrades that go off screen
if (upgrade.lastY <= 2757 && upgrade.y > 2757) {
upgrade.destroy();
upgrades.splice(i, 1);
continue;
}
// Check collision with player
if (upgrade.intersects(player)) {
LK.getSound('upgradePickup').play();
if (upgrade.type === 'RapidFire') {
playerUpgrades.rapidFire = true;
playerUpgrades.rapidFireTimer = 600; // 10 seconds
LK.effects.flashObject(player, 0x00ff00, 500);
} else if (upgrade.type === 'Shield') {
playerUpgrades.shield = true;
playerUpgrades.shieldTimer = 600; // 10 seconds
player.alpha = 0.7; // Make player semi-transparent to show shield
// Show shield visual effect
player.shieldGraphics.alpha = 0.6;
// Start pulsing animation for shield
tween(player.shieldGraphics, {
alpha: 0.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (playerUpgrades.shield) {
tween(player.shieldGraphics, {
alpha: 0.6
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: arguments.callee.caller
});
}
}
});
LK.effects.flashObject(player, 0x0080ff, 500);
} else if (upgrade.type === 'TripleShot') {
playerUpgrades.tripleShot = true;
playerUpgrades.tripleShotTimer = 600; // 10 seconds
LK.effects.flashObject(player, 0xff0080, 500);
}
upgrade.destroy();
upgrades.splice(i, 1);
continue;
}
upgrade.lastY = upgrade.y;
}
// Update and check enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.lastY === undefined) enemy.lastY = enemy.y;
// Remove enemies that go off screen
if (enemy.lastY <= 2757 && enemy.y > 2757) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
// Check collision with player
if (enemy.intersects(player)) {
if (!playerUpgrades.shield) {
LK.effects.flashScreen(0xff0000, 1000);
LK.getSound('playerHit').play();
LK.showGameOver();
return;
} else {
// Shield absorbs the hit and destroys the enemy
LK.setScore(LK.getScore() + 5); // Bonus points for shield collision
scoreTxt.setText(LK.getScore());
LK.effects.flashObject(player, 0x0080ff, 200);
enemy.destroy();
enemies.splice(i, 1);
continue;
}
}
enemy.lastY = enemy.y;
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
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 BossShip = Container.expand(function () {
var self = Container.call(this);
// Main body
var shipBody = self.attachAsset('bossShipBody', {
anchorX: 0.5,
anchorY: 0.5
});
// Wings
var wings = self.attachAsset('bossShipWings', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -20
});
// Core
var core = self.attachAsset('bossShipCore', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0
});
// Left turret
var leftTurret = self.attachAsset('bossShipTurrets', {
anchorX: 0.5,
anchorY: 0.5,
x: -80,
y: -10
});
// Right turret
var rightTurret = self.attachAsset('bossShipTurrets', {
anchorX: 0.5,
anchorY: 0.5,
x: 80,
y: -10
});
self.maxHealth = 100;
self.health = self.maxHealth;
self.speed = 1;
self.shootTimer = 0;
self.shootInterval = 30;
self.phase = 1;
self.moveTimer = 0;
self.moveDirection = 1;
self.phaseChangeTimer = 0;
// Entrance animation
self.targetY = 300;
self.y = -200;
tween(self, {
y: self.targetY
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
self.entryComplete = true;
}
});
self.update = function () {
if (!self.entryComplete) return;
// Movement pattern
self.moveTimer++;
if (self.moveTimer % 120 === 0) {
self.moveDirection *= -1;
}
self.x += self.moveDirection * self.speed;
// Keep boss within screen bounds
self.x = Math.max(150, Math.min(1898, self.x));
// Phase transitions based on health
if (self.health <= self.maxHealth * 0.66 && self.phase === 1) {
self.phase = 2;
self.shootInterval = 20;
self.speed = 1.5;
LK.effects.flashObject(self, 0xff0000, 500);
} else if (self.health <= self.maxHealth * 0.33 && self.phase === 2) {
self.phase = 3;
self.shootInterval = 15;
self.speed = 2;
LK.effects.flashObject(self, 0xff0000, 800);
}
// Shooting patterns
self.shootTimer++;
if (self.shootTimer >= self.shootInterval) {
self.shootTimer = 0;
if (self.phase === 1) {
// Single shot toward player
var bullet = new BossBullet();
bullet.x = self.x;
bullet.y = self.y + 75;
bullet.lastY = bullet.y;
bossBullets.push(bullet);
game.addChild(bullet);
} else if (self.phase === 2) {
// Triple spread shot
for (var shotIndex = 0; shotIndex < 3; shotIndex++) {
var bullet = new BossBullet();
bullet.x = self.x + (shotIndex - 1) * 50;
bullet.y = self.y + 75;
bullet.lastY = bullet.y;
bossBullets.push(bullet);
game.addChild(bullet);
}
} else if (self.phase === 3) {
// Five shot spread
for (var shotIndex = 0; shotIndex < 5; shotIndex++) {
var bullet = new BossBullet();
bullet.x = self.x + (shotIndex - 2) * 40;
bullet.y = self.y + 75;
bullet.lastY = bullet.y;
bossBullets.push(bullet);
game.addChild(bullet);
}
}
}
// Health-based visual effects
if (self.health < self.maxHealth * 0.5) {
// Damage effects for low health
if (Math.random() < 0.02) {
LK.effects.flashObject(core, 0xff0000, 100);
}
}
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
};
return self;
});
var EnemyShip = Container.expand(function () {
var self = Container.call(this);
// Main body
var shipBody = self.attachAsset('enemyShipBody', {
anchorX: 0.5,
anchorY: 0.5
});
// Wings
var wings = self.attachAsset('enemyShipWings', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -15
});
// Core/cockpit
var core = self.attachAsset('enemyShipCore', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0
});
// Thruster
var thruster = self.attachAsset('enemyShipThruster', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 30
});
self.speed = 3;
self.health = 1;
self.shootTimer = 0;
self.shootInterval = 60 + Math.random() * 120; // Random shooting interval
self.update = function () {
self.y += self.speed;
// Enemy shooting logic
self.shootTimer++;
if (self.shootTimer >= self.shootInterval) {
self.shootTimer = 0;
self.shootInterval = 60 + Math.random() * 120; // Reset with new random interval
// Create enemy bullet
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + 40;
bullet.lastY = bullet.y;
enemyBullets.push(bullet);
game.addChild(bullet);
}
};
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 = -18;
self.update = function () {
self.y += self.speed;
};
return self;
});
var PlayerShip = Container.expand(function () {
var self = Container.call(this);
// Main body
var shipBody = self.attachAsset('playerShipBody', {
anchorX: 0.5,
anchorY: 0.5
});
// Wings
var leftWing = self.attachAsset('playerShipWings', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 20
});
// Cockpit
var cockpit = self.attachAsset('playerShipCockpit', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -15
});
// Engine thrusters
var leftEngine = self.attachAsset('playerShipEngines', {
anchorX: 0.5,
anchorY: 0.5,
x: -25,
y: 35
});
var rightEngine = self.attachAsset('playerShipEngines', {
anchorX: 0.5,
anchorY: 0.5,
x: 25,
y: 35
});
// Shield visual effect (initially hidden)
var shieldGraphics = self.attachAsset('shieldEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
alpha: 0
});
self.shieldGraphics = shieldGraphics;
self.shootTimer = 0;
self.shootInterval = 15; // Shoot every 15 ticks (4 times per second)
self.update = function () {
// Auto-shooting logic
self.shootTimer++;
var currentShootInterval = playerUpgrades.rapidFire ? Math.floor(self.shootInterval / 2) : self.shootInterval;
if (self.shootTimer >= currentShootInterval) {
self.shootTimer = 0;
if (playerUpgrades.tripleShot) {
// Triple shot
for (var shotIndex = 0; shotIndex < 3; shotIndex++) {
var bullet = new PlayerBullet();
bullet.x = self.x + (shotIndex - 1) * 30; // Spread shots
bullet.y = self.y - 50;
bullet.lastY = bullet.y;
playerBullets.push(bullet);
game.addChild(bullet);
}
} else {
// Single shot
var bullet = new PlayerBullet();
bullet.x = self.x;
bullet.y = self.y - 50;
bullet.lastY = bullet.y;
playerBullets.push(bullet);
game.addChild(bullet);
}
LK.getSound('shoot').play();
}
};
return self;
});
var Upgrade = Container.expand(function (type) {
var self = Container.call(this);
self.type = type;
self.speed = 4;
var upgradeGraphics = self.attachAsset('upgrade' + type, {
anchorX: 0.5,
anchorY: 0.5
});
// Pulsing animation
tween(upgradeGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(upgradeGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Restart the pulsing animation
if (self.parent) {
tween(upgradeGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: arguments.callee.caller
});
}
}
});
}
});
self.update = function () {
self.y += self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000011
});
/****
* Game Code
****/
// Game variables
var playerBullets = [];
var enemyBullets = [];
var bossBullets = [];
var enemies = [];
var upgrades = [];
var boss = null;
var bossActive = false;
var enemySpawnTimer = 0;
var enemySpawnInterval = 120; // Start with 2 seconds between spawns
var upgradeSpawnTimer = 0;
var upgradeSpawnInterval = 600; // 10 seconds between upgrades
var dragNode = null;
var difficultyTimer = 0;
var nextBossScore = 500; // First boss at 500 points
// Upgrade system
var playerUpgrades = {
rapidFire: false,
rapidFireTimer: 0,
shield: false,
shieldTimer: 0,
tripleShot: false,
tripleShotTimer: 0
};
// Create player ship
var player = game.addChild(new PlayerShip());
player.x = 1024; // Center horizontally
player.y = 2400; // Near bottom of screen
// Create game title
var titleTxt = new Text2('SPACE INVADERS 2.0', {
size: 80,
fill: 0x00ff00
});
titleTxt.anchor.set(0.5, 0);
titleTxt.x = 0;
titleTxt.y = 20;
LK.gui.top.addChild(titleTxt);
// Create score display
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
scoreTxt.y = 120;
LK.gui.top.addChild(scoreTxt);
scoreTxt.setText(LK.getScore());
// Boss health bar (initially hidden)
var bossHealthBarBg = new Text2('', {
size: 40,
fill: 0x333333
});
bossHealthBarBg.anchor.set(0.5, 0);
bossHealthBarBg.y = 200;
LK.gui.top.addChild(bossHealthBarBg);
var bossHealthBar = new Text2('', {
size: 40,
fill: 0xff0000
});
bossHealthBar.anchor.set(0.5, 0);
bossHealthBar.y = 200;
LK.gui.top.addChild(bossHealthBar);
var bossNameTxt = new Text2('', {
size: 60,
fill: 0xff0000
});
bossNameTxt.anchor.set(0.5, 0);
bossNameTxt.y = 250;
LK.gui.top.addChild(bossNameTxt);
// Hide boss UI initially
bossHealthBarBg.alpha = 0;
bossHealthBar.alpha = 0;
bossNameTxt.alpha = 0;
// Touch/drag controls
function handleMove(x, y, obj) {
if (dragNode) {
// Keep player within screen bounds
dragNode.x = Math.max(40, Math.min(2008, x));
dragNode.y = Math.max(100, Math.min(2632, y));
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
dragNode = player;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
};
game.update = function () {
// Boss spawning logic
if (!bossActive && LK.getScore() >= nextBossScore) {
bossActive = true;
boss = new BossShip();
boss.x = 1024;
boss.y = -200;
game.addChild(boss);
LK.getSound('bossSpawn').play();
// Show boss UI
bossNameTxt.setText('DESTROYER COMMAND SHIP');
tween(bossHealthBarBg, {
alpha: 1
}, {
duration: 500
});
tween(bossHealthBar, {
alpha: 1
}, {
duration: 500
});
tween(bossNameTxt, {
alpha: 1
}, {
duration: 500
});
// Clear regular enemies during boss fight
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
enemies.splice(i, 1);
}
}
// Update boss health bar
if (bossActive && boss) {
var healthPercent = boss.health / boss.maxHealth;
var barWidth = Math.floor(healthPercent * 20);
var bgWidth = 20;
bossHealthBarBg.setText('░'.repeat(bgWidth));
bossHealthBar.setText('█'.repeat(barWidth));
}
// Update difficulty over time (only when boss is not active)
if (!bossActive) {
difficultyTimer++;
if (difficultyTimer % 600 == 0) {
// Every 10 seconds
enemySpawnInterval = Math.max(30, enemySpawnInterval - 10); // Faster spawning, minimum 0.5 seconds
}
}
// Update upgrade timers
if (playerUpgrades.rapidFire) {
playerUpgrades.rapidFireTimer--;
if (playerUpgrades.rapidFireTimer <= 0) {
playerUpgrades.rapidFire = false;
// Flash effect when upgrade expires
LK.effects.flashObject(player, 0x00ff00, 300);
}
}
if (playerUpgrades.shield) {
playerUpgrades.shieldTimer--;
if (playerUpgrades.shieldTimer <= 0) {
playerUpgrades.shield = false;
player.alpha = 1.0; // Reset transparency
// Hide shield visual effect
tween.stop(player.shieldGraphics);
tween(player.shieldGraphics, {
alpha: 0
}, {
duration: 300,
easing: tween.easeOut
});
}
}
if (playerUpgrades.tripleShot) {
playerUpgrades.tripleShotTimer--;
if (playerUpgrades.tripleShotTimer <= 0) {
playerUpgrades.tripleShot = false;
// Flash effect when upgrade expires
LK.effects.flashObject(player, 0xff0080, 300);
}
}
// Spawn upgrades
upgradeSpawnTimer++;
if (upgradeSpawnTimer >= upgradeSpawnInterval) {
upgradeSpawnTimer = 0;
var upgradeTypes = ['RapidFire', 'Shield', 'TripleShot'];
var randomType = upgradeTypes[Math.floor(Math.random() * upgradeTypes.length)];
var upgrade = new Upgrade(randomType);
upgrade.x = 100 + Math.random() * 1848;
upgrade.y = -50;
upgrade.lastY = upgrade.y;
upgrades.push(upgrade);
game.addChild(upgrade);
}
// Spawn enemies (only when boss is not active)
if (!bossActive) {
enemySpawnTimer++;
if (enemySpawnTimer >= enemySpawnInterval) {
enemySpawnTimer = 0;
var enemy = new EnemyShip();
enemy.x = 100 + Math.random() * 1848; // Random x position within screen
enemy.y = -50; // Start above screen
enemy.lastY = enemy.y;
enemy.speed = 2 + Math.random() * 3 + LK.getScore() / 50; // Increase speed with score
enemies.push(enemy);
game.addChild(enemy);
}
}
// Update and check player bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
if (bullet.lastY === undefined) bullet.lastY = bullet.y;
// Remove bullets that go off screen (increased range)
if (bullet.lastY >= -200 && bullet.y < -200) {
bullet.destroy();
playerBullets.splice(i, 1);
continue;
}
var hitTarget = false;
// Check collision with regular enemies (always check, regardless of boss state)
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
// Enemy destroyed
LK.setScore(LK.getScore() + 25);
scoreTxt.setText(LK.getScore());
// Visual effect
LK.effects.flashObject(enemy, 0xffffff, 200);
// Clean up
bullet.destroy();
enemy.destroy();
playerBullets.splice(i, 1);
enemies.splice(j, 1);
LK.getSound('enemyDestroy').play();
hitTarget = true;
break;
}
}
// Only check boss collision if no enemy was hit and boss exists
if (!hitTarget && bossActive && boss && bullet.intersects(boss)) {
boss.health -= 5;
LK.setScore(LK.getScore() + 15);
scoreTxt.setText(LK.getScore());
// Visual effect
LK.effects.flashObject(boss, 0xffffff, 150);
// Check if boss is defeated
if (boss.health <= 0) {
LK.setScore(LK.getScore() + 200); // Bonus points for boss kill
scoreTxt.setText(LK.getScore());
LK.effects.flashObject(boss, 0xffffff, 1000);
LK.getSound('bossDestroy').play();
boss.destroy();
boss = null;
bossActive = false;
nextBossScore = LK.getScore() + 1000; // Next boss after 1000 more points
// Hide boss UI
tween(bossHealthBarBg, {
alpha: 0
}, {
duration: 500
});
tween(bossHealthBar, {
alpha: 0
}, {
duration: 500
});
tween(bossNameTxt, {
alpha: 0
}, {
duration: 500
});
}
bullet.destroy();
playerBullets.splice(i, 1);
hitTarget = true;
}
// Update bullet tracking for next frame
if (!hitTarget) {
bullet.lastY = bullet.y;
}
// Update and check boss bullets
for (var i = bossBullets.length - 1; i >= 0; i--) {
var bullet = bossBullets[i];
if (bullet.lastY === undefined) bullet.lastY = bullet.y;
// Remove bullets that go off screen
if (bullet.lastY <= 2757 && bullet.y > 2757) {
bullet.destroy();
bossBullets.splice(i, 1);
continue;
}
var hitTarget = false;
// Check collision with enemy ships (boss bullets can destroy smaller ships)
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
// Enemy destroyed by boss bullet
LK.effects.flashObject(enemy, 0xffffff, 200);
bullet.destroy();
enemy.destroy();
bossBullets.splice(i, 1);
enemies.splice(j, 1);
LK.getSound('enemyDestroy').play();
hitTarget = true;
break;
}
}
if (!hitTarget) {
// Check collision with player
if (bullet.intersects(player)) {
if (!playerUpgrades.shield) {
LK.effects.flashScreen(0xff0000, 1000);
LK.getSound('playerHit').play();
LK.showGameOver();
return;
} else {
// Shield absorbs the hit
bullet.destroy();
bossBullets.splice(i, 1);
LK.effects.flashObject(player, 0x0080ff, 200);
continue;
}
}
}
if (!hitTarget) {
bullet.lastY = bullet.y;
}
}
}
// Update and check enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
if (bullet.lastY === undefined) bullet.lastY = bullet.y;
// Remove bullets that go off screen
if (bullet.lastY <= 2757 && bullet.y > 2757) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check collision with player
if (bullet.intersects(player)) {
if (!playerUpgrades.shield) {
LK.effects.flashScreen(0xff0000, 1000);
LK.getSound('playerHit').play();
LK.showGameOver();
return;
} else {
// Shield absorbs the hit
bullet.destroy();
enemyBullets.splice(i, 1);
LK.effects.flashObject(player, 0x0080ff, 200);
continue;
}
}
bullet.lastY = bullet.y;
}
// Update and check upgrades
for (var i = upgrades.length - 1; i >= 0; i--) {
var upgrade = upgrades[i];
if (upgrade.lastY === undefined) upgrade.lastY = upgrade.y;
// Remove upgrades that go off screen
if (upgrade.lastY <= 2757 && upgrade.y > 2757) {
upgrade.destroy();
upgrades.splice(i, 1);
continue;
}
// Check collision with player
if (upgrade.intersects(player)) {
LK.getSound('upgradePickup').play();
if (upgrade.type === 'RapidFire') {
playerUpgrades.rapidFire = true;
playerUpgrades.rapidFireTimer = 600; // 10 seconds
LK.effects.flashObject(player, 0x00ff00, 500);
} else if (upgrade.type === 'Shield') {
playerUpgrades.shield = true;
playerUpgrades.shieldTimer = 600; // 10 seconds
player.alpha = 0.7; // Make player semi-transparent to show shield
// Show shield visual effect
player.shieldGraphics.alpha = 0.6;
// Start pulsing animation for shield
tween(player.shieldGraphics, {
alpha: 0.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (playerUpgrades.shield) {
tween(player.shieldGraphics, {
alpha: 0.6
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: arguments.callee.caller
});
}
}
});
LK.effects.flashObject(player, 0x0080ff, 500);
} else if (upgrade.type === 'TripleShot') {
playerUpgrades.tripleShot = true;
playerUpgrades.tripleShotTimer = 600; // 10 seconds
LK.effects.flashObject(player, 0xff0080, 500);
}
upgrade.destroy();
upgrades.splice(i, 1);
continue;
}
upgrade.lastY = upgrade.y;
}
// Update and check enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.lastY === undefined) enemy.lastY = enemy.y;
// Remove enemies that go off screen
if (enemy.lastY <= 2757 && enemy.y > 2757) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
// Check collision with player
if (enemy.intersects(player)) {
if (!playerUpgrades.shield) {
LK.effects.flashScreen(0xff0000, 1000);
LK.getSound('playerHit').play();
LK.showGameOver();
return;
} else {
// Shield absorbs the hit and destroys the enemy
LK.setScore(LK.getScore() + 5); // Bonus points for shield collision
scoreTxt.setText(LK.getScore());
LK.effects.flashObject(player, 0x0080ff, 200);
enemy.destroy();
enemies.splice(i, 1);
continue;
}
}
enemy.lastY = enemy.y;
}
};