/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Bomb (for screen clear effect)
var Bomb = Container.expand(function () {
var self = Container.call(this);
var bomb = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
return self;
});
// Boss
var Boss = Container.expand(function () {
var self = Container.call(this);
var boss = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5
});
self.hp = 6;
self.targetX = 1024;
self.moveSpeed = 18;
self.shootCooldown = 30;
self.update = function () {
// Move horizontally only
var dx = self.targetX - self.x;
if (Math.abs(dx) > self.moveSpeed) {
self.x += dx > 0 ? self.moveSpeed : -self.moveSpeed;
} else {
self.x = self.targetX;
// Pick new target
self.targetX = 300 + Math.random() * (2048 - 600);
}
// Shooting
self.shootCooldown--;
if (self.shootCooldown <= 0) {
// Less frequent: shoot every 60 ticks (was 18)
self.shootCooldown = 60;
spawnBossRockets(self);
}
};
return self;
});
// Boss Rocket
var BossRocket = Container.expand(function () {
var self = Container.call(this);
var rocket = self.attachAsset('bossRocket', {
anchorX: 0.5,
anchorY: 0.5
});
self.dx = 0;
self.dy = 28;
self.ricochet = true;
self.update = function () {
self.x += self.dx;
self.y += self.dy;
// Ricochet off walls
if (self.ricochet) {
if (self.x < 0 && self.dx < 0) self.dx *= -1;
if (self.x > 2048 && self.dx > 0) self.dx *= -1;
}
};
return self;
});
// Enemy Rocket
var EnemyRocket = Container.expand(function () {
var self = Container.call(this);
var rocket = self.attachAsset('enemyRocket', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 10; // slowed down enemy rocket speed
self.dx = 0; // always vertical, no horizontal movement
self.dy = self.speed;
self.ricochet = false;
self.update = function () {
self.x += self.dx;
self.y += self.dy;
// Ricochet off walls if enabled
if (self.ricochet) {
if (self.x < 0 && self.dx < 0) self.dx *= -1;
if (self.x > 2048 && self.dx > 0) self.dx *= -1;
}
};
return self;
});
// Enemy Ship
var EnemyShip = Container.expand(function () {
var self = Container.call(this);
var ship = self.attachAsset('enemyShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.targetX = 0;
self.targetY = 0;
self.moveSpeed = 5 + Math.random() * 3; // slowed down enemy movement
self.shootCooldown = 60 + Math.floor(Math.random() * 60);
self.isRare = false; // rare enemies drop powerups
self.hp = 3; // takes 3 hits to defeat
self.update = function () {
// Move toward target
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > self.moveSpeed) {
self.x += dx / dist * self.moveSpeed;
self.y += dy / dist * self.moveSpeed;
} else {
self.x = self.targetX;
self.y = self.targetY;
// Randomly decide to stay in place for a while
if (Math.random() < 0.4) {
// Stay in place: do not pick a new target, just wait
// Optionally, could add a "waitTicks" property for more control
} else {
// Pick new target
self.targetX = 200 + Math.random() * (2048 - 400);
self.targetY = 200 + Math.random() * 800;
}
}
// Shooting
self.shootCooldown--;
if (self.shootCooldown <= 0) {
// Decrease frequency: increase cooldown (was 60-120, now 120-200)
self.shootCooldown = 120 + Math.floor(Math.random() * 80);
spawnEnemyRocket(self);
}
};
return self;
});
// Player Rocket
var PlayerRocket = Container.expand(function () {
var self = Container.call(this);
var rocket = self.attachAsset('playerRocket', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -32;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player Ship
var PlayerShip = Container.expand(function () {
var self = Container.call(this);
var ship = self.attachAsset('playerShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.lives = 3;
self.shielded = false;
self.fastShoot = false;
self.shootTimer = 0;
self.shieldNode = null;
self.update = function () {
// nothing here, movement is handled by drag
};
self.setShield = function (active) {
if (active && !self.shielded) {
self.shielded = true;
self.shieldNode = new Shield();
self.shieldNode.x = 0;
self.shieldNode.y = 0;
self.addChild(self.shieldNode);
} else if (!active && self.shielded) {
if (self.shieldNode) {
self.shieldNode.destroy();
self.shieldNode = null;
}
self.shielded = false;
}
};
return self;
});
// PowerUp
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerup = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'fast'; // 'fast', 'life', 'shield', 'bomb'
self.speed = 12;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Shield
var Shield = Container.expand(function () {
var self = Container.call(this);
var shield = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.4
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Play music
// Player ship: blue box
// Player rocket: yellow box
// Enemy ship: red ellipse
// Enemy rocket: orange box
// Power-up: green ellipse
// Shield: cyan ellipse
// Boss: purple box
// Boss rocket: magenta box
// Bomb: white ellipse
// Sounds
// Music
// Game state
var gameStarted = false;
var startScreen = null;
var titleText = null;
var playButton = null;
// Score, lives, state
var score = 0;
var lives = 3;
var player = null;
var playerShieldTimeout = 0;
var playerFastShootTimeout = 0;
var playerBombActive = false;
var playerInvulnTimeout = 0;
var boss = null;
var bossActive = false;
var bossDefeated = false;
var bossHpTxt = null;
// Arrays for game objects
var playerRockets = [];
var enemyRockets = [];
var bossRockets = [];
var enemies = [];
var powerups = [];
var bombs = [];
// GUI
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var livesTxt = new Text2('♥♥♥', {
size: 90,
fill: 0xFF6666
});
livesTxt.anchor.set(1, 0);
livesTxt.x = -60;
LK.gui.topRight.addChild(livesTxt);
// Helper: update GUI
function updateScore() {
scoreTxt.setText(score);
}
function updateLives() {
var s = '';
for (var i = 0; i < lives; ++i) s += '♥';
livesTxt.setText(s);
}
function showBossHp(hp) {
if (!bossHpTxt) {
bossHpTxt = new Text2('Boss: ' + hp, {
size: 90,
fill: 0xFFCCFF
});
bossHpTxt.anchor.set(0.5, 0);
LK.gui.bottom.addChild(bossHpTxt);
} else {
bossHpTxt.setText('Boss: ' + hp);
}
}
function hideBossHp() {
if (bossHpTxt) {
bossHpTxt.destroy();
bossHpTxt = null;
}
}
// Dragging
var dragNode = null;
game.down = function (x, y, obj) {
// If game hasn't started, start it on any tap
if (!gameStarted) {
startGame();
return;
}
// Only allow drag if touch is on player
if (player && Math.abs(x - player.x) < 120 && Math.abs(y - player.y) < 80) {
dragNode = player;
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
function handleMove(x, y, obj) {
if (dragNode === player && player) {
// Clamp to screen
var px = Math.max(80, Math.min(2048 - 80, x));
var py = Math.max(2732 - 300, Math.min(2732 - 80, y));
player.x = px;
player.y = py;
}
}
game.move = handleMove;
// Spawn enemy
function spawnEnemy() {
var e = new EnemyShip();
e.x = 200 + Math.random() * (2048 - 400);
e.y = 100 + Math.random() * 200;
e.targetX = 200 + Math.random() * (2048 - 400);
e.targetY = 200 + Math.random() * 800;
// 1 in 7 is rare
if (Math.random() < 0.14) {
e.isRare = true;
// Tint rare enemies
e.children[0].tint = 0x00ff99;
}
enemies.push(e);
game.addChild(e);
}
// Spawn powerup
function spawnPowerUp(x, y) {
var p = new PowerUp();
p.x = x;
p.y = y;
// Random type
var r = Math.random();
if (r < 0.25) p.type = 'fast';else if (r < 0.5) p.type = 'life';else if (r < 0.75) p.type = 'shield';else p.type = 'bomb';
// Color by type
if (p.type === 'fast') p.children[0].tint = 0x44ff44;
if (p.type === 'life') p.children[0].tint = 0xff66cc;
if (p.type === 'shield') p.children[0].tint = 0x00ffff;
if (p.type === 'bomb') p.children[0].tint = 0xffffff;
powerups.push(p);
game.addChild(p);
}
// Spawn player rocket
function spawnPlayerRocket() {
var r = new PlayerRocket();
r.x = player.x;
r.y = player.y - 80;
playerRockets.push(r);
game.addChild(r);
LK.getSound('shoot').play();
}
// Spawn enemy rocket
function spawnEnemyRocket(enemy) {
var r = new EnemyRocket();
r.x = enemy.x;
r.y = enemy.y + 60;
// Only shoot vertically down, no targeting
r.dx = 0;
r.dy = r.speed;
r.ricochet = false;
enemyRockets.push(r);
game.addChild(r);
LK.getSound('enemyShoot').play();
}
// Spawn boss
function spawnBoss() {
boss = new Boss();
boss.x = 1024;
boss.y = 400;
boss.hp = 6;
bossActive = true;
game.addChild(boss);
showBossHp(boss.hp);
}
// Spawn boss rockets (single fast ricochet, less frequent, less spread)
function spawnBossRockets(bossObj) {
// Only shoot a single rocket, straight down, fast, ricochet enabled
var r = new BossRocket();
r.x = bossObj.x;
r.y = bossObj.y + 100;
// Angle straight down
var angle = Math.PI / 2;
r.dx = 0;
r.dy = 38; // much faster
r.ricochet = true;
bossRockets.push(r);
game.addChild(r);
LK.getSound('bossShoot').play();
}
// Bomb effect: clear all enemy/boss rockets and damage all enemies
function activateBomb() {
if (playerBombActive) return;
playerBombActive = true;
var bomb = new Bomb();
bomb.x = player.x;
bomb.y = player.y - 100;
bombs.push(bomb);
game.addChild(bomb);
LK.getSound('bomb').play();
// Flash
LK.effects.flashScreen(0xffffff, 400);
// Destroy all enemy/boss rockets
for (var i = enemyRockets.length - 1; i >= 0; --i) {
enemyRockets[i].destroy();
enemyRockets.splice(i, 1);
}
for (var i = bossRockets.length - 1; i >= 0; --i) {
bossRockets[i].destroy();
bossRockets.splice(i, 1);
}
// Damage all enemies
for (var i = enemies.length - 1; i >= 0; --i) {
enemies[i].destroy();
enemies.splice(i, 1);
score += 1;
updateScore();
}
// Damage boss if present
if (bossActive && boss) {
boss.hp -= 5;
if (boss.hp < 0) boss.hp = 0;
showBossHp(boss.hp);
LK.effects.flashObject(boss, 0xffffff, 400);
}
// Remove bomb after effect
LK.setTimeout(function () {
if (bomb) bomb.destroy();
playerBombActive = false;
}, 600);
}
// Main update
game.update = function () {
// Don't update game logic if game hasn't started
if (!gameStarted) return;
// Player shooting
if (player) {
player.shootTimer--;
var shootInterval = player.fastShoot ? 7 : 18;
if (player.shootTimer <= 0) {
player.shootTimer = shootInterval;
spawnPlayerRocket();
}
}
// Player powerup timers
if (playerFastShootTimeout > 0) {
playerFastShootTimeout--;
if (playerFastShootTimeout === 0) {
player.fastShoot = false;
}
}
if (playerShieldTimeout > 0) {
playerShieldTimeout--;
if (playerShieldTimeout === 0) {
player.setShield(false);
}
}
if (playerInvulnTimeout > 0) {
playerInvulnTimeout--;
}
// Update player rockets
for (var i = playerRockets.length - 1; i >= 0; --i) {
var r = playerRockets[i];
r.update();
// Off screen
if (r.y < -80) {
r.destroy();
playerRockets.splice(i, 1);
continue;
}
// Hit enemy
for (var j = enemies.length - 1; j >= 0; --j) {
var e = enemies[j];
if (r.intersects(e)) {
LK.getSound('explosion').play();
r.destroy();
playerRockets.splice(i, 1);
e.hp -= 1;
LK.effects.flashObject(e, 0xffffff, 120);
// Powerup drop if destroyed
if (e.hp <= 0) {
if (e.isRare) {
spawnPowerUp(e.x, e.y);
}
e.destroy();
enemies.splice(j, 1);
score += 1;
updateScore();
}
break;
}
}
// Hit boss
if (bossActive && boss && r.intersects(boss)) {
LK.getSound('hit').play();
r.destroy();
playerRockets.splice(i, 1);
boss.hp -= 1;
showBossHp(boss.hp);
LK.effects.flashObject(boss, 0xff33cc, 200);
if (boss.hp <= 0) {
// Boss defeated
bossDefeated = true;
boss.destroy();
boss = null;
bossActive = false;
hideBossHp();
LK.effects.flashScreen(0x00ffcc, 1200);
LK.setScore(score);
LK.showYouWin();
return;
}
}
}
// Update enemy rockets
for (var i = enemyRockets.length - 1; i >= 0; --i) {
var r = enemyRockets[i];
r.update();
// Off screen
if (r.y > 2732 + 80 || r.x < -80 || r.x > 2048 + 80) {
r.destroy();
enemyRockets.splice(i, 1);
continue;
}
// Hit player
if (player && r.intersects(player)) {
r.destroy();
enemyRockets.splice(i, 1);
if (player.shielded) {
LK.getSound('shield').play();
player.setShield(false);
} else if (playerInvulnTimeout === 0) {
lives -= 1;
updateLives();
LK.getSound('hit').play();
LK.effects.flashObject(player, 0xff0000, 600);
playerInvulnTimeout = 60;
if (lives <= 0) {
LK.effects.flashScreen(0xff0000, 1200);
LK.setScore(score);
LK.showGameOver();
return;
}
}
}
}
// Update boss rockets
for (var i = bossRockets.length - 1; i >= 0; --i) {
var r = bossRockets[i];
r.update();
// Off screen
if (r.y > 2732 + 80 || r.x < -80 || r.x > 2048 + 80) {
r.destroy();
bossRockets.splice(i, 1);
continue;
}
// Hit player
if (player && r.intersects(player)) {
r.destroy();
bossRockets.splice(i, 1);
if (player.shielded) {
LK.getSound('shield').play();
player.setShield(false);
} else if (playerInvulnTimeout === 0) {
lives -= 1;
updateLives();
LK.getSound('hit').play();
LK.effects.flashObject(player, 0xff0000, 600);
playerInvulnTimeout = 60;
if (lives <= 0) {
LK.effects.flashScreen(0xff0000, 1200);
LK.setScore(score);
LK.showGameOver();
return;
}
}
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; --i) {
var e = enemies[i];
e.update();
// Off screen (shouldn't happen)
if (e.y > 2732 + 100) {
e.destroy();
enemies.splice(i, 1);
continue;
}
// Collide with player
if (player && e.intersects(player)) {
LK.getSound('hit').play();
e.destroy();
enemies.splice(i, 1);
if (player.shielded) {
player.setShield(false);
} else if (playerInvulnTimeout === 0) {
lives -= 1;
updateLives();
LK.effects.flashObject(player, 0xff0000, 600);
playerInvulnTimeout = 60;
if (lives <= 0) {
LK.effects.flashScreen(0xff0000, 1200);
LK.setScore(score);
LK.showGameOver();
return;
}
}
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; --i) {
var p = powerups[i];
p.update();
// Off screen
if (p.y > 2732 + 80) {
p.destroy();
powerups.splice(i, 1);
continue;
}
// Collect
if (player && p.intersects(player)) {
LK.getSound('powerup').play();
if (p.type === 'fast') {
player.fastShoot = true;
playerFastShootTimeout = 360;
} else if (p.type === 'life') {
lives += 1;
updateLives();
LK.getSound('lifeup').play();
} else if (p.type === 'shield') {
player.setShield(true);
playerShieldTimeout = 360;
LK.getSound('shield').play();
} else if (p.type === 'bomb') {
activateBomb();
}
p.destroy();
powerups.splice(i, 1);
}
}
// Update bombs
for (var i = bombs.length - 1; i >= 0; --i) {
// Bombs are visual only, auto destroy after effect
}
// Boss
if (bossActive && boss) {
boss.update();
}
// Spawn enemies: 2 at 0 points, +1 per point up to 6, always keep at least (score+2, max 6) enemies on screen
if (!bossActive) {
var desiredEnemies = Math.min(score + 2, 6);
var toSpawn = desiredEnemies - enemies.length;
for (var i = 0; i < toSpawn; ++i) {
spawnEnemy();
}
}
// Boss appears at 20 points
if (!bossActive && !bossDefeated && score >= 20) {
// Remove all enemies
for (var i = enemies.length - 1; i >= 0; --i) {
enemies[i].destroy();
enemies.splice(i, 1);
}
spawnBoss();
}
};
// Create start screen
function createStartScreen() {
startScreen = new Container();
game.addChild(startScreen);
// Title text
titleText = new Text2('SPACE SHOOTER', {
size: 180,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 1000;
startScreen.addChild(titleText);
// Play button
playButton = new Text2('TAP TO PLAY', {
size: 120,
fill: 0x00FF00
});
playButton.anchor.set(0.5, 0.5);
playButton.x = 1024;
playButton.y = 1400;
startScreen.addChild(playButton);
}
// Start the actual game
function startGame() {
if (gameStarted) return;
gameStarted = true;
// Remove start screen
if (startScreen) {
startScreen.destroy();
startScreen = null;
}
// Start music
LK.playMusic('spacebg');
// Spawn player
player = new PlayerShip();
player.x = 1024;
player.y = 2732 - 180;
game.addChild(player);
// Set initial GUI
updateScore();
updateLives();
}
// Initialize start screen
createStartScreen(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Bomb (for screen clear effect)
var Bomb = Container.expand(function () {
var self = Container.call(this);
var bomb = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
return self;
});
// Boss
var Boss = Container.expand(function () {
var self = Container.call(this);
var boss = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5
});
self.hp = 6;
self.targetX = 1024;
self.moveSpeed = 18;
self.shootCooldown = 30;
self.update = function () {
// Move horizontally only
var dx = self.targetX - self.x;
if (Math.abs(dx) > self.moveSpeed) {
self.x += dx > 0 ? self.moveSpeed : -self.moveSpeed;
} else {
self.x = self.targetX;
// Pick new target
self.targetX = 300 + Math.random() * (2048 - 600);
}
// Shooting
self.shootCooldown--;
if (self.shootCooldown <= 0) {
// Less frequent: shoot every 60 ticks (was 18)
self.shootCooldown = 60;
spawnBossRockets(self);
}
};
return self;
});
// Boss Rocket
var BossRocket = Container.expand(function () {
var self = Container.call(this);
var rocket = self.attachAsset('bossRocket', {
anchorX: 0.5,
anchorY: 0.5
});
self.dx = 0;
self.dy = 28;
self.ricochet = true;
self.update = function () {
self.x += self.dx;
self.y += self.dy;
// Ricochet off walls
if (self.ricochet) {
if (self.x < 0 && self.dx < 0) self.dx *= -1;
if (self.x > 2048 && self.dx > 0) self.dx *= -1;
}
};
return self;
});
// Enemy Rocket
var EnemyRocket = Container.expand(function () {
var self = Container.call(this);
var rocket = self.attachAsset('enemyRocket', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 10; // slowed down enemy rocket speed
self.dx = 0; // always vertical, no horizontal movement
self.dy = self.speed;
self.ricochet = false;
self.update = function () {
self.x += self.dx;
self.y += self.dy;
// Ricochet off walls if enabled
if (self.ricochet) {
if (self.x < 0 && self.dx < 0) self.dx *= -1;
if (self.x > 2048 && self.dx > 0) self.dx *= -1;
}
};
return self;
});
// Enemy Ship
var EnemyShip = Container.expand(function () {
var self = Container.call(this);
var ship = self.attachAsset('enemyShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.targetX = 0;
self.targetY = 0;
self.moveSpeed = 5 + Math.random() * 3; // slowed down enemy movement
self.shootCooldown = 60 + Math.floor(Math.random() * 60);
self.isRare = false; // rare enemies drop powerups
self.hp = 3; // takes 3 hits to defeat
self.update = function () {
// Move toward target
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > self.moveSpeed) {
self.x += dx / dist * self.moveSpeed;
self.y += dy / dist * self.moveSpeed;
} else {
self.x = self.targetX;
self.y = self.targetY;
// Randomly decide to stay in place for a while
if (Math.random() < 0.4) {
// Stay in place: do not pick a new target, just wait
// Optionally, could add a "waitTicks" property for more control
} else {
// Pick new target
self.targetX = 200 + Math.random() * (2048 - 400);
self.targetY = 200 + Math.random() * 800;
}
}
// Shooting
self.shootCooldown--;
if (self.shootCooldown <= 0) {
// Decrease frequency: increase cooldown (was 60-120, now 120-200)
self.shootCooldown = 120 + Math.floor(Math.random() * 80);
spawnEnemyRocket(self);
}
};
return self;
});
// Player Rocket
var PlayerRocket = Container.expand(function () {
var self = Container.call(this);
var rocket = self.attachAsset('playerRocket', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -32;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player Ship
var PlayerShip = Container.expand(function () {
var self = Container.call(this);
var ship = self.attachAsset('playerShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.lives = 3;
self.shielded = false;
self.fastShoot = false;
self.shootTimer = 0;
self.shieldNode = null;
self.update = function () {
// nothing here, movement is handled by drag
};
self.setShield = function (active) {
if (active && !self.shielded) {
self.shielded = true;
self.shieldNode = new Shield();
self.shieldNode.x = 0;
self.shieldNode.y = 0;
self.addChild(self.shieldNode);
} else if (!active && self.shielded) {
if (self.shieldNode) {
self.shieldNode.destroy();
self.shieldNode = null;
}
self.shielded = false;
}
};
return self;
});
// PowerUp
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerup = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'fast'; // 'fast', 'life', 'shield', 'bomb'
self.speed = 12;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Shield
var Shield = Container.expand(function () {
var self = Container.call(this);
var shield = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.4
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Play music
// Player ship: blue box
// Player rocket: yellow box
// Enemy ship: red ellipse
// Enemy rocket: orange box
// Power-up: green ellipse
// Shield: cyan ellipse
// Boss: purple box
// Boss rocket: magenta box
// Bomb: white ellipse
// Sounds
// Music
// Game state
var gameStarted = false;
var startScreen = null;
var titleText = null;
var playButton = null;
// Score, lives, state
var score = 0;
var lives = 3;
var player = null;
var playerShieldTimeout = 0;
var playerFastShootTimeout = 0;
var playerBombActive = false;
var playerInvulnTimeout = 0;
var boss = null;
var bossActive = false;
var bossDefeated = false;
var bossHpTxt = null;
// Arrays for game objects
var playerRockets = [];
var enemyRockets = [];
var bossRockets = [];
var enemies = [];
var powerups = [];
var bombs = [];
// GUI
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var livesTxt = new Text2('♥♥♥', {
size: 90,
fill: 0xFF6666
});
livesTxt.anchor.set(1, 0);
livesTxt.x = -60;
LK.gui.topRight.addChild(livesTxt);
// Helper: update GUI
function updateScore() {
scoreTxt.setText(score);
}
function updateLives() {
var s = '';
for (var i = 0; i < lives; ++i) s += '♥';
livesTxt.setText(s);
}
function showBossHp(hp) {
if (!bossHpTxt) {
bossHpTxt = new Text2('Boss: ' + hp, {
size: 90,
fill: 0xFFCCFF
});
bossHpTxt.anchor.set(0.5, 0);
LK.gui.bottom.addChild(bossHpTxt);
} else {
bossHpTxt.setText('Boss: ' + hp);
}
}
function hideBossHp() {
if (bossHpTxt) {
bossHpTxt.destroy();
bossHpTxt = null;
}
}
// Dragging
var dragNode = null;
game.down = function (x, y, obj) {
// If game hasn't started, start it on any tap
if (!gameStarted) {
startGame();
return;
}
// Only allow drag if touch is on player
if (player && Math.abs(x - player.x) < 120 && Math.abs(y - player.y) < 80) {
dragNode = player;
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
function handleMove(x, y, obj) {
if (dragNode === player && player) {
// Clamp to screen
var px = Math.max(80, Math.min(2048 - 80, x));
var py = Math.max(2732 - 300, Math.min(2732 - 80, y));
player.x = px;
player.y = py;
}
}
game.move = handleMove;
// Spawn enemy
function spawnEnemy() {
var e = new EnemyShip();
e.x = 200 + Math.random() * (2048 - 400);
e.y = 100 + Math.random() * 200;
e.targetX = 200 + Math.random() * (2048 - 400);
e.targetY = 200 + Math.random() * 800;
// 1 in 7 is rare
if (Math.random() < 0.14) {
e.isRare = true;
// Tint rare enemies
e.children[0].tint = 0x00ff99;
}
enemies.push(e);
game.addChild(e);
}
// Spawn powerup
function spawnPowerUp(x, y) {
var p = new PowerUp();
p.x = x;
p.y = y;
// Random type
var r = Math.random();
if (r < 0.25) p.type = 'fast';else if (r < 0.5) p.type = 'life';else if (r < 0.75) p.type = 'shield';else p.type = 'bomb';
// Color by type
if (p.type === 'fast') p.children[0].tint = 0x44ff44;
if (p.type === 'life') p.children[0].tint = 0xff66cc;
if (p.type === 'shield') p.children[0].tint = 0x00ffff;
if (p.type === 'bomb') p.children[0].tint = 0xffffff;
powerups.push(p);
game.addChild(p);
}
// Spawn player rocket
function spawnPlayerRocket() {
var r = new PlayerRocket();
r.x = player.x;
r.y = player.y - 80;
playerRockets.push(r);
game.addChild(r);
LK.getSound('shoot').play();
}
// Spawn enemy rocket
function spawnEnemyRocket(enemy) {
var r = new EnemyRocket();
r.x = enemy.x;
r.y = enemy.y + 60;
// Only shoot vertically down, no targeting
r.dx = 0;
r.dy = r.speed;
r.ricochet = false;
enemyRockets.push(r);
game.addChild(r);
LK.getSound('enemyShoot').play();
}
// Spawn boss
function spawnBoss() {
boss = new Boss();
boss.x = 1024;
boss.y = 400;
boss.hp = 6;
bossActive = true;
game.addChild(boss);
showBossHp(boss.hp);
}
// Spawn boss rockets (single fast ricochet, less frequent, less spread)
function spawnBossRockets(bossObj) {
// Only shoot a single rocket, straight down, fast, ricochet enabled
var r = new BossRocket();
r.x = bossObj.x;
r.y = bossObj.y + 100;
// Angle straight down
var angle = Math.PI / 2;
r.dx = 0;
r.dy = 38; // much faster
r.ricochet = true;
bossRockets.push(r);
game.addChild(r);
LK.getSound('bossShoot').play();
}
// Bomb effect: clear all enemy/boss rockets and damage all enemies
function activateBomb() {
if (playerBombActive) return;
playerBombActive = true;
var bomb = new Bomb();
bomb.x = player.x;
bomb.y = player.y - 100;
bombs.push(bomb);
game.addChild(bomb);
LK.getSound('bomb').play();
// Flash
LK.effects.flashScreen(0xffffff, 400);
// Destroy all enemy/boss rockets
for (var i = enemyRockets.length - 1; i >= 0; --i) {
enemyRockets[i].destroy();
enemyRockets.splice(i, 1);
}
for (var i = bossRockets.length - 1; i >= 0; --i) {
bossRockets[i].destroy();
bossRockets.splice(i, 1);
}
// Damage all enemies
for (var i = enemies.length - 1; i >= 0; --i) {
enemies[i].destroy();
enemies.splice(i, 1);
score += 1;
updateScore();
}
// Damage boss if present
if (bossActive && boss) {
boss.hp -= 5;
if (boss.hp < 0) boss.hp = 0;
showBossHp(boss.hp);
LK.effects.flashObject(boss, 0xffffff, 400);
}
// Remove bomb after effect
LK.setTimeout(function () {
if (bomb) bomb.destroy();
playerBombActive = false;
}, 600);
}
// Main update
game.update = function () {
// Don't update game logic if game hasn't started
if (!gameStarted) return;
// Player shooting
if (player) {
player.shootTimer--;
var shootInterval = player.fastShoot ? 7 : 18;
if (player.shootTimer <= 0) {
player.shootTimer = shootInterval;
spawnPlayerRocket();
}
}
// Player powerup timers
if (playerFastShootTimeout > 0) {
playerFastShootTimeout--;
if (playerFastShootTimeout === 0) {
player.fastShoot = false;
}
}
if (playerShieldTimeout > 0) {
playerShieldTimeout--;
if (playerShieldTimeout === 0) {
player.setShield(false);
}
}
if (playerInvulnTimeout > 0) {
playerInvulnTimeout--;
}
// Update player rockets
for (var i = playerRockets.length - 1; i >= 0; --i) {
var r = playerRockets[i];
r.update();
// Off screen
if (r.y < -80) {
r.destroy();
playerRockets.splice(i, 1);
continue;
}
// Hit enemy
for (var j = enemies.length - 1; j >= 0; --j) {
var e = enemies[j];
if (r.intersects(e)) {
LK.getSound('explosion').play();
r.destroy();
playerRockets.splice(i, 1);
e.hp -= 1;
LK.effects.flashObject(e, 0xffffff, 120);
// Powerup drop if destroyed
if (e.hp <= 0) {
if (e.isRare) {
spawnPowerUp(e.x, e.y);
}
e.destroy();
enemies.splice(j, 1);
score += 1;
updateScore();
}
break;
}
}
// Hit boss
if (bossActive && boss && r.intersects(boss)) {
LK.getSound('hit').play();
r.destroy();
playerRockets.splice(i, 1);
boss.hp -= 1;
showBossHp(boss.hp);
LK.effects.flashObject(boss, 0xff33cc, 200);
if (boss.hp <= 0) {
// Boss defeated
bossDefeated = true;
boss.destroy();
boss = null;
bossActive = false;
hideBossHp();
LK.effects.flashScreen(0x00ffcc, 1200);
LK.setScore(score);
LK.showYouWin();
return;
}
}
}
// Update enemy rockets
for (var i = enemyRockets.length - 1; i >= 0; --i) {
var r = enemyRockets[i];
r.update();
// Off screen
if (r.y > 2732 + 80 || r.x < -80 || r.x > 2048 + 80) {
r.destroy();
enemyRockets.splice(i, 1);
continue;
}
// Hit player
if (player && r.intersects(player)) {
r.destroy();
enemyRockets.splice(i, 1);
if (player.shielded) {
LK.getSound('shield').play();
player.setShield(false);
} else if (playerInvulnTimeout === 0) {
lives -= 1;
updateLives();
LK.getSound('hit').play();
LK.effects.flashObject(player, 0xff0000, 600);
playerInvulnTimeout = 60;
if (lives <= 0) {
LK.effects.flashScreen(0xff0000, 1200);
LK.setScore(score);
LK.showGameOver();
return;
}
}
}
}
// Update boss rockets
for (var i = bossRockets.length - 1; i >= 0; --i) {
var r = bossRockets[i];
r.update();
// Off screen
if (r.y > 2732 + 80 || r.x < -80 || r.x > 2048 + 80) {
r.destroy();
bossRockets.splice(i, 1);
continue;
}
// Hit player
if (player && r.intersects(player)) {
r.destroy();
bossRockets.splice(i, 1);
if (player.shielded) {
LK.getSound('shield').play();
player.setShield(false);
} else if (playerInvulnTimeout === 0) {
lives -= 1;
updateLives();
LK.getSound('hit').play();
LK.effects.flashObject(player, 0xff0000, 600);
playerInvulnTimeout = 60;
if (lives <= 0) {
LK.effects.flashScreen(0xff0000, 1200);
LK.setScore(score);
LK.showGameOver();
return;
}
}
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; --i) {
var e = enemies[i];
e.update();
// Off screen (shouldn't happen)
if (e.y > 2732 + 100) {
e.destroy();
enemies.splice(i, 1);
continue;
}
// Collide with player
if (player && e.intersects(player)) {
LK.getSound('hit').play();
e.destroy();
enemies.splice(i, 1);
if (player.shielded) {
player.setShield(false);
} else if (playerInvulnTimeout === 0) {
lives -= 1;
updateLives();
LK.effects.flashObject(player, 0xff0000, 600);
playerInvulnTimeout = 60;
if (lives <= 0) {
LK.effects.flashScreen(0xff0000, 1200);
LK.setScore(score);
LK.showGameOver();
return;
}
}
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; --i) {
var p = powerups[i];
p.update();
// Off screen
if (p.y > 2732 + 80) {
p.destroy();
powerups.splice(i, 1);
continue;
}
// Collect
if (player && p.intersects(player)) {
LK.getSound('powerup').play();
if (p.type === 'fast') {
player.fastShoot = true;
playerFastShootTimeout = 360;
} else if (p.type === 'life') {
lives += 1;
updateLives();
LK.getSound('lifeup').play();
} else if (p.type === 'shield') {
player.setShield(true);
playerShieldTimeout = 360;
LK.getSound('shield').play();
} else if (p.type === 'bomb') {
activateBomb();
}
p.destroy();
powerups.splice(i, 1);
}
}
// Update bombs
for (var i = bombs.length - 1; i >= 0; --i) {
// Bombs are visual only, auto destroy after effect
}
// Boss
if (bossActive && boss) {
boss.update();
}
// Spawn enemies: 2 at 0 points, +1 per point up to 6, always keep at least (score+2, max 6) enemies on screen
if (!bossActive) {
var desiredEnemies = Math.min(score + 2, 6);
var toSpawn = desiredEnemies - enemies.length;
for (var i = 0; i < toSpawn; ++i) {
spawnEnemy();
}
}
// Boss appears at 20 points
if (!bossActive && !bossDefeated && score >= 20) {
// Remove all enemies
for (var i = enemies.length - 1; i >= 0; --i) {
enemies[i].destroy();
enemies.splice(i, 1);
}
spawnBoss();
}
};
// Create start screen
function createStartScreen() {
startScreen = new Container();
game.addChild(startScreen);
// Title text
titleText = new Text2('SPACE SHOOTER', {
size: 180,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 1000;
startScreen.addChild(titleText);
// Play button
playButton = new Text2('TAP TO PLAY', {
size: 120,
fill: 0x00FF00
});
playButton.anchor.set(0.5, 0.5);
playButton.x = 1024;
playButton.y = 1400;
startScreen.addChild(playButton);
}
// Start the actual game
function startGame() {
if (gameStarted) return;
gameStarted = true;
// Remove start screen
if (startScreen) {
startScreen.destroy();
startScreen = null;
}
// Start music
LK.playMusic('spacebg');
// Spawn player
player = new PlayerShip();
player.x = 1024;
player.y = 2732 - 180;
game.addChild(player);
// Set initial GUI
updateScore();
updateLives();
}
// Initialize start screen
createStartScreen();
A bomb. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Boss ship top down. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Enemy ship. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Spaceship for players. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
make it greyish
Kinda a spike ball very red. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Diamond, . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat