User prompt
When the player collects 10 crystals, a wave will be added and a blank will come in each wave
User prompt
Slightly decrease the player's shooting speed
User prompt
halve player's firing speed
User prompt
move the crystal icon to the top left
User prompt
Have the crystal icon in the upper left corner and count the crystals
User prompt
Let's keep the crystal and just remove the score
User prompt
let's remove the score
User prompt
Let's take the score above to the left and when we reach the crystal character, the score will be taken and there will be a crystal icon next to it.
User prompt
Keep the player's firing speed constant at normal speed
User prompt
When the player first starts, set the firing speed to 0.10
User prompt
Let the player's shooting speed be 0.15
User prompt
The player's firing speed should be 0.25 and increase by 0.1 with each crystal
User prompt
Let the player's shooting speed be 0.5
User prompt
Let there be a wave in every 20 aliens
User prompt
Have the player fire slowly when starting
User prompt
Have the player fire slowly when they start and gradually speed up as they collect crystals
User prompt
If the player is touched by an alien, he dies.
User prompt
Move on to the next wave when 10 aliens are killed
User prompt
Every 10 aliens die, move on to the next wave
User prompt
Every 20 aliens killed, move on to the next wave
User prompt
Every 20 enemies killed, move on to the next wave
User prompt
There should be a wave every 20 enemies and an empty wave after each wave.
User prompt
Make sure that the crystals that fall from enemies come to the character
User prompt
Let the bonuses dropped from the enemies come to the character
User prompt
Please fix the bug: 'ReferenceError: Boss is not defined' in or related to this line: 'var boss = new Boss();' Line Number: 219
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Alien class var Alien = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('alien', { anchorX: 0.5, anchorY: 0.5 }); self.width = sprite.width; self.height = sprite.height; self.speed = 2.5; self.hp = 1; self.isDead = false; self.wave = 1; self.update = function () { // Move downwards if (!self.isDead) { if (typeof self.lastY === "undefined") self.lastY = self.y; self.y += self.speed; self.lastY = self.y; } }; return self; }); // Boss class var Boss = Container.expand(function () { var self = Container.call(this); // Use the alien asset for the boss, but scale up and tint for distinction var sprite = self.attachAsset('alien', { anchorX: 0.5, anchorY: 0.5 }); sprite.scaleX = 2.2; sprite.scaleY = 2.2; sprite.tint = 0xff4444; self.width = sprite.width * sprite.scaleX; self.height = sprite.height * sprite.scaleY; self.speed = 2.2; self.hp = 20; self.isDead = false; self.wave = 1; self.update = function () { if (!self.isDead) { if (typeof self.lastY === "undefined") self.lastY = self.y; self.y += self.speed; self.lastY = self.y; } }; return self; }); // Crystal class var Crystal = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('crystal', { anchorX: 0.5, anchorY: 0.5 }); self.width = sprite.width; self.height = sprite.height; // Crystals are static, but you could add animation or effects here if needed return self; }); // Laser class var Laser = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('laser', { anchorX: 0.5, anchorY: 0.5 }); self.width = sprite.width; self.height = sprite.height; self.speed = -28; self.pierce = false; self.update = function () { if (typeof self.lastY === "undefined") self.lastY = self.y; self.y += self.speed; self.lastY = self.y; }; return self; }); // Player class var Player = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.width = sprite.width; self.height = sprite.height; self.leftLimit = PLAYER_MIN_X; self.rightLimit = PLAYER_MAX_X; self.shootCooldown = 0; self.powered = false; self.powerType = null; self.powerTimer = 0; self.update = function () { // Powerup timer if (self.powered) { self.powerTimer--; if (self.powerTimer <= 0) { self.powered = false; self.powerType = null; } } }; self.setPower = function (type, duration) { self.powered = true; self.powerType = type; self.powerTimer = duration; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x0a0a1a }); /**** * Game Code ****/ // Game constants // Player // Alien (enemy) // Player bullet (laser) // Crystal (collectible) // Bonus box // Sound effects // Music var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var GROUND_Y = GAME_HEIGHT - 220; var PLAYER_START_X = GAME_WIDTH / 2; var PLAYER_START_Y = GROUND_Y; var PLAYER_MIN_X = 160 + 40; var PLAYER_MAX_X = GAME_WIDTH - 160 - 40; var ENEMY_START_X_MIN = 160; var ENEMY_START_X_MAX = GAME_WIDTH - 160; // Define 3 fixed road X positions for aliens to spawn and move along (top to bottom) var ROAD_X_POSITIONS = [ENEMY_START_X_MIN + Math.floor((ENEMY_START_X_MAX - ENEMY_START_X_MIN) * 0.2), ENEMY_START_X_MIN + Math.floor((ENEMY_START_X_MAX - ENEMY_START_X_MIN) * 0.5), ENEMY_START_X_MIN + Math.floor((ENEMY_START_X_MAX - ENEMY_START_X_MIN) * 0.8)]; var LASER_COOLDOWN = 18; // ticks var LASER_COOLDOWN_RAPID = 6; var CRYSTAL_COLLECT_DIST = 120; var BONUS_COLLECT_DIST = 120; var CRYSTAL_MOVE_STEP = 180; // How much movement area expands per crystal var MAX_CRYSTALS = 8; var WAVE_ENEMY_BASE = 10; var WAVE_ENEMY_INC = 5; // Increase by 5 each wave var WAVE_SPEED_INC = 0.4; var WAVE_HP_INC = 1; var BONUS_WAVE_INTERVAL = 3; var POWERUP_DURATION = 360; // 6 seconds at 60fps // Game state var player = null; var aliens = []; var lasers = []; var crystals = []; var bonuses = []; var dragNode = null; var dragOffsetX = 0; var dragOffsetY = 0; var lastMoveX = 0; var lastMoveY = 0; var lastAlienSpawnTick = 0; var wave = 1; var enemiesLeft = 0; var enemiesToSpawn = 0; var enemiesKilled = 0; var crystalsCollected = 0; var leftLimit = PLAYER_MIN_X; var rightLimit = PLAYER_MAX_X; var bonusActive = false; var bonusType = null; var score = 0; var scoreTxt = null; var waveTxt = null; var powerupTxt = null; var gameOver = false; var youWin = false; // GUI scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); waveTxt = new Text2('Wave 1', { size: 80, fill: 0xAAFFFF }); waveTxt.anchor.set(0.5, 0); LK.gui.top.addChild(waveTxt); waveTxt.y = 120; powerupTxt = new Text2('', { size: 70, fill: 0xFFE100 }); powerupTxt.anchor.set(0.5, 0); LK.gui.top.addChild(powerupTxt); powerupTxt.y = 220; // Start music LK.playMusic('bgmusic'); // Spawn player player = new Player(); player.x = PLAYER_START_X; player.y = PLAYER_START_Y; game.addChild(player); // Set initial movement limits leftLimit = player.leftLimit; rightLimit = player.rightLimit; // Start first wave function startWave(waveNum) { wave = waveNum; waveTxt.setText('Wave ' + wave); // Boss logic: spawn boss before normal enemies, except for first wave if (wave > 1) { // Only one boss at a time var boss = new Boss(); // Pick a random road for the boss to spawn on (X position) var roadIdx = randInt(0, ROAD_X_POSITIONS.length - 1); boss.x = ROAD_X_POSITIONS[roadIdx]; boss.y = -boss.height / 2; boss.wave = wave; boss.speed = 2.2 + (wave - 2) * 0.25; boss.hp = 12 + (wave - 2) * 6; aliens.push(boss); game.addChild(boss); enemiesToSpawn = 0; // Don't spawn normal enemies until boss is defeated enemiesLeft = 1; lastAlienSpawnTick = LK.ticks; return; } enemiesToSpawn = WAVE_ENEMY_BASE + (wave - 1) * WAVE_ENEMY_INC; enemiesLeft = enemiesToSpawn; lastAlienSpawnTick = LK.ticks; } startWave(1); // Utility: clamp function clamp(val, min, max) { if (val < min) return min; if (val > max) return max; return val; } // Utility: random int function randInt(min, max) { return min + Math.floor(Math.random() * (max - min + 1)); } // Utility: random powerup function randomPowerup() { var arr = ['rapid', 'pierce', 'shield']; return arr[randInt(0, arr.length - 1)]; } // Handle movement (dragging) function handleMove(x, y, obj) { if (dragNode === player) { // Clamp to current movement limits var newX = clamp(x, leftLimit, rightLimit); player.x = newX; lastMoveX = newX; lastMoveY = player.y; } } game.move = handleMove; game.down = function (x, y, obj) { // Only allow drag if touch/click is on player or below if (y > GROUND_Y - 200) { dragNode = player; handleMove(x, y, obj); } }; game.up = function (x, y, obj) { dragNode = null; }; // Main game update game.update = function () { if (gameOver || youWin) return; // Player update (powerup timer) player.update(); // Aliens update for (var i = aliens.length - 1; i >= 0; i--) { var alien = aliens[i]; alien.update(); // Check if alien reached bottom edge (player dies) if (alien.y > GAME_HEIGHT + alien.height / 2) { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); gameOver = true; return; } // Check collision with player (if shielded, destroy alien) if (player.powered && player.powerType === 'shield' && alien.intersects(player)) { LK.getSound('alien_die').play(); LK.effects.flashObject(alien, 0x00eaff, 400); alien.isDead = true; aliens.splice(i, 1); alien.destroy(); continue; } } // Lasers update for (var i = lasers.length - 1; i >= 0; i--) { var laser = lasers[i]; laser.update(); // Remove if off screen if (laser.y < -100) { lasers.splice(i, 1); laser.destroy(); continue; } // Check collision with aliens var hit = false; for (var j = aliens.length - 1; j >= 0; j--) { var alien = aliens[j]; if (!alien.isDead && laser.intersects(alien)) { alien.hp -= player.powered && player.powerType === 'pierce' ? 2 : 1; if (alien.hp <= 0) { // Alien dies LK.getSound('alien_die').play(); LK.effects.flashObject(alien, 0xff0000, 400); alien.isDead = true; // Drop crystal var crystal = new Crystal(); crystal.x = alien.x; crystal.y = alien.y; crystals.push(crystal); game.addChild(crystal); // Remove alien aliens.splice(j, 1); alien.destroy(); enemiesLeft--; enemiesKilled++; score += 10; scoreTxt.setText(score); // New wave? if (enemiesLeft <= 0) { // If a boss was just defeated, start the normal enemies for this wave var bossPresent = false; for (var k = 0; k < aliens.length; k++) { if (aliens[k] instanceof Boss) { bossPresent = true; break; } } if (bossPresent) { // Wait until boss is gone } else if (enemiesToSpawn === 0 && wave > 1) { // Now spawn the normal enemies for this wave enemiesToSpawn = WAVE_ENEMY_BASE + (wave - 1) * WAVE_ENEMY_INC; enemiesLeft = enemiesToSpawn; lastAlienSpawnTick = LK.ticks; // Bonus box every BONUS_WAVE_INTERVAL if (wave % BONUS_WAVE_INTERVAL === 0) { var bonus = new BonusBox(); bonus.x = randInt(leftLimit + 100, rightLimit - 100); bonus.y = randInt(ENEMY_START_Y_MIN + 100, ENEMY_START_Y_MAX - 100); bonus.type = randomPowerup(); bonuses.push(bonus); game.addChild(bonus); } } else { // End of normal enemies, start next wave (boss will spawn in startWave) if (wave % BONUS_WAVE_INTERVAL === 0) { var bonus = new BonusBox(); bonus.x = randInt(leftLimit + 100, rightLimit - 100); bonus.y = randInt(ENEMY_START_Y_MIN + 100, ENEMY_START_Y_MAX - 100); bonus.type = randomPowerup(); bonuses.push(bonus); game.addChild(bonus); } startWave(wave + 1); } } } hit = true; if (!(player.powered && player.powerType === 'pierce')) break; } } if (hit && !(player.powered && player.powerType === 'pierce')) { lasers.splice(i, 1); laser.destroy(); } } // Crystals update for (var i = crystals.length - 1; i >= 0; i--) { var crystal = crystals[i]; // If player close enough, collect var dx = player.x - crystal.x; var dy = player.y - crystal.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < CRYSTAL_COLLECT_DIST) { LK.getSound('crystal_get').play(); LK.effects.flashObject(crystal, 0x00eaff, 400); crystals.splice(i, 1); crystal.destroy(); // Expand movement area if (crystalsCollected < MAX_CRYSTALS) { leftLimit = Math.max(PLAYER_MIN_X - (crystalsCollected + 1) * CRYSTAL_MOVE_STEP, 80 + player.width / 2); rightLimit = Math.min(PLAYER_MAX_X + (crystalsCollected + 1) * CRYSTAL_MOVE_STEP, GAME_WIDTH - 80 - player.width / 2); crystalsCollected++; } } } // Bonus boxes update for (var i = bonuses.length - 1; i >= 0; i--) { var bonus = bonuses[i]; var dx = player.x - bonus.x; var dy = player.y - bonus.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < BONUS_COLLECT_DIST) { LK.getSound('bonus_get').play(); LK.effects.flashObject(bonus, 0xffe100, 400); bonuses.splice(i, 1); bonus.destroy(); // Grant powerup player.setPower(bonus.type, POWERUP_DURATION); if (bonus.type === 'rapid') { powerupTxt.setText('Rapid Fire!'); } else if (bonus.type === 'pierce') { powerupTxt.setText('Piercing Shots!'); } else if (bonus.type === 'shield') { powerupTxt.setText('Shield!'); } } } // Powerup text timer if (player.powered) { powerupTxt.alpha = 1; } else { if (powerupTxt.alpha > 0) { powerupTxt.alpha -= 0.04; if (powerupTxt.alpha < 0) powerupTxt.alpha = 0; } powerupTxt.setText(''); } // Player auto-fire var cooldown = player.powered && player.powerType === 'rapid' ? LASER_COOLDOWN_RAPID : LASER_COOLDOWN; if (player.shootCooldown > 0) player.shootCooldown--; // Find nearest alien in range var nearestAlien = null; var minDist = 99999; for (var i = 0; i < aliens.length; i++) { var alien = aliens[i]; var dx = alien.x - player.x; var dy = alien.y - player.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dy > 0) continue; // Only shoot aliens above the player if (dist < minDist) { minDist = dist; nearestAlien = alien; } } if (nearestAlien && player.shootCooldown <= 0) { // Fire laser var laser = new Laser(); laser.x = player.x; laser.y = player.y - player.height / 2 + 10; if (player.powered && player.powerType === 'pierce') { laser.pierce = true; } lasers.push(laser); game.addChild(laser); player.shootCooldown = cooldown; LK.getSound('laser_shoot').play(); } // Spawn aliens for current wave if (enemiesToSpawn > 0 && LK.ticks - lastAlienSpawnTick > 24) { var alien = new Alien(); // Pick a random road for the alien to spawn on (X position) var roadIdx = randInt(0, ROAD_X_POSITIONS.length - 1); alien.x = ROAD_X_POSITIONS[roadIdx]; alien.y = -alien.height / 2; alien.wave = wave; alien.speed = 2.5 + (wave - 1) * WAVE_SPEED_INC; alien.hp = 1 + Math.floor((wave - 1) * WAVE_HP_INC / 2); aliens.push(alien); game.addChild(alien); enemiesToSpawn--; lastAlienSpawnTick = LK.ticks; } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Alien class
var Alien = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('alien', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = sprite.width;
self.height = sprite.height;
self.speed = 2.5;
self.hp = 1;
self.isDead = false;
self.wave = 1;
self.update = function () {
// Move downwards
if (!self.isDead) {
if (typeof self.lastY === "undefined") self.lastY = self.y;
self.y += self.speed;
self.lastY = self.y;
}
};
return self;
});
// Boss class
var Boss = Container.expand(function () {
var self = Container.call(this);
// Use the alien asset for the boss, but scale up and tint for distinction
var sprite = self.attachAsset('alien', {
anchorX: 0.5,
anchorY: 0.5
});
sprite.scaleX = 2.2;
sprite.scaleY = 2.2;
sprite.tint = 0xff4444;
self.width = sprite.width * sprite.scaleX;
self.height = sprite.height * sprite.scaleY;
self.speed = 2.2;
self.hp = 20;
self.isDead = false;
self.wave = 1;
self.update = function () {
if (!self.isDead) {
if (typeof self.lastY === "undefined") self.lastY = self.y;
self.y += self.speed;
self.lastY = self.y;
}
};
return self;
});
// Crystal class
var Crystal = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('crystal', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = sprite.width;
self.height = sprite.height;
// Crystals are static, but you could add animation or effects here if needed
return self;
});
// Laser class
var Laser = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('laser', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = sprite.width;
self.height = sprite.height;
self.speed = -28;
self.pierce = false;
self.update = function () {
if (typeof self.lastY === "undefined") self.lastY = self.y;
self.y += self.speed;
self.lastY = self.y;
};
return self;
});
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = sprite.width;
self.height = sprite.height;
self.leftLimit = PLAYER_MIN_X;
self.rightLimit = PLAYER_MAX_X;
self.shootCooldown = 0;
self.powered = false;
self.powerType = null;
self.powerTimer = 0;
self.update = function () {
// Powerup timer
if (self.powered) {
self.powerTimer--;
if (self.powerTimer <= 0) {
self.powered = false;
self.powerType = null;
}
}
};
self.setPower = function (type, duration) {
self.powered = true;
self.powerType = type;
self.powerTimer = duration;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x0a0a1a
});
/****
* Game Code
****/
// Game constants
// Player
// Alien (enemy)
// Player bullet (laser)
// Crystal (collectible)
// Bonus box
// Sound effects
// Music
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var GROUND_Y = GAME_HEIGHT - 220;
var PLAYER_START_X = GAME_WIDTH / 2;
var PLAYER_START_Y = GROUND_Y;
var PLAYER_MIN_X = 160 + 40;
var PLAYER_MAX_X = GAME_WIDTH - 160 - 40;
var ENEMY_START_X_MIN = 160;
var ENEMY_START_X_MAX = GAME_WIDTH - 160;
// Define 3 fixed road X positions for aliens to spawn and move along (top to bottom)
var ROAD_X_POSITIONS = [ENEMY_START_X_MIN + Math.floor((ENEMY_START_X_MAX - ENEMY_START_X_MIN) * 0.2), ENEMY_START_X_MIN + Math.floor((ENEMY_START_X_MAX - ENEMY_START_X_MIN) * 0.5), ENEMY_START_X_MIN + Math.floor((ENEMY_START_X_MAX - ENEMY_START_X_MIN) * 0.8)];
var LASER_COOLDOWN = 18; // ticks
var LASER_COOLDOWN_RAPID = 6;
var CRYSTAL_COLLECT_DIST = 120;
var BONUS_COLLECT_DIST = 120;
var CRYSTAL_MOVE_STEP = 180; // How much movement area expands per crystal
var MAX_CRYSTALS = 8;
var WAVE_ENEMY_BASE = 10;
var WAVE_ENEMY_INC = 5; // Increase by 5 each wave
var WAVE_SPEED_INC = 0.4;
var WAVE_HP_INC = 1;
var BONUS_WAVE_INTERVAL = 3;
var POWERUP_DURATION = 360; // 6 seconds at 60fps
// Game state
var player = null;
var aliens = [];
var lasers = [];
var crystals = [];
var bonuses = [];
var dragNode = null;
var dragOffsetX = 0;
var dragOffsetY = 0;
var lastMoveX = 0;
var lastMoveY = 0;
var lastAlienSpawnTick = 0;
var wave = 1;
var enemiesLeft = 0;
var enemiesToSpawn = 0;
var enemiesKilled = 0;
var crystalsCollected = 0;
var leftLimit = PLAYER_MIN_X;
var rightLimit = PLAYER_MAX_X;
var bonusActive = false;
var bonusType = null;
var score = 0;
var scoreTxt = null;
var waveTxt = null;
var powerupTxt = null;
var gameOver = false;
var youWin = false;
// GUI
scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
waveTxt = new Text2('Wave 1', {
size: 80,
fill: 0xAAFFFF
});
waveTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(waveTxt);
waveTxt.y = 120;
powerupTxt = new Text2('', {
size: 70,
fill: 0xFFE100
});
powerupTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(powerupTxt);
powerupTxt.y = 220;
// Start music
LK.playMusic('bgmusic');
// Spawn player
player = new Player();
player.x = PLAYER_START_X;
player.y = PLAYER_START_Y;
game.addChild(player);
// Set initial movement limits
leftLimit = player.leftLimit;
rightLimit = player.rightLimit;
// Start first wave
function startWave(waveNum) {
wave = waveNum;
waveTxt.setText('Wave ' + wave);
// Boss logic: spawn boss before normal enemies, except for first wave
if (wave > 1) {
// Only one boss at a time
var boss = new Boss();
// Pick a random road for the boss to spawn on (X position)
var roadIdx = randInt(0, ROAD_X_POSITIONS.length - 1);
boss.x = ROAD_X_POSITIONS[roadIdx];
boss.y = -boss.height / 2;
boss.wave = wave;
boss.speed = 2.2 + (wave - 2) * 0.25;
boss.hp = 12 + (wave - 2) * 6;
aliens.push(boss);
game.addChild(boss);
enemiesToSpawn = 0; // Don't spawn normal enemies until boss is defeated
enemiesLeft = 1;
lastAlienSpawnTick = LK.ticks;
return;
}
enemiesToSpawn = WAVE_ENEMY_BASE + (wave - 1) * WAVE_ENEMY_INC;
enemiesLeft = enemiesToSpawn;
lastAlienSpawnTick = LK.ticks;
}
startWave(1);
// Utility: clamp
function clamp(val, min, max) {
if (val < min) return min;
if (val > max) return max;
return val;
}
// Utility: random int
function randInt(min, max) {
return min + Math.floor(Math.random() * (max - min + 1));
}
// Utility: random powerup
function randomPowerup() {
var arr = ['rapid', 'pierce', 'shield'];
return arr[randInt(0, arr.length - 1)];
}
// Handle movement (dragging)
function handleMove(x, y, obj) {
if (dragNode === player) {
// Clamp to current movement limits
var newX = clamp(x, leftLimit, rightLimit);
player.x = newX;
lastMoveX = newX;
lastMoveY = player.y;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only allow drag if touch/click is on player or below
if (y > GROUND_Y - 200) {
dragNode = player;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Main game update
game.update = function () {
if (gameOver || youWin) return;
// Player update (powerup timer)
player.update();
// Aliens update
for (var i = aliens.length - 1; i >= 0; i--) {
var alien = aliens[i];
alien.update();
// Check if alien reached bottom edge (player dies)
if (alien.y > GAME_HEIGHT + alien.height / 2) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
gameOver = true;
return;
}
// Check collision with player (if shielded, destroy alien)
if (player.powered && player.powerType === 'shield' && alien.intersects(player)) {
LK.getSound('alien_die').play();
LK.effects.flashObject(alien, 0x00eaff, 400);
alien.isDead = true;
aliens.splice(i, 1);
alien.destroy();
continue;
}
}
// Lasers update
for (var i = lasers.length - 1; i >= 0; i--) {
var laser = lasers[i];
laser.update();
// Remove if off screen
if (laser.y < -100) {
lasers.splice(i, 1);
laser.destroy();
continue;
}
// Check collision with aliens
var hit = false;
for (var j = aliens.length - 1; j >= 0; j--) {
var alien = aliens[j];
if (!alien.isDead && laser.intersects(alien)) {
alien.hp -= player.powered && player.powerType === 'pierce' ? 2 : 1;
if (alien.hp <= 0) {
// Alien dies
LK.getSound('alien_die').play();
LK.effects.flashObject(alien, 0xff0000, 400);
alien.isDead = true;
// Drop crystal
var crystal = new Crystal();
crystal.x = alien.x;
crystal.y = alien.y;
crystals.push(crystal);
game.addChild(crystal);
// Remove alien
aliens.splice(j, 1);
alien.destroy();
enemiesLeft--;
enemiesKilled++;
score += 10;
scoreTxt.setText(score);
// New wave?
if (enemiesLeft <= 0) {
// If a boss was just defeated, start the normal enemies for this wave
var bossPresent = false;
for (var k = 0; k < aliens.length; k++) {
if (aliens[k] instanceof Boss) {
bossPresent = true;
break;
}
}
if (bossPresent) {
// Wait until boss is gone
} else if (enemiesToSpawn === 0 && wave > 1) {
// Now spawn the normal enemies for this wave
enemiesToSpawn = WAVE_ENEMY_BASE + (wave - 1) * WAVE_ENEMY_INC;
enemiesLeft = enemiesToSpawn;
lastAlienSpawnTick = LK.ticks;
// Bonus box every BONUS_WAVE_INTERVAL
if (wave % BONUS_WAVE_INTERVAL === 0) {
var bonus = new BonusBox();
bonus.x = randInt(leftLimit + 100, rightLimit - 100);
bonus.y = randInt(ENEMY_START_Y_MIN + 100, ENEMY_START_Y_MAX - 100);
bonus.type = randomPowerup();
bonuses.push(bonus);
game.addChild(bonus);
}
} else {
// End of normal enemies, start next wave (boss will spawn in startWave)
if (wave % BONUS_WAVE_INTERVAL === 0) {
var bonus = new BonusBox();
bonus.x = randInt(leftLimit + 100, rightLimit - 100);
bonus.y = randInt(ENEMY_START_Y_MIN + 100, ENEMY_START_Y_MAX - 100);
bonus.type = randomPowerup();
bonuses.push(bonus);
game.addChild(bonus);
}
startWave(wave + 1);
}
}
}
hit = true;
if (!(player.powered && player.powerType === 'pierce')) break;
}
}
if (hit && !(player.powered && player.powerType === 'pierce')) {
lasers.splice(i, 1);
laser.destroy();
}
}
// Crystals update
for (var i = crystals.length - 1; i >= 0; i--) {
var crystal = crystals[i];
// If player close enough, collect
var dx = player.x - crystal.x;
var dy = player.y - crystal.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < CRYSTAL_COLLECT_DIST) {
LK.getSound('crystal_get').play();
LK.effects.flashObject(crystal, 0x00eaff, 400);
crystals.splice(i, 1);
crystal.destroy();
// Expand movement area
if (crystalsCollected < MAX_CRYSTALS) {
leftLimit = Math.max(PLAYER_MIN_X - (crystalsCollected + 1) * CRYSTAL_MOVE_STEP, 80 + player.width / 2);
rightLimit = Math.min(PLAYER_MAX_X + (crystalsCollected + 1) * CRYSTAL_MOVE_STEP, GAME_WIDTH - 80 - player.width / 2);
crystalsCollected++;
}
}
}
// Bonus boxes update
for (var i = bonuses.length - 1; i >= 0; i--) {
var bonus = bonuses[i];
var dx = player.x - bonus.x;
var dy = player.y - bonus.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < BONUS_COLLECT_DIST) {
LK.getSound('bonus_get').play();
LK.effects.flashObject(bonus, 0xffe100, 400);
bonuses.splice(i, 1);
bonus.destroy();
// Grant powerup
player.setPower(bonus.type, POWERUP_DURATION);
if (bonus.type === 'rapid') {
powerupTxt.setText('Rapid Fire!');
} else if (bonus.type === 'pierce') {
powerupTxt.setText('Piercing Shots!');
} else if (bonus.type === 'shield') {
powerupTxt.setText('Shield!');
}
}
}
// Powerup text timer
if (player.powered) {
powerupTxt.alpha = 1;
} else {
if (powerupTxt.alpha > 0) {
powerupTxt.alpha -= 0.04;
if (powerupTxt.alpha < 0) powerupTxt.alpha = 0;
}
powerupTxt.setText('');
}
// Player auto-fire
var cooldown = player.powered && player.powerType === 'rapid' ? LASER_COOLDOWN_RAPID : LASER_COOLDOWN;
if (player.shootCooldown > 0) player.shootCooldown--;
// Find nearest alien in range
var nearestAlien = null;
var minDist = 99999;
for (var i = 0; i < aliens.length; i++) {
var alien = aliens[i];
var dx = alien.x - player.x;
var dy = alien.y - player.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dy > 0) continue; // Only shoot aliens above the player
if (dist < minDist) {
minDist = dist;
nearestAlien = alien;
}
}
if (nearestAlien && player.shootCooldown <= 0) {
// Fire laser
var laser = new Laser();
laser.x = player.x;
laser.y = player.y - player.height / 2 + 10;
if (player.powered && player.powerType === 'pierce') {
laser.pierce = true;
}
lasers.push(laser);
game.addChild(laser);
player.shootCooldown = cooldown;
LK.getSound('laser_shoot').play();
}
// Spawn aliens for current wave
if (enemiesToSpawn > 0 && LK.ticks - lastAlienSpawnTick > 24) {
var alien = new Alien();
// Pick a random road for the alien to spawn on (X position)
var roadIdx = randInt(0, ROAD_X_POSITIONS.length - 1);
alien.x = ROAD_X_POSITIONS[roadIdx];
alien.y = -alien.height / 2;
alien.wave = wave;
alien.speed = 2.5 + (wave - 1) * WAVE_SPEED_INC;
alien.hp = 1 + Math.floor((wave - 1) * WAVE_HP_INC / 2);
aliens.push(alien);
game.addChild(alien);
enemiesToSpawn--;
lastAlienSpawnTick = LK.ticks;
}
};
A triangular spaceship. In-Game asset. 2d. High contrast. No shadows
alien creature drawing. In-Game asset. 2d. High contrast. No shadows
alien creature drawing. In-Game asset. 2d. High contrast. No shadows
alien creature drawing. In-Game asset. 2d. High contrast. No shadows
alien creature drawing. In-Game asset. 2d. High contrast. No shadows
blue crystal. In-Game asset. 2d. High contrast. No shadows
draw thick long laser bullet. In-Game asset. 2d. High contrast. No shadows
remove bonus text
spaceship. In-Game asset. 2d. High contrast. No shadows