User prompt
enemies start shooting after 500 points
User prompt
show powerup countdown on lower right
User prompt
powerup lasts 10 seconds
User prompt
add background
User prompt
my firerate is reduced little bit
User prompt
enemies at begining dont shoot.start shooting slowly after one minute and firerate increases
Code edit (1 edits merged)
Please save this source code
User prompt
Galactic Defender
Initial prompt
a space shooter game
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // 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 ship = self.attachAsset('enemyShip', { anchorX: 0.5, anchorY: 0.5 }); self.width = ship.width; self.height = ship.height; self.speed = 4 + Math.random() * 2; self.shootCooldown = 60 + Math.floor(Math.random() * 60); self.shootTimer = Math.floor(Math.random() * self.shootCooldown); self.hp = 1; self.update = function () { self.y += self.speed; if (self.shootTimer > 0) { self.shootTimer--; } }; 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 Ship var PlayerShip = Container.expand(function () { var self = Container.call(this); var ship = self.attachAsset('playerShip', { anchorX: 0.5, anchorY: 0.5 }); self.width = ship.width; self.height = ship.height; self.canShoot = true; self.shootCooldown = 24; // frames between shots (reduced fire rate) self.shootTimer = 0; // self.lives = 1; // For MVP, 1 hit = game over self.poweredUp = false; self.powerupTimer = 0; // Power-up effect self.setPowerup = function (duration) { self.poweredUp = true; self.powerupTimer = duration; ship.tint = 0x00ff99; }; self.clearPowerup = function () { self.poweredUp = false; self.powerupTimer = 0; ship.tint = 0xffffff; }; self.update = function () { if (self.shootTimer > 0) self.shootTimer--; if (self.poweredUp) { self.powerupTimer--; if (self.powerupTimer <= 0) { self.clearPowerup(); } } }; return self; }); // Powerup var Powerup = Container.expand(function () { var self = Container.call(this); var p = self.attachAsset('powerup', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.update = function () { self.y += self.speed; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000010 }); /**** * Game Code ****/ // Add starfield background image var background = LK.getAsset('starfield', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, scaleX: 2048 / 1024, // assuming starfield asset is 1024x1366, scale to fit scaleY: 2732 / 1366 }); game.addChild(background); // Tap-to-start overlay var tapToStartTxt = new Text2('Tap to Start', { size: 180, fill: 0xffffff, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); tapToStartTxt.anchor.set(0.5, 0.5); tapToStartTxt.x = 2048 / 2; tapToStartTxt.y = 2732 / 2; LK.gui.center.addChild(tapToStartTxt); var gameStarted = false; var savedUpdate = null; // Pause game logic until tap savedUpdate = game.update; game.update = function () { if (!gameStarted) return; savedUpdate(); }; // Pause music until start var musicStarted = false; // Block all input except tap to start var origMove = game.move; var origDown = game.down; var origUp = game.up; game.move = function () { if (gameStarted && origMove) origMove.apply(this, arguments); }; game.down = function (x, y, obj) { if (!gameStarted) { gameStarted = true; LK.gui.center.removeChild(tapToStartTxt); if (!musicStarted) { LK.playMusic('galacticMusic'); musicStarted = true; } // Restore input handlers if (origDown) origDown.apply(this, arguments); return; } if (origDown) origDown.apply(this, arguments); }; game.up = function () { if (gameStarted && origUp) origUp.apply(this, arguments); }; // Score display var scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Highscore display var highscore = storage.highscore || 0; var highscoreTxt = new Text2("HI " + highscore, { size: 80, fill: 0xFFD700 }); highscoreTxt.anchor.set(1, 0); LK.gui.topRight.addChild(highscoreTxt); // Powerup countdown display (lower right) var powerupCountdownTxt = new Text2('', { size: 80, fill: 0x00ff99 }); powerupCountdownTxt.anchor.set(1, 1); // bottom right LK.gui.bottomRight.addChild(powerupCountdownTxt); // Lives display (top left, but offset to avoid menu) var livesTxt = new Text2('♥♥♥', { size: 100, fill: 0xff4444 }); livesTxt.anchor.set(0, 0); livesTxt.x = 110; // leave 100px for menu livesTxt.y = 0; LK.gui.top.addChild(livesTxt); // Game variables var player; var playerBullets = []; var enemies = []; var enemyBullets = []; var powerups = []; var dragNode = null; var lastPlayerHit = false; var spawnTimer = 0; var powerupSpawnTimer = 0; // Enemy shooting control var enemyShootingEnabled = false; var enemyShootInterval = 120; // initial interval (frames between shots, high so they don't shoot at first) var lastEnemyShootInterval = 120; // Helper: Clamp value function clamp(val, min, max) { return Math.max(min, Math.min(max, val)); } // Initialize player player = new PlayerShip(); player.lives = 3; player.x = 2048 / 2; player.y = 2732 - 220; game.addChild(player); // Move handler (drag player ship) function handleMove(x, y, obj) { if (dragNode === player) { // Clamp player within screen bounds var halfW = player.width / 2; var halfH = player.height / 2; player.x = clamp(x, halfW, 2048 - halfW); player.y = clamp(y, 2048, 2732 - halfH); // Only allow movement in lower 700px } } game.move = handleMove; game.down = function (x, y, obj) { // Only start drag if touch is on player var local = player.toLocal(game.toGlobal({ x: x, y: y })); if (local.x >= -player.width / 2 && local.x <= player.width / 2 && local.y >= -player.height / 2 && local.y <= player.height / 2) { dragNode = player; handleMove(x, y, obj); } }; game.up = function (x, y, obj) { dragNode = null; }; // Fire player bullet (auto-fire, no mouse button needed) function firePlayerBullet() { if (player.shootTimer === 0) { var bullet = new PlayerBullet(); bullet.x = player.x; bullet.y = player.y - player.height / 2 - 10; playerBullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); player.shootTimer = player.poweredUp ? 6 : player.shootCooldown; // Power-up: triple shot if (player.poweredUp) { for (var i = -1; i <= 1; i += 2) { var b = new PlayerBullet(); b.x = player.x + i * 50; b.y = player.y - player.height / 2 - 10; b.rotation = i * 0.18; b.update = function (angle) { return function () { this.y += this.speed * Math.cos(angle); this.x += this.speed * Math.sin(angle); }; }(i * 0.18); playerBullets.push(b); game.addChild(b); } } } } // Spawn enemy function spawnEnemy() { var enemy = new EnemyShip(); enemy.x = 180 + Math.random() * (2048 - 360); enemy.y = -enemy.height / 2 - 10; // Set initial shootCooldown based on current global interval enemy.shootCooldown = enemyShootingEnabled ? enemyShootInterval + Math.floor(Math.random() * 30) : 120 + Math.floor(Math.random() * 60); enemy.shootTimer = Math.floor(Math.random() * enemy.shootCooldown); enemies.push(enemy); game.addChild(enemy); } // Spawn powerup function spawnPowerup() { var p = new Powerup(); p.x = 180 + Math.random() * (2048 - 360); p.y = -p.height / 2 - 10; powerups.push(p); game.addChild(p); } // Main update loop game.update = function () { // Player update player.update(); // Update lives display var livesStr = ''; for (var i = 0; i < player.lives; i++) livesStr += '♥'; livesTxt.setText(livesStr); // Update powerup countdown text if (player.poweredUp && player.powerupTimer > 0) { // Show seconds left, rounded up var seconds = Math.ceil(player.powerupTimer / 60); powerupCountdownTxt.setText(seconds + "s"); } else { powerupCountdownTxt.setText(''); } // Player auto-fire if (LK.ticks % 2 === 0) { firePlayerBullet(); } // Update player bullets for (var i = playerBullets.length - 1; i >= 0; i--) { var b = playerBullets[i]; b.update(); // Remove if off screen 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)) { e.hp--; if (e.hp <= 0) { // Explosion effect LK.effects.flashObject(e, 0xffffff, 200); // Add explosion sprite at enemy position var explosion = LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5, x: e.x, y: e.y }); game.addChild(explosion); // Animate explosion fade out and destroy after 400ms tween(explosion, { alpha: 0 }, { duration: 400, onFinish: function onFinish() { explosion.destroy(); } }); LK.getSound('explosion').play(); LK.setScore(LK.getScore() + 10); scoreTxt.setText(LK.getScore()); // Highscore logic if (LK.getScore() > highscore) { highscore = LK.getScore(); storage.highscore = highscore; highscoreTxt.setText("HI " + highscore); } e.destroy(); enemies.splice(j, 1); } b.destroy(); playerBullets.splice(i, 1); break; } } } // Update enemies var playerWasHitThisFrame = false; // Track if player was hit this frame for (var i = enemies.length - 1; i >= 0; i--) { var e = enemies[i]; e.update(); // Remove if off screen if (e.y > 2732 + e.height) { e.destroy(); enemies.splice(i, 1); continue; } // Enemy shooting logic // Enable enemy shooting after player reaches 200 points if (!enemyShootingEnabled && LK.getScore() >= 200) { enemyShootingEnabled = true; } // Gradually increase fire rate after enemy shooting is enabled if (enemyShootingEnabled) { // Decrease interval every 10 seconds, down to a minimum var minInterval = 24; // fastest fire rate (lower = faster) var maxInterval = 120; // slowest fire rate (at start) // Use ticks since enabling shooting to ramp up fire rate if (typeof enemyShootingStartTick === "undefined") { enemyShootingStartTick = LK.ticks; } var elapsed = LK.ticks - enemyShootingStartTick; var interval = Math.max(minInterval, maxInterval - Math.floor(elapsed / 600) * 8); enemyShootInterval = interval; // If interval changed, update all enemies' shootCooldown if (enemyShootInterval !== lastEnemyShootInterval) { for (var j = 0; j < enemies.length; j++) { enemies[j].shootCooldown = enemyShootInterval + Math.floor(Math.random() * 30); } lastEnemyShootInterval = enemyShootInterval; } } // Enemy shooting if (enemyShootingEnabled && e.shootTimer === 0) { var eb = new EnemyBullet(); eb.x = e.x; eb.y = e.y + e.height / 2 + 10; enemyBullets.push(eb); game.addChild(eb); LK.getSound('enemyShoot').play(); e.shootTimer = e.shootCooldown; } // Check collision with player if (e.intersects(player)) { if (!e.lastHitPlayer) { if (!lastPlayerHit && !playerWasHitThisFrame) { LK.effects.flashScreen(0xff0000, 800); LK.getSound('explosion').play(); player.lives--; playerWasHitThisFrame = true; if (player.lives <= 0) { player.lives = 0; livesTxt.setText(''); LK.showGameOver(); } else { // Reset player position and give brief invulnerability player.x = 2048 / 2; player.y = 2732 - 220; } lastPlayerHit = true; } e.lastHitPlayer = true; } } else { e.lastHitPlayer = false; } } // Update enemy bullets for (var i = enemyBullets.length - 1; i >= 0; i--) { var eb = enemyBullets[i]; eb.update(); if (eb.y > 2732 + 60) { eb.destroy(); enemyBullets.splice(i, 1); continue; } // Check collision with player if (eb.intersects(player)) { if (!eb.lastHitPlayer) { if (!lastPlayerHit && !playerWasHitThisFrame) { LK.effects.flashScreen(0xff0000, 800); LK.getSound('explosion').play(); player.lives--; playerWasHitThisFrame = true; if (player.lives <= 0) { player.lives = 0; livesTxt.setText(''); LK.showGameOver(); } else { // Reset player position and give brief invulnerability player.x = 2048 / 2; player.y = 2732 - 220; } lastPlayerHit = true; } eb.lastHitPlayer = true; } } else { eb.lastHitPlayer = false; } } // Update powerups for (var i = powerups.length - 1; i >= 0; i--) { var p = powerups[i]; p.update(); if (p.y > 2732 + 60) { p.destroy(); powerups.splice(i, 1); continue; } // Check collision with player if (p.intersects(player)) { player.setPowerup(600); // 10 seconds at 60fps LK.getSound('powerup').play(); p.destroy(); powerups.splice(i, 1); } } // Enemy spawn logic spawnTimer--; if (spawnTimer <= 0) { spawnEnemy(); // After 600 points, spawn an extra enemy each spawn cycle if (LK.getScore() >= 600) { spawnEnemy(); } spawnTimer = 36 + Math.floor(Math.random() * 24); // spawn every 1s approx } // Powerup spawn logic powerupSpawnTimer--; if (powerupSpawnTimer <= 0) { if (Math.random() < 0.18) { spawnPowerup(); } powerupSpawnTimer = 360 + Math.floor(Math.random() * 240); // every 6-10s } // Reset lastPlayerHit if player is alive if (!player.destroyed) { lastPlayerHit = false; } }; // Reset game state on game over LK.on('gameover', function () { // Clean up all objects for (var i = 0; i < playerBullets.length; i++) playerBullets[i].destroy(); for (var i = 0; i < enemies.length; i++) enemies[i].destroy(); for (var i = 0; i < enemyBullets.length; i++) enemyBullets[i].destroy(); for (var i = 0; i < powerups.length; i++) powerups[i].destroy(); playerBullets = []; enemies = []; enemyBullets = []; powerups = []; lastPlayerHit = false; // Reset player lives if (player) player.lives = 3; // Update lives display var livesStr = ''; for (var i = 0; i < 3; i++) livesStr += '♥'; livesTxt.setText(livesStr); // Music will be handled by LK // Update highscore display in case storage was externally changed highscore = storage.highscore || 0; highscoreTxt.setText("HI " + highscore); }); // Reset game state on win (not used in MVP, but for future) LK.on('youwin', function () { for (var i = 0; i < playerBullets.length; i++) playerBullets[i].destroy(); for (var i = 0; i < enemies.length; i++) enemies[i].destroy(); for (var i = 0; i < enemyBullets.length; i++) enemyBullets[i].destroy(); for (var i = 0; i < powerups.length; i++) powerups[i].destroy(); playerBullets = []; enemies = []; enemyBullets = []; powerups = []; lastPlayerHit = false; // Reset player lives if (player) player.lives = 3; // Update lives display var livesStr = ''; for (var i = 0; i < 3; i++) livesStr += '♥'; livesTxt.setText(livesStr); // Update highscore display in case storage was externally changed highscore = storage.highscore || 0; highscoreTxt.setText("HI " + highscore); });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// 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 ship = self.attachAsset('enemyShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = ship.width;
self.height = ship.height;
self.speed = 4 + Math.random() * 2;
self.shootCooldown = 60 + Math.floor(Math.random() * 60);
self.shootTimer = Math.floor(Math.random() * self.shootCooldown);
self.hp = 1;
self.update = function () {
self.y += self.speed;
if (self.shootTimer > 0) {
self.shootTimer--;
}
};
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 Ship
var PlayerShip = Container.expand(function () {
var self = Container.call(this);
var ship = self.attachAsset('playerShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = ship.width;
self.height = ship.height;
self.canShoot = true;
self.shootCooldown = 24; // frames between shots (reduced fire rate)
self.shootTimer = 0;
// self.lives = 1; // For MVP, 1 hit = game over
self.poweredUp = false;
self.powerupTimer = 0;
// Power-up effect
self.setPowerup = function (duration) {
self.poweredUp = true;
self.powerupTimer = duration;
ship.tint = 0x00ff99;
};
self.clearPowerup = function () {
self.poweredUp = false;
self.powerupTimer = 0;
ship.tint = 0xffffff;
};
self.update = function () {
if (self.shootTimer > 0) self.shootTimer--;
if (self.poweredUp) {
self.powerupTimer--;
if (self.powerupTimer <= 0) {
self.clearPowerup();
}
}
};
return self;
});
// Powerup
var Powerup = Container.expand(function () {
var self = Container.call(this);
var p = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000010
});
/****
* Game Code
****/
// Add starfield background image
var background = LK.getAsset('starfield', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 2048 / 1024,
// assuming starfield asset is 1024x1366, scale to fit
scaleY: 2732 / 1366
});
game.addChild(background);
// Tap-to-start overlay
var tapToStartTxt = new Text2('Tap to Start', {
size: 180,
fill: 0xffffff,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
tapToStartTxt.anchor.set(0.5, 0.5);
tapToStartTxt.x = 2048 / 2;
tapToStartTxt.y = 2732 / 2;
LK.gui.center.addChild(tapToStartTxt);
var gameStarted = false;
var savedUpdate = null;
// Pause game logic until tap
savedUpdate = game.update;
game.update = function () {
if (!gameStarted) return;
savedUpdate();
};
// Pause music until start
var musicStarted = false;
// Block all input except tap to start
var origMove = game.move;
var origDown = game.down;
var origUp = game.up;
game.move = function () {
if (gameStarted && origMove) origMove.apply(this, arguments);
};
game.down = function (x, y, obj) {
if (!gameStarted) {
gameStarted = true;
LK.gui.center.removeChild(tapToStartTxt);
if (!musicStarted) {
LK.playMusic('galacticMusic');
musicStarted = true;
}
// Restore input handlers
if (origDown) origDown.apply(this, arguments);
return;
}
if (origDown) origDown.apply(this, arguments);
};
game.up = function () {
if (gameStarted && origUp) origUp.apply(this, arguments);
};
// Score display
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Highscore display
var highscore = storage.highscore || 0;
var highscoreTxt = new Text2("HI " + highscore, {
size: 80,
fill: 0xFFD700
});
highscoreTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(highscoreTxt);
// Powerup countdown display (lower right)
var powerupCountdownTxt = new Text2('', {
size: 80,
fill: 0x00ff99
});
powerupCountdownTxt.anchor.set(1, 1); // bottom right
LK.gui.bottomRight.addChild(powerupCountdownTxt);
// Lives display (top left, but offset to avoid menu)
var livesTxt = new Text2('♥♥♥', {
size: 100,
fill: 0xff4444
});
livesTxt.anchor.set(0, 0);
livesTxt.x = 110; // leave 100px for menu
livesTxt.y = 0;
LK.gui.top.addChild(livesTxt);
// Game variables
var player;
var playerBullets = [];
var enemies = [];
var enemyBullets = [];
var powerups = [];
var dragNode = null;
var lastPlayerHit = false;
var spawnTimer = 0;
var powerupSpawnTimer = 0;
// Enemy shooting control
var enemyShootingEnabled = false;
var enemyShootInterval = 120; // initial interval (frames between shots, high so they don't shoot at first)
var lastEnemyShootInterval = 120;
// Helper: Clamp value
function clamp(val, min, max) {
return Math.max(min, Math.min(max, val));
}
// Initialize player
player = new PlayerShip();
player.lives = 3;
player.x = 2048 / 2;
player.y = 2732 - 220;
game.addChild(player);
// Move handler (drag player ship)
function handleMove(x, y, obj) {
if (dragNode === player) {
// Clamp player within screen bounds
var halfW = player.width / 2;
var halfH = player.height / 2;
player.x = clamp(x, halfW, 2048 - halfW);
player.y = clamp(y, 2048, 2732 - halfH); // Only allow movement in lower 700px
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only start drag if touch is on player
var local = player.toLocal(game.toGlobal({
x: x,
y: y
}));
if (local.x >= -player.width / 2 && local.x <= player.width / 2 && local.y >= -player.height / 2 && local.y <= player.height / 2) {
dragNode = player;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Fire player bullet (auto-fire, no mouse button needed)
function firePlayerBullet() {
if (player.shootTimer === 0) {
var bullet = new PlayerBullet();
bullet.x = player.x;
bullet.y = player.y - player.height / 2 - 10;
playerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
player.shootTimer = player.poweredUp ? 6 : player.shootCooldown;
// Power-up: triple shot
if (player.poweredUp) {
for (var i = -1; i <= 1; i += 2) {
var b = new PlayerBullet();
b.x = player.x + i * 50;
b.y = player.y - player.height / 2 - 10;
b.rotation = i * 0.18;
b.update = function (angle) {
return function () {
this.y += this.speed * Math.cos(angle);
this.x += this.speed * Math.sin(angle);
};
}(i * 0.18);
playerBullets.push(b);
game.addChild(b);
}
}
}
}
// Spawn enemy
function spawnEnemy() {
var enemy = new EnemyShip();
enemy.x = 180 + Math.random() * (2048 - 360);
enemy.y = -enemy.height / 2 - 10;
// Set initial shootCooldown based on current global interval
enemy.shootCooldown = enemyShootingEnabled ? enemyShootInterval + Math.floor(Math.random() * 30) : 120 + Math.floor(Math.random() * 60);
enemy.shootTimer = Math.floor(Math.random() * enemy.shootCooldown);
enemies.push(enemy);
game.addChild(enemy);
}
// Spawn powerup
function spawnPowerup() {
var p = new Powerup();
p.x = 180 + Math.random() * (2048 - 360);
p.y = -p.height / 2 - 10;
powerups.push(p);
game.addChild(p);
}
// Main update loop
game.update = function () {
// Player update
player.update();
// Update lives display
var livesStr = '';
for (var i = 0; i < player.lives; i++) livesStr += '♥';
livesTxt.setText(livesStr);
// Update powerup countdown text
if (player.poweredUp && player.powerupTimer > 0) {
// Show seconds left, rounded up
var seconds = Math.ceil(player.powerupTimer / 60);
powerupCountdownTxt.setText(seconds + "s");
} else {
powerupCountdownTxt.setText('');
}
// Player auto-fire
if (LK.ticks % 2 === 0) {
firePlayerBullet();
}
// Update player bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
var b = playerBullets[i];
b.update();
// Remove if off screen
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)) {
e.hp--;
if (e.hp <= 0) {
// Explosion effect
LK.effects.flashObject(e, 0xffffff, 200);
// Add explosion sprite at enemy position
var explosion = LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
x: e.x,
y: e.y
});
game.addChild(explosion);
// Animate explosion fade out and destroy after 400ms
tween(explosion, {
alpha: 0
}, {
duration: 400,
onFinish: function onFinish() {
explosion.destroy();
}
});
LK.getSound('explosion').play();
LK.setScore(LK.getScore() + 10);
scoreTxt.setText(LK.getScore());
// Highscore logic
if (LK.getScore() > highscore) {
highscore = LK.getScore();
storage.highscore = highscore;
highscoreTxt.setText("HI " + highscore);
}
e.destroy();
enemies.splice(j, 1);
}
b.destroy();
playerBullets.splice(i, 1);
break;
}
}
}
// Update enemies
var playerWasHitThisFrame = false; // Track if player was hit this frame
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
e.update();
// Remove if off screen
if (e.y > 2732 + e.height) {
e.destroy();
enemies.splice(i, 1);
continue;
}
// Enemy shooting logic
// Enable enemy shooting after player reaches 200 points
if (!enemyShootingEnabled && LK.getScore() >= 200) {
enemyShootingEnabled = true;
}
// Gradually increase fire rate after enemy shooting is enabled
if (enemyShootingEnabled) {
// Decrease interval every 10 seconds, down to a minimum
var minInterval = 24; // fastest fire rate (lower = faster)
var maxInterval = 120; // slowest fire rate (at start)
// Use ticks since enabling shooting to ramp up fire rate
if (typeof enemyShootingStartTick === "undefined") {
enemyShootingStartTick = LK.ticks;
}
var elapsed = LK.ticks - enemyShootingStartTick;
var interval = Math.max(minInterval, maxInterval - Math.floor(elapsed / 600) * 8);
enemyShootInterval = interval;
// If interval changed, update all enemies' shootCooldown
if (enemyShootInterval !== lastEnemyShootInterval) {
for (var j = 0; j < enemies.length; j++) {
enemies[j].shootCooldown = enemyShootInterval + Math.floor(Math.random() * 30);
}
lastEnemyShootInterval = enemyShootInterval;
}
}
// Enemy shooting
if (enemyShootingEnabled && e.shootTimer === 0) {
var eb = new EnemyBullet();
eb.x = e.x;
eb.y = e.y + e.height / 2 + 10;
enemyBullets.push(eb);
game.addChild(eb);
LK.getSound('enemyShoot').play();
e.shootTimer = e.shootCooldown;
}
// Check collision with player
if (e.intersects(player)) {
if (!e.lastHitPlayer) {
if (!lastPlayerHit && !playerWasHitThisFrame) {
LK.effects.flashScreen(0xff0000, 800);
LK.getSound('explosion').play();
player.lives--;
playerWasHitThisFrame = true;
if (player.lives <= 0) {
player.lives = 0;
livesTxt.setText('');
LK.showGameOver();
} else {
// Reset player position and give brief invulnerability
player.x = 2048 / 2;
player.y = 2732 - 220;
}
lastPlayerHit = true;
}
e.lastHitPlayer = true;
}
} else {
e.lastHitPlayer = false;
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var eb = enemyBullets[i];
eb.update();
if (eb.y > 2732 + 60) {
eb.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check collision with player
if (eb.intersects(player)) {
if (!eb.lastHitPlayer) {
if (!lastPlayerHit && !playerWasHitThisFrame) {
LK.effects.flashScreen(0xff0000, 800);
LK.getSound('explosion').play();
player.lives--;
playerWasHitThisFrame = true;
if (player.lives <= 0) {
player.lives = 0;
livesTxt.setText('');
LK.showGameOver();
} else {
// Reset player position and give brief invulnerability
player.x = 2048 / 2;
player.y = 2732 - 220;
}
lastPlayerHit = true;
}
eb.lastHitPlayer = true;
}
} else {
eb.lastHitPlayer = false;
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
var p = powerups[i];
p.update();
if (p.y > 2732 + 60) {
p.destroy();
powerups.splice(i, 1);
continue;
}
// Check collision with player
if (p.intersects(player)) {
player.setPowerup(600); // 10 seconds at 60fps
LK.getSound('powerup').play();
p.destroy();
powerups.splice(i, 1);
}
}
// Enemy spawn logic
spawnTimer--;
if (spawnTimer <= 0) {
spawnEnemy();
// After 600 points, spawn an extra enemy each spawn cycle
if (LK.getScore() >= 600) {
spawnEnemy();
}
spawnTimer = 36 + Math.floor(Math.random() * 24); // spawn every 1s approx
}
// Powerup spawn logic
powerupSpawnTimer--;
if (powerupSpawnTimer <= 0) {
if (Math.random() < 0.18) {
spawnPowerup();
}
powerupSpawnTimer = 360 + Math.floor(Math.random() * 240); // every 6-10s
}
// Reset lastPlayerHit if player is alive
if (!player.destroyed) {
lastPlayerHit = false;
}
};
// Reset game state on game over
LK.on('gameover', function () {
// Clean up all objects
for (var i = 0; i < playerBullets.length; i++) playerBullets[i].destroy();
for (var i = 0; i < enemies.length; i++) enemies[i].destroy();
for (var i = 0; i < enemyBullets.length; i++) enemyBullets[i].destroy();
for (var i = 0; i < powerups.length; i++) powerups[i].destroy();
playerBullets = [];
enemies = [];
enemyBullets = [];
powerups = [];
lastPlayerHit = false;
// Reset player lives
if (player) player.lives = 3;
// Update lives display
var livesStr = '';
for (var i = 0; i < 3; i++) livesStr += '♥';
livesTxt.setText(livesStr);
// Music will be handled by LK
// Update highscore display in case storage was externally changed
highscore = storage.highscore || 0;
highscoreTxt.setText("HI " + highscore);
});
// Reset game state on win (not used in MVP, but for future)
LK.on('youwin', function () {
for (var i = 0; i < playerBullets.length; i++) playerBullets[i].destroy();
for (var i = 0; i < enemies.length; i++) enemies[i].destroy();
for (var i = 0; i < enemyBullets.length; i++) enemyBullets[i].destroy();
for (var i = 0; i < powerups.length; i++) powerups[i].destroy();
playerBullets = [];
enemies = [];
enemyBullets = [];
powerups = [];
lastPlayerHit = false;
// Reset player lives
if (player) player.lives = 3;
// Update lives display
var livesStr = '';
for (var i = 0; i < 3; i++) livesStr += '♥';
livesTxt.setText(livesStr);
// Update highscore display in case storage was externally changed
highscore = storage.highscore || 0;
highscoreTxt.setText("HI " + highscore);
});
spaceship seen from above. In-Game asset. 2d. High contrast. No shadows
spaceship seen from up going down. In-Game asset. 2d. High contrast. No shadows
color blue
red laser beam. In-Game asset. 2d. High contrast. No shadows
color green
explosion. In-Game asset. 2d. High contrast. No shadows
starfield banner
make box yellow