User prompt
When the bonus box comes to the player, the player and the laser will be red until the double shot is finished.
User prompt
When the bonus box comes to the player, the player will be red for 25 seconds
User prompt
When the bonus box comes to the player, the player will be red for 15 seconds
User prompt
When the bonus box comes to the player, the player will be red for 10 seconds
User prompt
Please fix the bug: 'ReferenceError: playerSprite is not defined' in or related to this line: 'if (playerSprite && playerSprite.tint !== undefined) {' Line Number: 566
User prompt
When the bonus box comes to the player, the player will be red for 6 seconds
User prompt
When the bonus box comes to the player, the player will be red for 5 seconds
User prompt
When the bonus reaches the player the laser will fire double shots for 5 seconds.
User prompt
When the bonus reaches the player, the 5-second laser will become 2x.
User prompt
When the bonus hits the player, the 5-second shot will be 2x
User prompt
Bonuses come to the player
User prompt
7. dalgaya koyabilecek bir uzaylı eklemek istiyorum
User prompt
Player's firing speed increases by 0.2x with each wave
User prompt
Player's firing speed is doubled in each wave
User prompt
Increases the wave speed in each wave by 2 times
User prompt
fix fire rate
User prompt
Increases the wave speed in each wave by 0.5 times
User prompt
Increases the wave speed in each wave by 0.3 times
User prompt
Increases the wave speed in each wave by 0.25 times
User prompt
Increases the wave speed in each wave by 0.2 times
User prompt
Increases the wave speed in each wave by 0.15 times
User prompt
Different aliens come in every 3 waves fix
User prompt
Different aliens come in every 3 waves
User prompt
fix wave score
User prompt
reorder wave score
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Alien class var Alien = Container.expand(function () { var self = Container.call(this); var alienSprite = self.attachAsset('alien', { anchorX: 0.5, anchorY: 0.5 }); self.width = alienSprite.width; self.height = alienSprite.height; self.speed = 2.5; self.hp = 1; self.wave = 1; self.lastX = self.x; self.lastY = self.y; self.lastIntersecting = false; self.isDead = false; self.update = function () { self.lastY = self.y; self.y += self.speed; }; return self; }); // Bonus box class var BonusBox = Container.expand(function () { var self = Container.call(this); var bonusSprite = self.attachAsset('bonus', { anchorX: 0.5, anchorY: 0.5 }); self.width = bonusSprite.width; self.height = bonusSprite.height; self.lastY = self.y; self.lastIntersecting = false; self.type = null; // Set on spawn return self; }); // Crystal class var Crystal = Container.expand(function () { var self = Container.call(this); var crystalSprite = self.attachAsset('crystal', { anchorX: 0.5, anchorY: 0.5 }); self.width = crystalSprite.width; self.height = crystalSprite.height; self.lastY = self.y; self.lastIntersecting = false; self.fallSpeed = 12; // Speed at which the crystal falls toward the player self.update = function () { // Move toward the player's current position if (typeof player !== "undefined" && player !== null) { var dx = player.x - self.x; var dy = player.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 2) { // Normalize and move toward player self.x += dx / dist * self.fallSpeed; self.y += dy / dist * self.fallSpeed; } } }; return self; }); // Laser class var Laser = Container.expand(function () { var self = Container.call(this); var laserSprite = self.attachAsset('laser', { anchorX: 0.5, anchorY: 1 }); self.width = laserSprite.width; self.height = laserSprite.height; self.speed = 32; self.pierce = false; self.lastY = self.y; self.lastIntersecting = false; self.update = function () { self.y -= self.speed; }; return self; }); // Player class var Player = Container.expand(function () { var self = Container.call(this); var playerSprite = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.width = playerSprite.width; self.height = playerSprite.height; self.shootCooldown = 0; self.moveSpeed = 32; // px per tick when dragged self.leftLimit = 0 + self.width / 2 + 40; self.rightLimit = 2048 - self.width / 2 - 40; self.powered = false; self.powerTimer = 0; self.powerType = null; // For powerup visuals self.setPower = function (type, duration) { self.powered = true; self.powerType = type; self.powerTimer = duration; if (type === 'rapid') { playerSprite.tint = 0xffe100; } else if (type === 'pierce') { playerSprite.tint = 0x00ffb0; } else if (type === 'shield') { playerSprite.tint = 0x00eaff; } }; self.clearPower = function () { self.powered = false; self.powerType = null; self.powerTimer = 0; playerSprite.tint = 0xffffff; }; self.update = function () { if (self.powered) { self.powerTimer--; if (self.powerTimer <= 0) { self.clearPower(); } } }; return self; }); // Star class for background stars var Star = Container.expand(function () { var self = Container.call(this); // Use a white ellipse as a star, randomize size var size = randInt(2, 6); var starSprite = self.attachAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: size / 100, scaleY: size / 100, tint: 0xffffff }); self.width = size; self.height = size; self.speed = randInt(1, 4) * 0.2; // slow speed for parallax self.alpha = Math.random() * 0.5 + 0.5; self.update = function () { self.y += self.speed; if (self.y > GAME_HEIGHT + self.height) { self.y = -self.height; self.x = randInt(0, GAME_WIDTH); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Music // Sound effects // Bonus box // Crystal (collectible) // Player bullet (laser) // Alien (enemy) // Player // Game constants var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; // --- Starfield background --- var NUM_STARS = 80; var stars = []; for (var i = 0; i < NUM_STARS; i++) { var star = new Star(); star.x = randInt(0, GAME_WIDTH); star.y = randInt(0, GAME_HEIGHT); stars.push(star); game.addChild(star); } 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 Y spawn range for enemies (top margin to bottom margin for bonus box spawn) var ENEMY_START_Y_MIN = 0; var ENEMY_START_Y_MAX = GAME_HEIGHT - 400; // 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; // Track current firing cooldown, increases as waves progress var currentLaserCooldown = LASER_COOLDOWN; var currentLaserCooldownRapid = LASER_COOLDOWN_RAPID; 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.10; 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 aliensKilledThisWave = 0; // Track aliens killed in current wave var crystalsCollected = 0; var leftLimit = PLAYER_MIN_X; var rightLimit = PLAYER_MAX_X; var bonusActive = false; var bonusType = null; // Removed score and scoreTxt, replaced by crystalTxt var waveTxt = null; var powerupTxt = null; var gameOver = false; var youWin = false; // GUI // Crystal icon in upper left (avoid top left 100x100 for menu) // Crystal icon and count, aligned and sized together var crystalIcon = LK.getAsset('crystal', { anchorX: 0, anchorY: 0.5 }); crystalIcon.x = 20; crystalIcon.y = 160 + 40; // center of 80px icon at 160+40=200 crystalIcon.width = 100; crystalIcon.height = 100; LK.gui.topLeft.addChild(crystalIcon); // Crystal count text, vertically centered with icon, same height var crystalTxt = new Text2('0', { size: 100, fill: 0x00eaff }); crystalTxt.anchor.set(0, 0.5); crystalTxt.x = crystalIcon.x + crystalIcon.width + 24; crystalTxt.y = crystalIcon.y; LK.gui.topLeft.addChild(crystalTxt); 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); // Set enemiesToSpawn to 30 for each wave, so wave increases every 30 aliens enemiesToSpawn = 30; enemiesLeft = enemiesToSpawn; lastAlienSpawnTick = LK.ticks; aliensKilledThisWave = 0; // Reset for new wave // Every 2 waves, increase firing speed (decrease cooldown by 2, min 2) if (wave > 1 && wave % 2 === 1) { currentLaserCooldown = Math.max(2, currentLaserCooldown - 2); currentLaserCooldownRapid = Math.max(2, currentLaserCooldownRapid - 2); } } 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 () { // Update background stars for (var i = 0; i < stars.length; i++) { stars[i].update(); } 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++; aliensKilledThisWave++; // New wave? if (enemiesLeft <= 0) { // All aliens for this wave have been killed, start next wave startWave(wave + 1); // Bonus box every BONUS_WAVE_INTERVAL after wave increment 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); } return; // Prevent double wave start } } 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 and always update score crystalsCollected++; crystalTxt.setText(crystalsCollected); if (crystalsCollected <= MAX_CRYSTALS) { leftLimit = Math.max(PLAYER_MIN_X - crystalsCollected * CRYSTAL_MOVE_STEP, 80 + player.width / 2); rightLimit = Math.min(PLAYER_MAX_X + crystalsCollected * CRYSTAL_MOVE_STEP, GAME_WIDTH - 80 - player.width / 2); } } } // 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' ? currentLaserCooldownRapid : currentLaserCooldown; 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 alienSprite = self.attachAsset('alien', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = alienSprite.width;
self.height = alienSprite.height;
self.speed = 2.5;
self.hp = 1;
self.wave = 1;
self.lastX = self.x;
self.lastY = self.y;
self.lastIntersecting = false;
self.isDead = false;
self.update = function () {
self.lastY = self.y;
self.y += self.speed;
};
return self;
});
// Bonus box class
var BonusBox = Container.expand(function () {
var self = Container.call(this);
var bonusSprite = self.attachAsset('bonus', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = bonusSprite.width;
self.height = bonusSprite.height;
self.lastY = self.y;
self.lastIntersecting = false;
self.type = null; // Set on spawn
return self;
});
// Crystal class
var Crystal = Container.expand(function () {
var self = Container.call(this);
var crystalSprite = self.attachAsset('crystal', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = crystalSprite.width;
self.height = crystalSprite.height;
self.lastY = self.y;
self.lastIntersecting = false;
self.fallSpeed = 12; // Speed at which the crystal falls toward the player
self.update = function () {
// Move toward the player's current position
if (typeof player !== "undefined" && player !== null) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 2) {
// Normalize and move toward player
self.x += dx / dist * self.fallSpeed;
self.y += dy / dist * self.fallSpeed;
}
}
};
return self;
});
// Laser class
var Laser = Container.expand(function () {
var self = Container.call(this);
var laserSprite = self.attachAsset('laser', {
anchorX: 0.5,
anchorY: 1
});
self.width = laserSprite.width;
self.height = laserSprite.height;
self.speed = 32;
self.pierce = false;
self.lastY = self.y;
self.lastIntersecting = false;
self.update = function () {
self.y -= self.speed;
};
return self;
});
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
var playerSprite = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = playerSprite.width;
self.height = playerSprite.height;
self.shootCooldown = 0;
self.moveSpeed = 32; // px per tick when dragged
self.leftLimit = 0 + self.width / 2 + 40;
self.rightLimit = 2048 - self.width / 2 - 40;
self.powered = false;
self.powerTimer = 0;
self.powerType = null;
// For powerup visuals
self.setPower = function (type, duration) {
self.powered = true;
self.powerType = type;
self.powerTimer = duration;
if (type === 'rapid') {
playerSprite.tint = 0xffe100;
} else if (type === 'pierce') {
playerSprite.tint = 0x00ffb0;
} else if (type === 'shield') {
playerSprite.tint = 0x00eaff;
}
};
self.clearPower = function () {
self.powered = false;
self.powerType = null;
self.powerTimer = 0;
playerSprite.tint = 0xffffff;
};
self.update = function () {
if (self.powered) {
self.powerTimer--;
if (self.powerTimer <= 0) {
self.clearPower();
}
}
};
return self;
});
// Star class for background stars
var Star = Container.expand(function () {
var self = Container.call(this);
// Use a white ellipse as a star, randomize size
var size = randInt(2, 6);
var starSprite = self.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: size / 100,
scaleY: size / 100,
tint: 0xffffff
});
self.width = size;
self.height = size;
self.speed = randInt(1, 4) * 0.2; // slow speed for parallax
self.alpha = Math.random() * 0.5 + 0.5;
self.update = function () {
self.y += self.speed;
if (self.y > GAME_HEIGHT + self.height) {
self.y = -self.height;
self.x = randInt(0, GAME_WIDTH);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Music
// Sound effects
// Bonus box
// Crystal (collectible)
// Player bullet (laser)
// Alien (enemy)
// Player
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
// --- Starfield background ---
var NUM_STARS = 80;
var stars = [];
for (var i = 0; i < NUM_STARS; i++) {
var star = new Star();
star.x = randInt(0, GAME_WIDTH);
star.y = randInt(0, GAME_HEIGHT);
stars.push(star);
game.addChild(star);
}
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 Y spawn range for enemies (top margin to bottom margin for bonus box spawn)
var ENEMY_START_Y_MIN = 0;
var ENEMY_START_Y_MAX = GAME_HEIGHT - 400;
// 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;
// Track current firing cooldown, increases as waves progress
var currentLaserCooldown = LASER_COOLDOWN;
var currentLaserCooldownRapid = LASER_COOLDOWN_RAPID;
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.10;
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 aliensKilledThisWave = 0; // Track aliens killed in current wave
var crystalsCollected = 0;
var leftLimit = PLAYER_MIN_X;
var rightLimit = PLAYER_MAX_X;
var bonusActive = false;
var bonusType = null;
// Removed score and scoreTxt, replaced by crystalTxt
var waveTxt = null;
var powerupTxt = null;
var gameOver = false;
var youWin = false;
// GUI
// Crystal icon in upper left (avoid top left 100x100 for menu)
// Crystal icon and count, aligned and sized together
var crystalIcon = LK.getAsset('crystal', {
anchorX: 0,
anchorY: 0.5
});
crystalIcon.x = 20;
crystalIcon.y = 160 + 40; // center of 80px icon at 160+40=200
crystalIcon.width = 100;
crystalIcon.height = 100;
LK.gui.topLeft.addChild(crystalIcon);
// Crystal count text, vertically centered with icon, same height
var crystalTxt = new Text2('0', {
size: 100,
fill: 0x00eaff
});
crystalTxt.anchor.set(0, 0.5);
crystalTxt.x = crystalIcon.x + crystalIcon.width + 24;
crystalTxt.y = crystalIcon.y;
LK.gui.topLeft.addChild(crystalTxt);
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);
// Set enemiesToSpawn to 30 for each wave, so wave increases every 30 aliens
enemiesToSpawn = 30;
enemiesLeft = enemiesToSpawn;
lastAlienSpawnTick = LK.ticks;
aliensKilledThisWave = 0; // Reset for new wave
// Every 2 waves, increase firing speed (decrease cooldown by 2, min 2)
if (wave > 1 && wave % 2 === 1) {
currentLaserCooldown = Math.max(2, currentLaserCooldown - 2);
currentLaserCooldownRapid = Math.max(2, currentLaserCooldownRapid - 2);
}
}
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 () {
// Update background stars
for (var i = 0; i < stars.length; i++) {
stars[i].update();
}
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++;
aliensKilledThisWave++;
// New wave?
if (enemiesLeft <= 0) {
// All aliens for this wave have been killed, start next wave
startWave(wave + 1);
// Bonus box every BONUS_WAVE_INTERVAL after wave increment
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);
}
return; // Prevent double wave start
}
}
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 and always update score
crystalsCollected++;
crystalTxt.setText(crystalsCollected);
if (crystalsCollected <= MAX_CRYSTALS) {
leftLimit = Math.max(PLAYER_MIN_X - crystalsCollected * CRYSTAL_MOVE_STEP, 80 + player.width / 2);
rightLimit = Math.min(PLAYER_MAX_X + crystalsCollected * CRYSTAL_MOVE_STEP, GAME_WIDTH - 80 - player.width / 2);
}
}
}
// 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' ? currentLaserCooldownRapid : currentLaserCooldown;
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