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