/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Boss1Ship = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('bossShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 500;
self.maxHealth = 500;
self.speed = 2;
self.shootCooldown = 0;
self.shootRate = 60;
self.moveDirection = 1;
self.moveTimer = 0;
self.moveInterval = 120;
self.bossNumber = 1;
self.startX = 1024;
self.attackPhaseTimer = 0;
self.isAttackPhase = true;
self.update = function () {
self.attackPhaseTimer++;
if (self.isAttackPhase && self.attackPhaseTimer <= 600) {
self.moveTimer++;
var circleX = Math.cos(self.moveTimer / 30) * 150;
var circleY = Math.sin(self.moveTimer / 60) * 100;
var movePattern = Math.sin(self.moveTimer / 20) * 600;
self.x = self.startX + movePattern + circleX;
self.y = 300 + circleY;
if (self.x < 50 || self.x > 1998) {
self.x = self.startX;
self.moveTimer = 0;
}
self.y = Math.max(100, Math.min(800, self.y));
self.shootCooldown--;
} else if (self.isAttackPhase && self.attackPhaseTimer > 600) {
self.isAttackPhase = false;
self.attackPhaseTimer = 0;
}
if (!self.isAttackPhase) {
self.shootCooldown = 1000;
if (self.attackPhaseTimer >= 300) {
self.isAttackPhase = true;
self.attackPhaseTimer = 0;
self.shootCooldown = 0;
}
self.attackPhaseTimer++;
}
};
return self;
});
var Boss2Ship = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('bossShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 700;
self.maxHealth = 700;
self.speed = 5;
self.shootCooldown = 0;
self.shootRate = 45;
self.moveDirection = 1;
self.moveTimer = 0;
self.moveInterval = 120;
self.bossNumber = 2;
self.startX = 1024;
self.attackPhaseTimer = 0;
self.isAttackPhase = true;
self.velocityX = 5; //{Boss2-velX}
self.velocityY = 4; //{Boss2-velY}
self.lastContactWithPlayer = false; //{Boss2-lastContact}
self.update = function () {
self.attackPhaseTimer++;
if (self.isAttackPhase && self.attackPhaseTimer <= 600) {
self.x += self.velocityX;
self.y += self.velocityY;
if (self.x < 100 || self.x > 1948) {
self.velocityX *= -1;
} //{Boss2-bounceX}
if (self.y < 150 || self.y > 800) {
self.velocityY *= -1;
} //{Boss2-bounceY}
self.shootCooldown--;
} else if (self.isAttackPhase && self.attackPhaseTimer > 600) {
self.isAttackPhase = false;
self.attackPhaseTimer = 0;
}
if (!self.isAttackPhase) {
self.shootCooldown = 1000;
self.velocityX = 0;
self.velocityY = 0;
if (self.attackPhaseTimer >= 300) {
self.isAttackPhase = true;
self.attackPhaseTimer = 0;
self.shootCooldown = 0;
self.velocityX = Math.random() * 6 - 3;
self.velocityY = Math.random() * 6 - 3;
}
}
};
return self;
});
var Boss3Ship = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('bossShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 900;
self.maxHealth = 900;
self.speed = 4;
self.shootCooldown = 0;
self.shootRate = 30;
self.moveDirection = 1;
self.moveTimer = 0;
self.moveInterval = 120;
self.bossNumber = 3;
self.startX = 1024;
self.attackPhaseTimer = 0;
self.isAttackPhase = true;
self.update = function () {
self.attackPhaseTimer++;
if (self.isAttackPhase && self.attackPhaseTimer <= 400) {
self.moveTimer++;
var circleX = Math.cos(self.moveTimer / 20) * 250;
var circleY = Math.sin(self.moveTimer / 40) * 200;
var movePattern = Math.sin(self.moveTimer / 10) * 800;
self.x = self.startX + movePattern + circleX;
self.y = 200 + circleY;
if (self.x < 50 || self.x > 1998) {
self.x = self.startX;
self.moveTimer = 0;
}
self.y = Math.max(100, Math.min(800, self.y));
self.shootCooldown--;
} else if (self.isAttackPhase && self.attackPhaseTimer > 400) {
self.isAttackPhase = false;
self.attackPhaseTimer = 0;
}
if (!self.isAttackPhase) {
self.shootCooldown = 600;
if (self.attackPhaseTimer >= 200) {
self.isAttackPhase = true;
self.attackPhaseTimer = 0;
self.shootCooldown = 0;
}
self.attackPhaseTimer++;
}
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = 1;
self.update = function () {
self.y += self.speed;
};
return self;
});
var EnemyShip = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemyShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 1;
self.maxHealth = 1;
self.speed = 3;
self.shootCooldown = 0;
self.shootRate = 60;
self.waveNumber = 1;
self.update = function () {
self.y += self.speed;
self.shootCooldown--;
};
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 = -15;
self.damage = 1;
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.health = 25;
self.maxHealth = 25;
self.speed = 6;
self.shootCooldown = 0;
self.shootRate = 15;
self.firepower = 1;
self.armor = 1;
self.upgradePoints = 0;
self.down = function (x, y, obj) {
//Placeholder for future touch interactions
};
self.up = function (x, y, obj) {
//Placeholder for future touch interactions
};
self.move = function (x, y, obj) {
//Handled by game.move
};
self.update = function () {
self.shootCooldown--;
};
return self;
});
var UpgradePickup = Container.expand(function () {
var self = Container.call(this);
var pickupGraphics = self.attachAsset('upgradePickup', {
anchorX: 0.5,
anchorY: 0.5
});
self.upgradeType = 'firepower';
self.worldType = 1;
self.floatSpeed = 2;
self.floatDirection = -1;
self.originalY = 0;
self.setWorldType = function (worldNum) {
self.worldType = worldNum;
if (worldNum === 2) {
pickupGraphics.tint = 0x00ffff;
} else if (worldNum === 3) {
pickupGraphics.tint = 0xff00ff;
}
};
self.update = function () {
self.y += self.floatSpeed * self.floatDirection;
if (self.y < self.originalY - 40 || self.y > self.originalY + 40) {
self.floatDirection *= -1;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x001a4d
});
/****
* Game Code
****/
//Game state variables
var playerShip = null;
var enemyBullets = [];
var playerBullets = [];
var enemies = [];
var bosses = [];
var upgradePickups = [];
var waveNumber = 1;
var bossNumber = 1;
var waveEnemyCount = 0;
var waveEnemiesDefeated = 0;
var isBossWave = false;
var gameState = 'playing';
var moveDirection = 0;
var touchX = 1024;
//UI Elements
var healthText = new Text2('Health: 25/25', {
size: 80,
fill: '#ffffff'
});
healthText.anchor.set(0, 0);
LK.gui.topRight.addChild(healthText);
var playerHealthBarBg = LK.getAsset('starfield', {
width: 300,
height: 25,
color: 0x333333,
shape: 'box'
});
playerHealthBarBg.anchor.set(0, 0.5);
LK.gui.topRight.addChild(playerHealthBarBg);
var playerHealthBar = LK.getAsset('starfield', {
width: 280,
height: 15,
color: 0x00ff00,
shape: 'box'
});
playerHealthBar.anchor.set(0, 0.5);
LK.gui.topRight.addChild(playerHealthBar);
var upgradeText = new Text2('Upgrades: 0', {
size: 80,
fill: '#ffff00'
});
upgradeText.anchor.set(0, 0);
LK.gui.top.addChild(upgradeText);
var waveText = new Text2('Wave: 1', {
size: 80,
fill: '#00ff00'
});
waveText.anchor.set(1, 0);
LK.gui.topRight.addChild(waveText);
var bossHealthBarBg = LK.getAsset('starfield', {
width: 400,
height: 30,
color: 0x333333,
shape: 'box'
});
bossHealthBarBg.anchor.set(0.5, 0.5);
bossHealthBarBg.visible = false;
LK.gui.top.addChild(bossHealthBarBg);
var bossHealthBar = LK.getAsset('starfield', {
width: 380,
height: 20,
color: 0xff0000,
shape: 'box'
});
bossHealthBar.anchor.set(0, 0.5);
bossHealthBar.visible = false;
LK.gui.top.addChild(bossHealthBar);
var bossHealthText = new Text2('Boss Health', {
size: 60,
fill: '#ff0000'
});
bossHealthText.anchor.set(0.5, 0.5);
bossHealthText.visible = false;
LK.gui.top.addChild(bossHealthText);
var world2Button = LK.getAsset('starfield', {
width: 300,
height: 100,
color: 0x00ffff,
shape: 'box'
}); //{3a-world2}
world2Button.anchor.set(0.5, 0.5);
world2Button.interactive = true;
world2Button.visible = false;
LK.gui.bottomRight.addChild(world2Button);
var world2ButtonText = new Text2('World 2', {
size: 60,
fill: '#000000'
}); //{3b-world2}
world2ButtonText.anchor.set(0.5, 0.5);
world2Button.addChild(world2ButtonText);
world2Button.down = function (x, y, obj) {
if (waveNumber === 1 && bosses.length === 0 && enemies.length === 0) {
waveNumber = 2;
bossNumber = 2;
world2Button.visible = false;
world3Button.visible = true;
spawnBoss();
}
}; //{3c-world2}
var world3Button = LK.getAsset('starfield', {
width: 300,
//{3c-world3}
height: 100,
color: 0xff00ff,
shape: 'box' //{3d-world3}
}); //{3a-world3}
world3Button.anchor.set(0.5, 0.5);
world3Button.interactive = true;
world3Button.visible = false;
LK.gui.bottomLeft.addChild(world3Button);
var world3ButtonText = new Text2('World 3', {
size: 60,
//{3e-world3}
fill: '#000000'
}); //{3b-world3}
world3ButtonText.anchor.set(0.5, 0.5);
world3Button.addChild(world3ButtonText);
world3Button.down = function (x, y, obj) {
if (waveNumber === 2 && bosses.length === 0 && enemies.length === 0) {
waveNumber = 3;
bossNumber = 3;
world3Button.visible = false;
playerShip.health = 1000;
playerShip.maxHealth = 1000;
healthText.setText('Health: 1000/1000');
spawnBoss();
} //{3g-world3}
}; //{3c-world3}
var pauseMenuBg = LK.getAsset('starfield', {
width: 1024,
height: 800,
color: 0x000000,
shape: 'box'
});
pauseMenuBg.alpha = 0.9;
pauseMenuBg.visible = false;
LK.gui.center.addChild(pauseMenuBg);
var pauseTitle = new Text2('PAUSED', {
size: 150,
fill: '#ffff00'
});
pauseTitle.anchor.set(0.5, 0.5);
pauseTitle.visible = false;
LK.gui.center.addChild(pauseTitle);
var pauseInfo = new Text2('Tap to Resume', {
size: 100,
fill: '#ffffff'
});
pauseInfo.anchor.set(0.5, 0.5);
pauseInfo.visible = false;
LK.gui.center.addChild(pauseInfo);
//Add starfield background
var starfield = game.addChild(LK.getAsset('starfield', {
anchorX: 0,
anchorY: 0
}));
starfield.x = 0;
starfield.y = 0;
//Add pixel art stars to background
for (var s = 0; s < 200; s++) {
var star = game.addChild(LK.getAsset('pixelStar', {
anchorX: 0.5,
anchorY: 0.5
}));
star.x = Math.random() * 2048;
star.y = Math.random() * 2732;
}
//Add pixel art planets to background
var planet1 = game.addChild(LK.getAsset('pixelPlanet1', {
anchorX: 0.5,
anchorY: 0.5
}));
planet1.x = 300;
planet1.y = 400;
planet1.alpha = 0.6;
var planet2 = game.addChild(LK.getAsset('pixelPlanet2', {
anchorX: 0.5,
anchorY: 0.5
}));
planet2.x = 1800;
planet2.y = 1200;
planet2.alpha = 0.5;
var planet3 = game.addChild(LK.getAsset('pixelPlanet3', {
anchorX: 0.5,
anchorY: 0.5
}));
planet3.x = 500;
planet3.y = 2200;
planet3.alpha = 0.7;
//Initialize player ship
playerShip = game.addChild(new PlayerShip());
playerShip.x = 1024;
playerShip.y = 2400;
//Start music
LK.playMusic('spaceMusic', {
loop: true
});
//Function to get world-specific upgrade pool
function getWorldUpgrades() {
if (waveNumber === 1) {
return ['firepower', 'armor', 'speed'];
} else if (waveNumber === 2) {
return ['firepower', 'armor', 'speed', 'rapidFire', 'shield'];
} else if (waveNumber === 3) {
return ['firepower', 'armor', 'speed', 'rapidFire', 'shield', 'laser', 'multishot'];
}
return ['firepower', 'armor', 'speed'];
}
//Function to calculate enemy wave count
function calculateWaveSize() {
return 8 + Math.floor(waveNumber / 2);
}
//Function to spawn enemy wave
function spawnEnemyWave() {
if (waveNumber === 3) {
waveEnemyCount = 0;
waveEnemiesDefeated = 0;
isBossWave = false;
LK.setTimeout(function () {
if (enemies.length === 0 && bosses.length === 0) {
spawnBoss();
}
}, 1000);
return;
}
waveEnemyCount = 9;
waveEnemiesDefeated = 0;
isBossWave = false;
for (var wave = 0; wave < 3; wave++) {
LK.setTimeout(function (w) {
return function () {
for (var i = 0; i < 3; i++) {
var enemy = game.addChild(new EnemyShip());
enemy.x = 100 + Math.random() * 1848;
enemy.y = -100 - i * 150;
enemy.speed = 2 + waveNumber * 0.3;
enemy.shootRate = 60;
enemy.waveNumber = waveNumber;
enemy.lastBulletIntersecting = false;
enemies.push(enemy);
}
};
}(wave), wave * 15000);
}
}
//Function to spawn boss wave
function spawnBoss() {
isBossWave = true;
waveEnemiesDefeated = 0;
var boss;
if (bossNumber === 1) {
boss = game.addChild(new Boss1Ship());
} else if (bossNumber === 2) {
boss = game.addChild(new Boss2Ship());
} else if (bossNumber === 3) {
boss = game.addChild(new Boss3Ship());
} else {
boss = game.addChild(new Boss3Ship());
}
boss.x = 1024;
boss.startX = 1024;
boss.y = 300;
boss.shootRate = Math.max(35, 60 - bossNumber * 3);
bossHealthBarBg.visible = true;
bossHealthBar.visible = true;
bossHealthText.visible = true;
bosses.push(boss);
}
//Initialize first wave
spawnEnemyWave();
//Handle game movement
game.move = function (x, y, obj) {
touchX = x;
playerShip.x = Math.max(30, Math.min(2018, x));
playerShip.y = Math.max(100, Math.min(2650, y));
};
//Main game update loop
game.update = function () {
if (gameState !== 'playing') {
return;
}
//Update player ship
playerShip.update();
playerShip.shootCooldown--;
//Player shooting
if (playerShip.shootCooldown <= 0) {
for (var i = 0; i < playerShip.firepower; i++) {
var offset = (playerShip.firepower - 1) * -15 + i * 30;
var bullet = game.addChild(new PlayerBullet());
bullet.x = playerShip.x + offset;
bullet.y = playerShip.y - 40;
bullet.damage = playerShip.firepower;
playerBullets.push(bullet);
LK.getSound('playerShoot').play();
}
playerShip.shootCooldown = playerShip.shootRate;
}
//Update player bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
bullet.update();
if (bullet.y < -50) {
bullet.destroy();
playerBullets.splice(i, 1);
continue;
}
}
//Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (!enemy) {
continue;
}
enemy.update();
//Enemy shooting
enemy.shootCooldown--;
if (enemy.shootCooldown <= 0) {
var eBullet = game.addChild(new EnemyBullet());
eBullet.x = enemy.x;
eBullet.y = enemy.y + 30;
enemyBullets.push(eBullet);
LK.getSound('enemyShoot').play();
enemy.shootCooldown = enemy.shootRate;
}
//Check enemy off-screen
if (enemy.y > 2800) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
//Check bullet collisions with enemies
for (var j = playerBullets.length - 1; j >= 0; j--) {
var bullet = playerBullets[j];
if (bullet && enemy && bullet.intersects(enemy) && !enemy.lastBulletIntersecting) {
enemy.health -= bullet.damage;
enemy.lastBulletIntersecting = true;
bullet.destroy();
playerBullets.splice(j, 1);
if (enemy.health <= 0) {
LK.getSound('explosion').play();
LK.effects.flashObject(enemy, 0xff0000, 200);
LK.setScore(LK.getScore() + 100);
waveEnemiesDefeated++;
//Drop upgrade pickup chance
if (Math.random() < 0.3) {
var pickup = game.addChild(new UpgradePickup());
pickup.x = enemy.x;
pickup.y = enemy.y;
pickup.originalY = enemy.y;
var worldUpgrades = getWorldUpgrades();
pickup.upgradeType = worldUpgrades[Math.floor(Math.random() * worldUpgrades.length)];
pickup.setWorldType(waveNumber);
upgradePickups.push(pickup);
}
enemy.destroy();
enemies.splice(i, 1);
}
break;
}
}
}
//Update bosses
for (var i = bosses.length - 1; i >= 0; i--) {
var boss = bosses[i];
if (!boss) {
continue;
}
boss.update();
//Check Boss2 contact damage with player
if (boss.bossNumber === 2 && boss.isAttackPhase) {
if (boss.intersects(playerShip) && !boss.lastContactWithPlayer) {
boss.lastContactWithPlayer = true;
playerShip.health -= 1;
LK.effects.flashObject(playerShip, 0xff0000, 200);
if (playerShip.health <= 0) {
gameState = 'gameover';
LK.showGameOver();
} //{Boss2-contactDamage}
} else if (!boss.intersects(playerShip)) {
boss.lastContactWithPlayer = false;
}
} //{Boss2-contactDamageEnd}
//Boss shooting (multiple bullets)
boss.shootCooldown--;
if (boss.shootCooldown <= 0) {
for (var b = 0; b < 2; b++) {
var eBullet = game.addChild(new EnemyBullet());
eBullet.x = boss.x + (b - 1) * 30;
eBullet.y = boss.y + 60;
enemyBullets.push(eBullet);
}
LK.getSound('enemyShoot').play();
boss.shootCooldown = boss.shootRate;
}
//Check bullet collisions with boss
for (var j = playerBullets.length - 1; j >= 0; j--) {
var bullet = playerBullets[j];
if (bullet && boss && bullet.intersects(boss)) {
boss.health -= 1;
bullet.destroy();
playerBullets.splice(j, 1);
LK.effects.flashObject(boss, 0xff00ff, 150);
if (boss.health <= 0) {
LK.getSound('explosion').play();
LK.effects.flashScreen(0xffff00, 500);
LK.setScore(LK.getScore() + 1000);
//Grant boss defeat upgrade
playerShip.upgradePoints += 3;
playerShip.firepower += 1;
playerShip.armor += 1;
playerShip.health = playerShip.maxHealth;
boss.destroy();
bosses.splice(i, 1);
bossNumber++;
waveNumber++;
//Spawn next wave after delay
LK.setTimeout(function () {
spawnEnemyWave();
}, 1000);
}
break;
}
}
}
//Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
bullet.update();
if (bullet.y > 2800) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
//Check collision with player
if (bullet.intersects(playerShip) && !bullet.lastPlayerIntersecting) {
bullet.lastPlayerIntersecting = true;
playerShip.health -= Math.max(1, bullet.damage - (playerShip.armor - 1));
bullet.destroy();
enemyBullets.splice(i, 1);
LK.effects.flashObject(playerShip, 0xff0000, 200);
if (playerShip.health <= 0) {
gameState = 'gameover';
LK.showGameOver();
}
continue;
}
}
//Update upgrade pickups
for (var i = upgradePickups.length - 1; i >= 0; i--) {
var pickup = upgradePickups[i];
pickup.update();
//Check collision with player
if (pickup.intersects(playerShip) && !pickup.lastPlayerIntersecting) {
pickup.lastPlayerIntersecting = true;
LK.getSound('upgrade').play();
if (pickup.upgradeType === 'firepower') {
playerShip.firepower++;
} else if (pickup.upgradeType === 'armor') {
playerShip.armor++;
playerShip.health = playerShip.maxHealth;
} else if (pickup.upgradeType === 'speed') {
playerShip.shootRate = Math.max(5, playerShip.shootRate - 2);
} else if (pickup.upgradeType === 'rapidFire') {
playerShip.shootRate = Math.max(5, playerShip.shootRate - 3);
} else if (pickup.upgradeType === 'shield') {
playerShip.armor += 2;
playerShip.health = playerShip.maxHealth;
} else if (pickup.upgradeType === 'laser') {
playerShip.firepower += 2;
} else if (pickup.upgradeType === 'multishot') {
playerShip.firepower += 1;
playerShip.shootRate = Math.max(5, playerShip.shootRate - 1);
}
playerShip.upgradePoints++;
LK.effects.flashObject(playerShip, 0x00ff00, 300);
pickup.destroy();
upgradePickups.splice(i, 1);
continue;
}
//Remove off-screen pickups
if (pickup.y > 2800) {
pickup.destroy();
upgradePickups.splice(i, 1);
}
}
//Check if wave is complete and spawn boss
if (enemies.length === 0 && !isBossWave && bosses.length === 0 && waveEnemiesDefeated === waveEnemyCount && waveEnemyCount > 0) {
waveEnemyCount = 0;
LK.setTimeout(function () {
if (enemies.length === 0 && bosses.length === 0) {
spawnBoss();
}
}, 1500);
}
//Update boss health bar
if (bosses.length > 0) {
var boss = bosses[0];
var healthPercentage = Math.max(0, boss.health / boss.maxHealth);
bossHealthBar.scaleX = healthPercentage;
bossHealthBarBg.x = 1024;
bossHealthBarBg.y = 60;
bossHealthBar.x = 1024 - 190;
bossHealthBar.y = 60;
bossHealthText.setText('Boss ' + boss.bossNumber + ' HP: ' + Math.max(0, boss.health) + '/' + boss.maxHealth);
} else {
bossHealthBarBg.visible = false;
bossHealthBar.visible = false;
bossHealthText.visible = false;
}
//Update UI
healthText.setText('Health: ' + Math.max(0, playerShip.health) + '/' + playerShip.maxHealth);
var healthPercentage = Math.max(0, playerShip.health / playerShip.maxHealth);
playerHealthBar.scaleX = healthPercentage;
if (healthPercentage > 0.5) {
playerHealthBar.tint = 0x00ff00;
} else if (healthPercentage > 0.25) {
playerHealthBar.tint = 0xffff00;
} else {
playerHealthBar.tint = 0xff0000;
}
upgradeText.setText('Upgrades: ' + playerShip.upgradePoints);
waveText.setText('Wave: ' + waveNumber + ' | Boss: ' + bossNumber);
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Boss1Ship = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('bossShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 500;
self.maxHealth = 500;
self.speed = 2;
self.shootCooldown = 0;
self.shootRate = 60;
self.moveDirection = 1;
self.moveTimer = 0;
self.moveInterval = 120;
self.bossNumber = 1;
self.startX = 1024;
self.attackPhaseTimer = 0;
self.isAttackPhase = true;
self.update = function () {
self.attackPhaseTimer++;
if (self.isAttackPhase && self.attackPhaseTimer <= 600) {
self.moveTimer++;
var circleX = Math.cos(self.moveTimer / 30) * 150;
var circleY = Math.sin(self.moveTimer / 60) * 100;
var movePattern = Math.sin(self.moveTimer / 20) * 600;
self.x = self.startX + movePattern + circleX;
self.y = 300 + circleY;
if (self.x < 50 || self.x > 1998) {
self.x = self.startX;
self.moveTimer = 0;
}
self.y = Math.max(100, Math.min(800, self.y));
self.shootCooldown--;
} else if (self.isAttackPhase && self.attackPhaseTimer > 600) {
self.isAttackPhase = false;
self.attackPhaseTimer = 0;
}
if (!self.isAttackPhase) {
self.shootCooldown = 1000;
if (self.attackPhaseTimer >= 300) {
self.isAttackPhase = true;
self.attackPhaseTimer = 0;
self.shootCooldown = 0;
}
self.attackPhaseTimer++;
}
};
return self;
});
var Boss2Ship = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('bossShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 700;
self.maxHealth = 700;
self.speed = 5;
self.shootCooldown = 0;
self.shootRate = 45;
self.moveDirection = 1;
self.moveTimer = 0;
self.moveInterval = 120;
self.bossNumber = 2;
self.startX = 1024;
self.attackPhaseTimer = 0;
self.isAttackPhase = true;
self.velocityX = 5; //{Boss2-velX}
self.velocityY = 4; //{Boss2-velY}
self.lastContactWithPlayer = false; //{Boss2-lastContact}
self.update = function () {
self.attackPhaseTimer++;
if (self.isAttackPhase && self.attackPhaseTimer <= 600) {
self.x += self.velocityX;
self.y += self.velocityY;
if (self.x < 100 || self.x > 1948) {
self.velocityX *= -1;
} //{Boss2-bounceX}
if (self.y < 150 || self.y > 800) {
self.velocityY *= -1;
} //{Boss2-bounceY}
self.shootCooldown--;
} else if (self.isAttackPhase && self.attackPhaseTimer > 600) {
self.isAttackPhase = false;
self.attackPhaseTimer = 0;
}
if (!self.isAttackPhase) {
self.shootCooldown = 1000;
self.velocityX = 0;
self.velocityY = 0;
if (self.attackPhaseTimer >= 300) {
self.isAttackPhase = true;
self.attackPhaseTimer = 0;
self.shootCooldown = 0;
self.velocityX = Math.random() * 6 - 3;
self.velocityY = Math.random() * 6 - 3;
}
}
};
return self;
});
var Boss3Ship = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('bossShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 900;
self.maxHealth = 900;
self.speed = 4;
self.shootCooldown = 0;
self.shootRate = 30;
self.moveDirection = 1;
self.moveTimer = 0;
self.moveInterval = 120;
self.bossNumber = 3;
self.startX = 1024;
self.attackPhaseTimer = 0;
self.isAttackPhase = true;
self.update = function () {
self.attackPhaseTimer++;
if (self.isAttackPhase && self.attackPhaseTimer <= 400) {
self.moveTimer++;
var circleX = Math.cos(self.moveTimer / 20) * 250;
var circleY = Math.sin(self.moveTimer / 40) * 200;
var movePattern = Math.sin(self.moveTimer / 10) * 800;
self.x = self.startX + movePattern + circleX;
self.y = 200 + circleY;
if (self.x < 50 || self.x > 1998) {
self.x = self.startX;
self.moveTimer = 0;
}
self.y = Math.max(100, Math.min(800, self.y));
self.shootCooldown--;
} else if (self.isAttackPhase && self.attackPhaseTimer > 400) {
self.isAttackPhase = false;
self.attackPhaseTimer = 0;
}
if (!self.isAttackPhase) {
self.shootCooldown = 600;
if (self.attackPhaseTimer >= 200) {
self.isAttackPhase = true;
self.attackPhaseTimer = 0;
self.shootCooldown = 0;
}
self.attackPhaseTimer++;
}
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = 1;
self.update = function () {
self.y += self.speed;
};
return self;
});
var EnemyShip = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemyShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 1;
self.maxHealth = 1;
self.speed = 3;
self.shootCooldown = 0;
self.shootRate = 60;
self.waveNumber = 1;
self.update = function () {
self.y += self.speed;
self.shootCooldown--;
};
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 = -15;
self.damage = 1;
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.health = 25;
self.maxHealth = 25;
self.speed = 6;
self.shootCooldown = 0;
self.shootRate = 15;
self.firepower = 1;
self.armor = 1;
self.upgradePoints = 0;
self.down = function (x, y, obj) {
//Placeholder for future touch interactions
};
self.up = function (x, y, obj) {
//Placeholder for future touch interactions
};
self.move = function (x, y, obj) {
//Handled by game.move
};
self.update = function () {
self.shootCooldown--;
};
return self;
});
var UpgradePickup = Container.expand(function () {
var self = Container.call(this);
var pickupGraphics = self.attachAsset('upgradePickup', {
anchorX: 0.5,
anchorY: 0.5
});
self.upgradeType = 'firepower';
self.worldType = 1;
self.floatSpeed = 2;
self.floatDirection = -1;
self.originalY = 0;
self.setWorldType = function (worldNum) {
self.worldType = worldNum;
if (worldNum === 2) {
pickupGraphics.tint = 0x00ffff;
} else if (worldNum === 3) {
pickupGraphics.tint = 0xff00ff;
}
};
self.update = function () {
self.y += self.floatSpeed * self.floatDirection;
if (self.y < self.originalY - 40 || self.y > self.originalY + 40) {
self.floatDirection *= -1;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x001a4d
});
/****
* Game Code
****/
//Game state variables
var playerShip = null;
var enemyBullets = [];
var playerBullets = [];
var enemies = [];
var bosses = [];
var upgradePickups = [];
var waveNumber = 1;
var bossNumber = 1;
var waveEnemyCount = 0;
var waveEnemiesDefeated = 0;
var isBossWave = false;
var gameState = 'playing';
var moveDirection = 0;
var touchX = 1024;
//UI Elements
var healthText = new Text2('Health: 25/25', {
size: 80,
fill: '#ffffff'
});
healthText.anchor.set(0, 0);
LK.gui.topRight.addChild(healthText);
var playerHealthBarBg = LK.getAsset('starfield', {
width: 300,
height: 25,
color: 0x333333,
shape: 'box'
});
playerHealthBarBg.anchor.set(0, 0.5);
LK.gui.topRight.addChild(playerHealthBarBg);
var playerHealthBar = LK.getAsset('starfield', {
width: 280,
height: 15,
color: 0x00ff00,
shape: 'box'
});
playerHealthBar.anchor.set(0, 0.5);
LK.gui.topRight.addChild(playerHealthBar);
var upgradeText = new Text2('Upgrades: 0', {
size: 80,
fill: '#ffff00'
});
upgradeText.anchor.set(0, 0);
LK.gui.top.addChild(upgradeText);
var waveText = new Text2('Wave: 1', {
size: 80,
fill: '#00ff00'
});
waveText.anchor.set(1, 0);
LK.gui.topRight.addChild(waveText);
var bossHealthBarBg = LK.getAsset('starfield', {
width: 400,
height: 30,
color: 0x333333,
shape: 'box'
});
bossHealthBarBg.anchor.set(0.5, 0.5);
bossHealthBarBg.visible = false;
LK.gui.top.addChild(bossHealthBarBg);
var bossHealthBar = LK.getAsset('starfield', {
width: 380,
height: 20,
color: 0xff0000,
shape: 'box'
});
bossHealthBar.anchor.set(0, 0.5);
bossHealthBar.visible = false;
LK.gui.top.addChild(bossHealthBar);
var bossHealthText = new Text2('Boss Health', {
size: 60,
fill: '#ff0000'
});
bossHealthText.anchor.set(0.5, 0.5);
bossHealthText.visible = false;
LK.gui.top.addChild(bossHealthText);
var world2Button = LK.getAsset('starfield', {
width: 300,
height: 100,
color: 0x00ffff,
shape: 'box'
}); //{3a-world2}
world2Button.anchor.set(0.5, 0.5);
world2Button.interactive = true;
world2Button.visible = false;
LK.gui.bottomRight.addChild(world2Button);
var world2ButtonText = new Text2('World 2', {
size: 60,
fill: '#000000'
}); //{3b-world2}
world2ButtonText.anchor.set(0.5, 0.5);
world2Button.addChild(world2ButtonText);
world2Button.down = function (x, y, obj) {
if (waveNumber === 1 && bosses.length === 0 && enemies.length === 0) {
waveNumber = 2;
bossNumber = 2;
world2Button.visible = false;
world3Button.visible = true;
spawnBoss();
}
}; //{3c-world2}
var world3Button = LK.getAsset('starfield', {
width: 300,
//{3c-world3}
height: 100,
color: 0xff00ff,
shape: 'box' //{3d-world3}
}); //{3a-world3}
world3Button.anchor.set(0.5, 0.5);
world3Button.interactive = true;
world3Button.visible = false;
LK.gui.bottomLeft.addChild(world3Button);
var world3ButtonText = new Text2('World 3', {
size: 60,
//{3e-world3}
fill: '#000000'
}); //{3b-world3}
world3ButtonText.anchor.set(0.5, 0.5);
world3Button.addChild(world3ButtonText);
world3Button.down = function (x, y, obj) {
if (waveNumber === 2 && bosses.length === 0 && enemies.length === 0) {
waveNumber = 3;
bossNumber = 3;
world3Button.visible = false;
playerShip.health = 1000;
playerShip.maxHealth = 1000;
healthText.setText('Health: 1000/1000');
spawnBoss();
} //{3g-world3}
}; //{3c-world3}
var pauseMenuBg = LK.getAsset('starfield', {
width: 1024,
height: 800,
color: 0x000000,
shape: 'box'
});
pauseMenuBg.alpha = 0.9;
pauseMenuBg.visible = false;
LK.gui.center.addChild(pauseMenuBg);
var pauseTitle = new Text2('PAUSED', {
size: 150,
fill: '#ffff00'
});
pauseTitle.anchor.set(0.5, 0.5);
pauseTitle.visible = false;
LK.gui.center.addChild(pauseTitle);
var pauseInfo = new Text2('Tap to Resume', {
size: 100,
fill: '#ffffff'
});
pauseInfo.anchor.set(0.5, 0.5);
pauseInfo.visible = false;
LK.gui.center.addChild(pauseInfo);
//Add starfield background
var starfield = game.addChild(LK.getAsset('starfield', {
anchorX: 0,
anchorY: 0
}));
starfield.x = 0;
starfield.y = 0;
//Add pixel art stars to background
for (var s = 0; s < 200; s++) {
var star = game.addChild(LK.getAsset('pixelStar', {
anchorX: 0.5,
anchorY: 0.5
}));
star.x = Math.random() * 2048;
star.y = Math.random() * 2732;
}
//Add pixel art planets to background
var planet1 = game.addChild(LK.getAsset('pixelPlanet1', {
anchorX: 0.5,
anchorY: 0.5
}));
planet1.x = 300;
planet1.y = 400;
planet1.alpha = 0.6;
var planet2 = game.addChild(LK.getAsset('pixelPlanet2', {
anchorX: 0.5,
anchorY: 0.5
}));
planet2.x = 1800;
planet2.y = 1200;
planet2.alpha = 0.5;
var planet3 = game.addChild(LK.getAsset('pixelPlanet3', {
anchorX: 0.5,
anchorY: 0.5
}));
planet3.x = 500;
planet3.y = 2200;
planet3.alpha = 0.7;
//Initialize player ship
playerShip = game.addChild(new PlayerShip());
playerShip.x = 1024;
playerShip.y = 2400;
//Start music
LK.playMusic('spaceMusic', {
loop: true
});
//Function to get world-specific upgrade pool
function getWorldUpgrades() {
if (waveNumber === 1) {
return ['firepower', 'armor', 'speed'];
} else if (waveNumber === 2) {
return ['firepower', 'armor', 'speed', 'rapidFire', 'shield'];
} else if (waveNumber === 3) {
return ['firepower', 'armor', 'speed', 'rapidFire', 'shield', 'laser', 'multishot'];
}
return ['firepower', 'armor', 'speed'];
}
//Function to calculate enemy wave count
function calculateWaveSize() {
return 8 + Math.floor(waveNumber / 2);
}
//Function to spawn enemy wave
function spawnEnemyWave() {
if (waveNumber === 3) {
waveEnemyCount = 0;
waveEnemiesDefeated = 0;
isBossWave = false;
LK.setTimeout(function () {
if (enemies.length === 0 && bosses.length === 0) {
spawnBoss();
}
}, 1000);
return;
}
waveEnemyCount = 9;
waveEnemiesDefeated = 0;
isBossWave = false;
for (var wave = 0; wave < 3; wave++) {
LK.setTimeout(function (w) {
return function () {
for (var i = 0; i < 3; i++) {
var enemy = game.addChild(new EnemyShip());
enemy.x = 100 + Math.random() * 1848;
enemy.y = -100 - i * 150;
enemy.speed = 2 + waveNumber * 0.3;
enemy.shootRate = 60;
enemy.waveNumber = waveNumber;
enemy.lastBulletIntersecting = false;
enemies.push(enemy);
}
};
}(wave), wave * 15000);
}
}
//Function to spawn boss wave
function spawnBoss() {
isBossWave = true;
waveEnemiesDefeated = 0;
var boss;
if (bossNumber === 1) {
boss = game.addChild(new Boss1Ship());
} else if (bossNumber === 2) {
boss = game.addChild(new Boss2Ship());
} else if (bossNumber === 3) {
boss = game.addChild(new Boss3Ship());
} else {
boss = game.addChild(new Boss3Ship());
}
boss.x = 1024;
boss.startX = 1024;
boss.y = 300;
boss.shootRate = Math.max(35, 60 - bossNumber * 3);
bossHealthBarBg.visible = true;
bossHealthBar.visible = true;
bossHealthText.visible = true;
bosses.push(boss);
}
//Initialize first wave
spawnEnemyWave();
//Handle game movement
game.move = function (x, y, obj) {
touchX = x;
playerShip.x = Math.max(30, Math.min(2018, x));
playerShip.y = Math.max(100, Math.min(2650, y));
};
//Main game update loop
game.update = function () {
if (gameState !== 'playing') {
return;
}
//Update player ship
playerShip.update();
playerShip.shootCooldown--;
//Player shooting
if (playerShip.shootCooldown <= 0) {
for (var i = 0; i < playerShip.firepower; i++) {
var offset = (playerShip.firepower - 1) * -15 + i * 30;
var bullet = game.addChild(new PlayerBullet());
bullet.x = playerShip.x + offset;
bullet.y = playerShip.y - 40;
bullet.damage = playerShip.firepower;
playerBullets.push(bullet);
LK.getSound('playerShoot').play();
}
playerShip.shootCooldown = playerShip.shootRate;
}
//Update player bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
bullet.update();
if (bullet.y < -50) {
bullet.destroy();
playerBullets.splice(i, 1);
continue;
}
}
//Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (!enemy) {
continue;
}
enemy.update();
//Enemy shooting
enemy.shootCooldown--;
if (enemy.shootCooldown <= 0) {
var eBullet = game.addChild(new EnemyBullet());
eBullet.x = enemy.x;
eBullet.y = enemy.y + 30;
enemyBullets.push(eBullet);
LK.getSound('enemyShoot').play();
enemy.shootCooldown = enemy.shootRate;
}
//Check enemy off-screen
if (enemy.y > 2800) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
//Check bullet collisions with enemies
for (var j = playerBullets.length - 1; j >= 0; j--) {
var bullet = playerBullets[j];
if (bullet && enemy && bullet.intersects(enemy) && !enemy.lastBulletIntersecting) {
enemy.health -= bullet.damage;
enemy.lastBulletIntersecting = true;
bullet.destroy();
playerBullets.splice(j, 1);
if (enemy.health <= 0) {
LK.getSound('explosion').play();
LK.effects.flashObject(enemy, 0xff0000, 200);
LK.setScore(LK.getScore() + 100);
waveEnemiesDefeated++;
//Drop upgrade pickup chance
if (Math.random() < 0.3) {
var pickup = game.addChild(new UpgradePickup());
pickup.x = enemy.x;
pickup.y = enemy.y;
pickup.originalY = enemy.y;
var worldUpgrades = getWorldUpgrades();
pickup.upgradeType = worldUpgrades[Math.floor(Math.random() * worldUpgrades.length)];
pickup.setWorldType(waveNumber);
upgradePickups.push(pickup);
}
enemy.destroy();
enemies.splice(i, 1);
}
break;
}
}
}
//Update bosses
for (var i = bosses.length - 1; i >= 0; i--) {
var boss = bosses[i];
if (!boss) {
continue;
}
boss.update();
//Check Boss2 contact damage with player
if (boss.bossNumber === 2 && boss.isAttackPhase) {
if (boss.intersects(playerShip) && !boss.lastContactWithPlayer) {
boss.lastContactWithPlayer = true;
playerShip.health -= 1;
LK.effects.flashObject(playerShip, 0xff0000, 200);
if (playerShip.health <= 0) {
gameState = 'gameover';
LK.showGameOver();
} //{Boss2-contactDamage}
} else if (!boss.intersects(playerShip)) {
boss.lastContactWithPlayer = false;
}
} //{Boss2-contactDamageEnd}
//Boss shooting (multiple bullets)
boss.shootCooldown--;
if (boss.shootCooldown <= 0) {
for (var b = 0; b < 2; b++) {
var eBullet = game.addChild(new EnemyBullet());
eBullet.x = boss.x + (b - 1) * 30;
eBullet.y = boss.y + 60;
enemyBullets.push(eBullet);
}
LK.getSound('enemyShoot').play();
boss.shootCooldown = boss.shootRate;
}
//Check bullet collisions with boss
for (var j = playerBullets.length - 1; j >= 0; j--) {
var bullet = playerBullets[j];
if (bullet && boss && bullet.intersects(boss)) {
boss.health -= 1;
bullet.destroy();
playerBullets.splice(j, 1);
LK.effects.flashObject(boss, 0xff00ff, 150);
if (boss.health <= 0) {
LK.getSound('explosion').play();
LK.effects.flashScreen(0xffff00, 500);
LK.setScore(LK.getScore() + 1000);
//Grant boss defeat upgrade
playerShip.upgradePoints += 3;
playerShip.firepower += 1;
playerShip.armor += 1;
playerShip.health = playerShip.maxHealth;
boss.destroy();
bosses.splice(i, 1);
bossNumber++;
waveNumber++;
//Spawn next wave after delay
LK.setTimeout(function () {
spawnEnemyWave();
}, 1000);
}
break;
}
}
}
//Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
bullet.update();
if (bullet.y > 2800) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
//Check collision with player
if (bullet.intersects(playerShip) && !bullet.lastPlayerIntersecting) {
bullet.lastPlayerIntersecting = true;
playerShip.health -= Math.max(1, bullet.damage - (playerShip.armor - 1));
bullet.destroy();
enemyBullets.splice(i, 1);
LK.effects.flashObject(playerShip, 0xff0000, 200);
if (playerShip.health <= 0) {
gameState = 'gameover';
LK.showGameOver();
}
continue;
}
}
//Update upgrade pickups
for (var i = upgradePickups.length - 1; i >= 0; i--) {
var pickup = upgradePickups[i];
pickup.update();
//Check collision with player
if (pickup.intersects(playerShip) && !pickup.lastPlayerIntersecting) {
pickup.lastPlayerIntersecting = true;
LK.getSound('upgrade').play();
if (pickup.upgradeType === 'firepower') {
playerShip.firepower++;
} else if (pickup.upgradeType === 'armor') {
playerShip.armor++;
playerShip.health = playerShip.maxHealth;
} else if (pickup.upgradeType === 'speed') {
playerShip.shootRate = Math.max(5, playerShip.shootRate - 2);
} else if (pickup.upgradeType === 'rapidFire') {
playerShip.shootRate = Math.max(5, playerShip.shootRate - 3);
} else if (pickup.upgradeType === 'shield') {
playerShip.armor += 2;
playerShip.health = playerShip.maxHealth;
} else if (pickup.upgradeType === 'laser') {
playerShip.firepower += 2;
} else if (pickup.upgradeType === 'multishot') {
playerShip.firepower += 1;
playerShip.shootRate = Math.max(5, playerShip.shootRate - 1);
}
playerShip.upgradePoints++;
LK.effects.flashObject(playerShip, 0x00ff00, 300);
pickup.destroy();
upgradePickups.splice(i, 1);
continue;
}
//Remove off-screen pickups
if (pickup.y > 2800) {
pickup.destroy();
upgradePickups.splice(i, 1);
}
}
//Check if wave is complete and spawn boss
if (enemies.length === 0 && !isBossWave && bosses.length === 0 && waveEnemiesDefeated === waveEnemyCount && waveEnemyCount > 0) {
waveEnemyCount = 0;
LK.setTimeout(function () {
if (enemies.length === 0 && bosses.length === 0) {
spawnBoss();
}
}, 1500);
}
//Update boss health bar
if (bosses.length > 0) {
var boss = bosses[0];
var healthPercentage = Math.max(0, boss.health / boss.maxHealth);
bossHealthBar.scaleX = healthPercentage;
bossHealthBarBg.x = 1024;
bossHealthBarBg.y = 60;
bossHealthBar.x = 1024 - 190;
bossHealthBar.y = 60;
bossHealthText.setText('Boss ' + boss.bossNumber + ' HP: ' + Math.max(0, boss.health) + '/' + boss.maxHealth);
} else {
bossHealthBarBg.visible = false;
bossHealthBar.visible = false;
bossHealthText.visible = false;
}
//Update UI
healthText.setText('Health: ' + Math.max(0, playerShip.health) + '/' + playerShip.maxHealth);
var healthPercentage = Math.max(0, playerShip.health / playerShip.maxHealth);
playerHealthBar.scaleX = healthPercentage;
if (healthPercentage > 0.5) {
playerHealthBar.tint = 0x00ff00;
} else if (healthPercentage > 0.25) {
playerHealthBar.tint = 0xffff00;
} else {
playerHealthBar.tint = 0xff0000;
}
upgradeText.setText('Upgrades: ' + playerShip.upgradePoints);
waveText.setText('Wave: ' + waveNumber + ' | Boss: ' + bossNumber);
};