/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Boss ship var BossShip = Container.expand(function () { var self = Container.call(this); var bossSprite = self.attachAsset('enemyShip', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.2, scaleY: 2.2 }); self.radius = bossSprite.width * 0.5; self.hp = 20; self.update = function () { // Boss movement: simple horizontal oscillation if (!self.dir) self.dir = 1; self.x += self.dir * 10; if (self.x > 2048 - self.radius - 40) self.dir = -1; if (self.x < self.radius + 40) self.dir = 1; // Boss shooting logic if (self.shootCooldown === undefined) self.shootCooldown = 86; self.shootCooldown--; if (self.shootCooldown <= 0) { // Fire 3-way spread for (var i = 0; i < 3; i++) { var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y + self.radius; // Add spread: -0.2, 0, +0.2 radians bullet.angle = -0.2 + 0.2 * i; bullet.update = function (origUpdate, angle) { return function () { this.x += Math.sin(angle) * 12; this.y += Math.cos(angle) * this.speed; }; }(bullet.update, bullet.angle); if (typeof enemyBullets !== "undefined") { enemyBullets.push(bullet); } if (typeof game !== "undefined") { game.addChild(bullet); } } self.shootCooldown = 86; } }; return self; }); // Enemy bullet var EnemyBullet = Container.expand(function () { var self = Container.call(this); var bullet = self.attachAsset('enemyBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 18; self.update = function () { self.y += self.speed; }; return self; }); // Enemy ship var EnemyShip = Container.expand(function () { var self = Container.call(this); var enemy = self.attachAsset('enemyShip', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 3 + Math.random() * 2; self.shootCooldown = 60 + Math.floor(Math.random() * 60); self.radius = enemy.width * 0.5; self.update = function () { self.y += self.speed; self.shootCooldown--; }; return self; }); // Player bullet var PlayerBullet = Container.expand(function () { var self = Container.call(this); var bullet = self.attachAsset('playerBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -32; self.update = function () { self.y += self.speed; }; return self; }); // Player spaceship var Spaceship = Container.expand(function () { var self = Container.call(this); var ship = self.attachAsset('spaceship', { anchorX: 0.5, anchorY: 0.5 }); self.radius = ship.width * 0.5; self.invincible = false; self.update = function () {}; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000010 }); /**** * Game Code ****/ // Game variables // Spaceship (player) // Player bullet // Enemy ship // Enemy bullet // Asteroid // Powerup // Add background image to the game scene function spawnBoss() { if (bossActive) return; boss = new BossShip(); boss.x = 2048 / 2; boss.y = 400; bossActive = true; game.addChild(boss); } var background = LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChild(background); var player; var playerBullets = []; var enemies = []; var enemyBullets = []; // Boss variables must be defined in global scope for all functions to access var boss = null; var bossActive = false; var dragNode = null; var lastPlayerX = 0, lastPlayerY = 0; var canShoot = true; var shootCooldown = 0; var score = 0; // Player lives var playerLives = 3; var livesTxt = new Text2('♥ x3', { size: 90, fill: 0xFF4444 }); livesTxt.anchor.set(0.5, 0); livesTxt.x = 2048 / 2; LK.gui.top.addChild(livesTxt); var scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); function spawnPlayer() { player = new Spaceship(); player.x = 2048 / 2; player.y = 2732 - 350; game.addChild(player); } function spawnEnemy() { var enemy = new EnemyShip(); enemy.x = 200 + Math.random() * (2048 - 400); enemy.y = -100; enemies.push(enemy); game.addChild(enemy); } function firePlayerBullet() { if (!player || player.invincible) return; if (player.tripleShot && player.tripleShot > 0) { // Fire 3-way spread for (var i = 0; i < 3; i++) { var bullet = new PlayerBullet(); bullet.x = player.x; bullet.y = player.y - player.radius; // Add spread: -0.2, 0, +0.2 radians bullet.angle = -0.2 + 0.2 * i; bullet.update = function (origUpdate, angle) { return function () { this.x += Math.sin(angle) * 16; this.y += Math.cos(angle) * this.speed; }; }(bullet.update, bullet.angle); playerBullets.push(bullet); game.addChild(bullet); } player.tripleShot--; if (player.tripleShot <= 0) { player.tripleShot = 0; } } else { var bullet = new PlayerBullet(); bullet.x = player.x; bullet.y = player.y - player.radius; playerBullets.push(bullet); game.addChild(bullet); } } function fireEnemyBullet(enemy) { var bullet = new EnemyBullet(); bullet.x = enemy.x; bullet.y = enemy.y + enemy.radius; enemyBullets.push(bullet); game.addChild(bullet); } function resetGame() { // Not needed, handled by LK.showGameOver } // Dragging and shooting game.down = function (x, y, obj) { if (!player) return; dragNode = player; lastPlayerX = x; lastPlayerY = y; }; game.up = function (x, y, obj) { dragNode = null; }; function handleMove(x, y, obj) { if (dragNode === player) { // Clamp to game area, avoid top 100px (menu) var minX = player.radius + 20; var maxX = 2048 - player.radius - 20; var minY = 120 + player.radius; var maxY = 2732 - player.radius - 20; player.x = Math.max(minX, Math.min(maxX, x)); player.y = Math.max(minY, Math.min(maxY, y)); } } game.move = handleMove; // Automatic player bullet firing handled in game.update (removed tap-to-shoot) // Main update loop game.update = function () { // Player automatic shooting if (!player.invincible) { if (!canShoot) { shootCooldown--; if (shootCooldown <= 0) { canShoot = true; } } if (canShoot) { firePlayerBullet(); canShoot = false; shootCooldown = 10; } } // Update player if (player) player.update(); // Update and remove player bullets for (var i = playerBullets.length - 1; i >= 0; i--) { var b = playerBullets[i]; b.update(); if (b.y < -80) { b.destroy(); playerBullets.splice(i, 1); continue; } // Check collision with enemies for (var j = enemies.length - 1; j >= 0; j--) { var e = enemies[j]; if (b.intersects(e)) { // Destroy both b.destroy(); playerBullets.splice(i, 1); e.destroy(); enemies.splice(j, 1); // 100% chance to drop 3-way shot powerup if (typeof spawnPowerup === "function") { spawnPowerup(e.x, e.y); } score += 1; scoreTxt.setText(score); // Boss spawn check: every 30 points if (score % 30 === 0 && score > 0) { if (typeof spawnBoss === "function") { spawnBoss(); } } break; } } } // Update and remove enemies for (var i = enemies.length - 1; i >= 0; i--) { var e = enemies[i]; e.update(); if (e.y > 2732 + 100) { e.destroy(); enemies.splice(i, 1); continue; } // Enemy shooting if (e.shootCooldown <= 0) { fireEnemyBullet(e); // Increase cooldown for less frequent enemy bullets e.shootCooldown = 180 + Math.floor(Math.random() * 90); } // Collision with player if (player && !player.invincible && e.intersects(player)) { LK.effects.flashScreen(0xff0000, 800); playerLives--; livesTxt.setText('♥ x' + playerLives); if (playerLives <= 0) { LK.showGameOver(); return; } else { // Respawn player player.destroy(); spawnPlayer(); return; } } } // Powerup collision with player (player must collect the powerup) if (typeof powerups !== "undefined") { for (var k = powerups.length - 1; k >= 0; k--) { var p = powerups[k]; if (player && p.intersects(player)) { // Grant triple shot for 10 shots player.tripleShot = 10; p.destroy(); powerups.splice(k, 1); } } } // Update and remove enemy bullets for (var i = enemyBullets.length - 1; i >= 0; i--) { var b = enemyBullets[i]; b.update(); if (b.y > 2732 + 80) { b.destroy(); enemyBullets.splice(i, 1); continue; } // Collision with player if (player && !player.invincible && b.intersects(player)) { LK.effects.flashScreen(0xff0000, 800); playerLives--; livesTxt.setText('♥ x' + playerLives); // score += 1; // Removed: No score for being hit by enemy bullet // scoreTxt.setText(score); b.destroy(); enemyBullets.splice(i, 1); if (playerLives <= 0) { LK.showGameOver(); return; } else { // Respawn player player.destroy(); spawnPlayer(); return; } } } // Boss logic if (bossActive && boss) { boss.update(); // Boss collision with player bullets for (var i = playerBullets.length - 1; i >= 0; i--) { var b = playerBullets[i]; if (b.intersects(boss)) { b.destroy(); playerBullets.splice(i, 1); boss.hp--; if (boss.hp <= 0) { boss.destroy(); boss = null; bossActive = false; score += 5; scoreTxt.setText(score); break; } } } // Boss collision with player if (player && !player.invincible && boss && boss.intersects(player)) { LK.effects.flashScreen(0xff0000, 1000); playerLives--; livesTxt.setText('♥ x' + playerLives); if (playerLives <= 0) { LK.showGameOver(); return; } else { player.destroy(); spawnPlayer(); return; } } } // Spawning logic if (LK.ticks % 60 === 0 && !bossActive) { spawnEnemy(); } }; // Start game spawnPlayer();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Boss ship
var BossShip = Container.expand(function () {
var self = Container.call(this);
var bossSprite = self.attachAsset('enemyShip', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.2,
scaleY: 2.2
});
self.radius = bossSprite.width * 0.5;
self.hp = 20;
self.update = function () {
// Boss movement: simple horizontal oscillation
if (!self.dir) self.dir = 1;
self.x += self.dir * 10;
if (self.x > 2048 - self.radius - 40) self.dir = -1;
if (self.x < self.radius + 40) self.dir = 1;
// Boss shooting logic
if (self.shootCooldown === undefined) self.shootCooldown = 86;
self.shootCooldown--;
if (self.shootCooldown <= 0) {
// Fire 3-way spread
for (var i = 0; i < 3; i++) {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + self.radius;
// Add spread: -0.2, 0, +0.2 radians
bullet.angle = -0.2 + 0.2 * i;
bullet.update = function (origUpdate, angle) {
return function () {
this.x += Math.sin(angle) * 12;
this.y += Math.cos(angle) * this.speed;
};
}(bullet.update, bullet.angle);
if (typeof enemyBullets !== "undefined") {
enemyBullets.push(bullet);
}
if (typeof game !== "undefined") {
game.addChild(bullet);
}
}
self.shootCooldown = 86;
}
};
return self;
});
// Enemy bullet
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bullet = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 18;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Enemy ship
var EnemyShip = Container.expand(function () {
var self = Container.call(this);
var enemy = self.attachAsset('enemyShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3 + Math.random() * 2;
self.shootCooldown = 60 + Math.floor(Math.random() * 60);
self.radius = enemy.width * 0.5;
self.update = function () {
self.y += self.speed;
self.shootCooldown--;
};
return self;
});
// Player bullet
var PlayerBullet = Container.expand(function () {
var self = Container.call(this);
var bullet = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -32;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player spaceship
var Spaceship = Container.expand(function () {
var self = Container.call(this);
var ship = self.attachAsset('spaceship', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = ship.width * 0.5;
self.invincible = false;
self.update = function () {};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000010
});
/****
* Game Code
****/
// Game variables
// Spaceship (player)
// Player bullet
// Enemy ship
// Enemy bullet
// Asteroid
// Powerup
// Add background image to the game scene
function spawnBoss() {
if (bossActive) return;
boss = new BossShip();
boss.x = 2048 / 2;
boss.y = 400;
bossActive = true;
game.addChild(boss);
}
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(background);
var player;
var playerBullets = [];
var enemies = [];
var enemyBullets = [];
// Boss variables must be defined in global scope for all functions to access
var boss = null;
var bossActive = false;
var dragNode = null;
var lastPlayerX = 0,
lastPlayerY = 0;
var canShoot = true;
var shootCooldown = 0;
var score = 0;
// Player lives
var playerLives = 3;
var livesTxt = new Text2('♥ x3', {
size: 90,
fill: 0xFF4444
});
livesTxt.anchor.set(0.5, 0);
livesTxt.x = 2048 / 2;
LK.gui.top.addChild(livesTxt);
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
function spawnPlayer() {
player = new Spaceship();
player.x = 2048 / 2;
player.y = 2732 - 350;
game.addChild(player);
}
function spawnEnemy() {
var enemy = new EnemyShip();
enemy.x = 200 + Math.random() * (2048 - 400);
enemy.y = -100;
enemies.push(enemy);
game.addChild(enemy);
}
function firePlayerBullet() {
if (!player || player.invincible) return;
if (player.tripleShot && player.tripleShot > 0) {
// Fire 3-way spread
for (var i = 0; i < 3; i++) {
var bullet = new PlayerBullet();
bullet.x = player.x;
bullet.y = player.y - player.radius;
// Add spread: -0.2, 0, +0.2 radians
bullet.angle = -0.2 + 0.2 * i;
bullet.update = function (origUpdate, angle) {
return function () {
this.x += Math.sin(angle) * 16;
this.y += Math.cos(angle) * this.speed;
};
}(bullet.update, bullet.angle);
playerBullets.push(bullet);
game.addChild(bullet);
}
player.tripleShot--;
if (player.tripleShot <= 0) {
player.tripleShot = 0;
}
} else {
var bullet = new PlayerBullet();
bullet.x = player.x;
bullet.y = player.y - player.radius;
playerBullets.push(bullet);
game.addChild(bullet);
}
}
function fireEnemyBullet(enemy) {
var bullet = new EnemyBullet();
bullet.x = enemy.x;
bullet.y = enemy.y + enemy.radius;
enemyBullets.push(bullet);
game.addChild(bullet);
}
function resetGame() {
// Not needed, handled by LK.showGameOver
}
// Dragging and shooting
game.down = function (x, y, obj) {
if (!player) return;
dragNode = player;
lastPlayerX = x;
lastPlayerY = y;
};
game.up = function (x, y, obj) {
dragNode = null;
};
function handleMove(x, y, obj) {
if (dragNode === player) {
// Clamp to game area, avoid top 100px (menu)
var minX = player.radius + 20;
var maxX = 2048 - player.radius - 20;
var minY = 120 + player.radius;
var maxY = 2732 - player.radius - 20;
player.x = Math.max(minX, Math.min(maxX, x));
player.y = Math.max(minY, Math.min(maxY, y));
}
}
game.move = handleMove;
// Automatic player bullet firing handled in game.update (removed tap-to-shoot)
// Main update loop
game.update = function () {
// Player automatic shooting
if (!player.invincible) {
if (!canShoot) {
shootCooldown--;
if (shootCooldown <= 0) {
canShoot = true;
}
}
if (canShoot) {
firePlayerBullet();
canShoot = false;
shootCooldown = 10;
}
}
// Update player
if (player) player.update();
// Update and remove player bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
var b = playerBullets[i];
b.update();
if (b.y < -80) {
b.destroy();
playerBullets.splice(i, 1);
continue;
}
// Check collision with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var e = enemies[j];
if (b.intersects(e)) {
// Destroy both
b.destroy();
playerBullets.splice(i, 1);
e.destroy();
enemies.splice(j, 1);
// 100% chance to drop 3-way shot powerup
if (typeof spawnPowerup === "function") {
spawnPowerup(e.x, e.y);
}
score += 1;
scoreTxt.setText(score);
// Boss spawn check: every 30 points
if (score % 30 === 0 && score > 0) {
if (typeof spawnBoss === "function") {
spawnBoss();
}
}
break;
}
}
}
// Update and remove enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
e.update();
if (e.y > 2732 + 100) {
e.destroy();
enemies.splice(i, 1);
continue;
}
// Enemy shooting
if (e.shootCooldown <= 0) {
fireEnemyBullet(e);
// Increase cooldown for less frequent enemy bullets
e.shootCooldown = 180 + Math.floor(Math.random() * 90);
}
// Collision with player
if (player && !player.invincible && e.intersects(player)) {
LK.effects.flashScreen(0xff0000, 800);
playerLives--;
livesTxt.setText('♥ x' + playerLives);
if (playerLives <= 0) {
LK.showGameOver();
return;
} else {
// Respawn player
player.destroy();
spawnPlayer();
return;
}
}
}
// Powerup collision with player (player must collect the powerup)
if (typeof powerups !== "undefined") {
for (var k = powerups.length - 1; k >= 0; k--) {
var p = powerups[k];
if (player && p.intersects(player)) {
// Grant triple shot for 10 shots
player.tripleShot = 10;
p.destroy();
powerups.splice(k, 1);
}
}
}
// Update and remove enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var b = enemyBullets[i];
b.update();
if (b.y > 2732 + 80) {
b.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Collision with player
if (player && !player.invincible && b.intersects(player)) {
LK.effects.flashScreen(0xff0000, 800);
playerLives--;
livesTxt.setText('♥ x' + playerLives);
// score += 1; // Removed: No score for being hit by enemy bullet
// scoreTxt.setText(score);
b.destroy();
enemyBullets.splice(i, 1);
if (playerLives <= 0) {
LK.showGameOver();
return;
} else {
// Respawn player
player.destroy();
spawnPlayer();
return;
}
}
}
// Boss logic
if (bossActive && boss) {
boss.update();
// Boss collision with player bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
var b = playerBullets[i];
if (b.intersects(boss)) {
b.destroy();
playerBullets.splice(i, 1);
boss.hp--;
if (boss.hp <= 0) {
boss.destroy();
boss = null;
bossActive = false;
score += 5;
scoreTxt.setText(score);
break;
}
}
}
// Boss collision with player
if (player && !player.invincible && boss && boss.intersects(player)) {
LK.effects.flashScreen(0xff0000, 1000);
playerLives--;
livesTxt.setText('♥ x' + playerLives);
if (playerLives <= 0) {
LK.showGameOver();
return;
} else {
player.destroy();
spawnPlayer();
return;
}
}
}
// Spawning logic
if (LK.ticks % 60 === 0 && !bossActive) {
spawnEnemy();
}
};
// Start game
spawnPlayer();