/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Asteroid = Container.expand(function () {
var self = Container.call(this);
var size = 60 + Math.floor(Math.random() * 60);
var asteroidGraphics = self.attachAsset('asteroid', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: size / 60,
scaleY: size / 60
});
self.health = 20 + Math.floor(size / 10);
self.speed = 1 + Math.random() * 3;
self.rotationSpeed = (Math.random() - 0.5) * 0.05;
self.points = 50;
self.update = function () {
self.y += self.speed;
asteroidGraphics.rotation += self.rotationSpeed;
// Check if asteroid is off-screen
if (self.y > 2832) {
return true; // Mark for removal
}
return false;
};
self.damage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xffffff, 100);
if (self.health <= 0) {
LK.getSound('explosion').play();
return true; // Asteroid destroyed
}
return false;
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = 10;
self.update = function () {
self.y += self.speed;
// Check if bullet is off-screen
if (self.y > 2832) {
return true; // Mark for removal
}
return false;
};
return self;
});
var EnemyShip = Container.expand(function () {
var self = Container.call(this);
var shipGraphics = self.attachAsset('enemyShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 30;
self.type = 'standard';
self.speed = 2 + Math.random() * 2;
self.points = 100;
self.fireRate = 120; // Frames between shots
self.fireCooldown = Math.floor(Math.random() * 60);
self.update = function () {
self.y += self.speed;
// Check if enemy is off-screen
if (self.y > 2832) {
return true; // Mark for removal
}
if (Math.random() < 0.02) {
self.direction = Math.random() > 0.5 ? 1 : -1;
}
if (self.direction) {
self.x += self.direction * (self.speed * 0.8);
// Keep within bounds
if (self.x < 100) {
self.x = 100;
self.direction = 1;
} else if (self.x > 2048 - 100) {
self.x = 2048 - 100;
self.direction = -1;
}
}
// Shooting
if (self.fireCooldown <= 0) {
self.fireCooldown = self.fireRate;
self.shoot();
} else {
self.fireCooldown--;
}
return false;
};
self.shoot = function () {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + 40;
enemyBullets.push(bullet);
game.addChild(bullet);
LK.getSound('enemyShoot').play();
};
self.damage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xffffff, 100);
if (self.health <= 0) {
LK.getSound('explosion').play();
return true; // Enemy destroyed
}
return false;
};
return self;
});
var HomeScreen = Container.expand(function () {
var self = Container.call(this);
// Title text
var titleText = new Text2('SPACE DEFENDER', {
size: 120,
fill: 0x33ccff
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 600;
self.addChild(titleText);
// Start button
var startButton = LK.getAsset('playerShip', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2,
tint: 0x33ff33
});
startButton.x = 2048 / 2;
startButton.y = 1200;
self.addChild(startButton);
// Start text
var startText = new Text2('START', {
size: 80,
fill: 0xFFFFFF
});
startText.anchor.set(0.5, 0.5);
startText.x = 2048 / 2;
startText.y = 1200;
self.addChild(startText);
// Shop button
var shopButton = LK.getAsset('powerUp', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3,
tint: 0xffcc33
});
shopButton.x = 2048 / 2;
shopButton.y = 1600;
self.addChild(shopButton);
// Shop text
var shopText = new Text2('SHOP', {
size: 80,
fill: 0xFFFFFF
});
shopText.anchor.set(0.5, 0.5);
shopText.x = 2048 / 2;
shopText.y = 1600;
self.addChild(shopText);
// Coins display
var coins = storage.coins || 0;
var coinsText = new Text2('COINS: ' + coins, {
size: 60,
fill: 0xffcc33
});
coinsText.anchor.set(0.5, 0);
coinsText.x = 2048 / 2;
coinsText.y = 1800;
self.addChild(coinsText);
// Event handlers
startButton.interactive = true;
startButton.down = function () {
game.gameState = 'playing';
self.destroy();
initGame();
};
shopButton.interactive = true;
shopButton.down = function () {
game.gameState = 'shop';
self.destroy();
showShop();
};
return self;
});
var PlayerBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 15;
self.power = 10;
self.update = function () {
self.y -= self.speed;
// Check if bullet is off-screen
if (self.y < -50) {
return true; // Mark for removal
}
return false;
};
return self;
});
var PlayerShip = Container.expand(function () {
var self = Container.call(this);
var shipGraphics = self.attachAsset('playerShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 200;
self.maxHealth = 200;
self.fireRate = 20; // Frames between shots
self.fireCooldown = 0;
self.bulletSpeed = 15;
self.speed = 8;
self.powerLevel = 1;
self.targetX = 0;
self.targetY = 0;
self.isMoving = false;
self.update = function () {
if (self.fireCooldown > 0) {
self.fireCooldown--;
}
// Move toward target position if moving
if (self.isMoving) {
// Calculate direction vector
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
// Calculate distance
var distance = Math.sqrt(dx * dx + dy * dy);
// If we're close enough, just set position directly
if (distance < self.speed) {
self.x = self.targetX;
self.y = self.targetY;
} else {
// Normalize direction vector and move
dx = dx / distance * self.speed;
dy = dy / distance * self.speed;
// Update position
self.x += dx;
self.y += dy;
}
// Keep player in bounds
self.x = Math.max(50, Math.min(self.x, 2048 - 50));
self.y = Math.max(50, Math.min(self.y, 2732 - 50));
}
};
self.damage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xff0000, 300);
// Add visual feedback
var damageText = new Text2('-' + amount, {
size: 40,
fill: 0xff3333
});
damageText.anchor.set(0.5, 0.5);
damageText.x = self.x;
damageText.y = self.y - 60;
game.addChild(damageText);
// Animate damage text
tween(damageText, {
y: damageText.y - 80,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
damageText.destroy();
}
});
if (self.health <= 0) {
self.health = 0;
return true; // Ship destroyed
}
return false;
};
self.shoot = function () {
if (self.fireCooldown <= 0) {
self.fireCooldown = self.fireRate;
LK.getSound('playerShoot').play();
// Create bullets based on power level
if (self.powerLevel === 1) {
// Single bullet
createBullet(self.x, self.y - 40);
} else if (self.powerLevel === 2) {
// Double bullets
createBullet(self.x - 20, self.y - 30);
createBullet(self.x + 20, self.y - 30);
} else {
// Triple bullets
createBullet(self.x, self.y - 40);
createBullet(self.x - 25, self.y - 30);
createBullet(self.x + 25, self.y - 30);
}
}
};
// Helper function to create a bullet
function createBullet(x, y) {
var bullet = new PlayerBullet();
bullet.x = x;
bullet.y = y;
playerBullets.push(bullet);
game.addChild(bullet);
}
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerUpGraphics = self.attachAsset('powerUp', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = Math.random() < 0.7 ? 'weapon' : 'health';
self.speed = 3;
// Change color based on type
if (self.type === 'health') {
powerUpGraphics.tint = 0x33ff33; // Green for health
} else {
powerUpGraphics.tint = 0xffcc33; // Yellow for weapon
}
self.update = function () {
self.y += self.speed;
// Slight bobbing motion
self.x += Math.sin(LK.ticks * 0.05) * 0.5;
// Check if power-up is off-screen
if (self.y > 2832) {
return true; // Mark for removal
}
return false;
};
return self;
});
var ShootButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('shootButton', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6
});
self.pressed = false;
self.down = function () {
self.pressed = true;
buttonGraphics.alpha = 0.9;
buttonGraphics.scale.set(0.9);
};
self.up = function () {
self.pressed = false;
buttonGraphics.alpha = 0.6;
buttonGraphics.scale.set(1);
};
return self;
});
var ShopScreen = Container.expand(function () {
var self = Container.call(this);
// Title
var titleText = new Text2('SHOP', {
size: 120,
fill: 0xffcc33
});
titleText.anchor.set(0.5, 0);
titleText.x = 2048 / 2;
titleText.y = 300;
self.addChild(titleText);
// Coins display
var coins = storage.coins || 0;
var coinsText = new Text2('COINS: ' + coins, {
size: 60,
fill: 0xffcc33
});
coinsText.anchor.set(0.5, 0);
coinsText.x = 2048 / 2;
coinsText.y = 450;
self.addChild(coinsText);
// Shop items
var items = [{
name: 'HEALTH+',
cost: 100,
effect: 'Increases max health by 20',
property: 'maxHealthBonus'
}, {
name: 'DAMAGE+',
cost: 150,
effect: 'Increases weapon damage by 25%',
property: 'damageBonus'
}, {
name: 'SPEED+',
cost: 120,
effect: 'Increases ship speed by 20%',
property: 'speedBonus'
}];
var itemY = 600;
for (var i = 0; i < items.length; i++) {
var item = items[i];
// Item container
var itemBg = LK.getAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 6,
scaleY: 2,
tint: 0x333366,
alpha: 0.7
});
itemBg.x = 2048 / 2;
itemBg.y = itemY;
self.addChild(itemBg);
// Item name
var nameText = new Text2(item.name, {
size: 60,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0.5);
nameText.x = 2048 / 2 - 400;
nameText.y = itemY - 40;
self.addChild(nameText);
// Item effect
var effectText = new Text2(item.effect, {
size: 40,
fill: 0xcccccc
});
effectText.anchor.set(0.5, 0.5);
effectText.x = 2048 / 2;
effectText.y = itemY + 40;
self.addChild(effectText);
// Cost
var costText = new Text2(item.cost + ' COINS', {
size: 50,
fill: 0xffcc33
});
costText.anchor.set(0.5, 0.5);
costText.x = 2048 / 2 + 350;
costText.y = itemY;
self.addChild(costText);
// Buy button
var buyButton = LK.getAsset('powerUp', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
tint: coins >= item.cost ? 0x33ff33 : 0x666666
});
buyButton.x = 2048 / 2 + 500;
buyButton.y = itemY;
buyButton.interactive = true;
buyButton.itemIndex = i;
buyButton.down = function () {
var selectedItem = items[this.itemIndex];
if ((storage.coins || 0) >= selectedItem.cost) {
// Purchase item
storage.coins = (storage.coins || 0) - selectedItem.cost;
// Increase the upgrade level
storage[selectedItem.property] = (storage[selectedItem.property] || 0) + 1;
// Show feedback and refresh shop
LK.effects.flashObject(this, 0x33ff33, 300);
self.destroy();
showShop();
} else {
// Not enough coins
LK.effects.flashObject(this, 0xff3333, 300);
}
};
self.addChild(buyButton);
// Level indicator
var level = storage[item.property] || 0;
var levelText = new Text2('LEVEL: ' + level, {
size: 40,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
levelText.x = 2048 / 2 - 400;
levelText.y = itemY + 40;
self.addChild(levelText);
itemY += 250;
}
// Back button
var backButton = LK.getAsset('playerShip', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
rotation: Math.PI,
tint: 0x3399ff
});
backButton.x = 2048 / 2;
backButton.y = 2300;
backButton.interactive = true;
backButton.down = function () {
self.destroy();
showHomeScreen();
};
self.addChild(backButton);
// Back text
var backText = new Text2('BACK', {
size: 60,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = 2048 / 2;
backText.y = 2300;
self.addChild(backText);
return self;
});
var VirtualJoystick = Container.expand(function () {
var self = Container.call(this);
var baseGraphics = self.attachAsset('joystick', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6
});
var knobGraphics = self.attachAsset('joystickKnob', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
self.maxDistance = 80;
self.active = false;
self.dx = 0;
self.dy = 0;
self.down = function (x, y) {
var localPos = self.toLocal({
x: x,
y: y
});
if (Math.sqrt(Math.pow(localPos.x, 2) + Math.pow(localPos.y, 2)) <= baseGraphics.width / 2) {
self.active = true;
self.moveKnob(localPos.x, localPos.y);
}
};
self.moveKnob = function (x, y) {
var dist = Math.sqrt(x * x + y * y);
if (dist > self.maxDistance) {
x = x / dist * self.maxDistance;
y = y / dist * self.maxDistance;
}
knobGraphics.x = x;
knobGraphics.y = y;
// Normalize to -1 to 1 range for movement
self.dx = x / self.maxDistance;
self.dy = y / self.maxDistance;
};
self.move = function (x, y) {
if (self.active) {
var localPos = self.toLocal({
x: x,
y: y
});
self.moveKnob(localPos.x, localPos.y);
}
};
self.up = function () {
self.active = false;
knobGraphics.x = 0;
knobGraphics.y = 0;
self.dx = 0;
self.dy = 0;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game state
var gameState = 'home';
var wave = 1;
var enemySpawnTimer = 0;
var asteroidSpawnTimer = 0;
var waveEnemyCount = 0;
var waveMaxEnemies = 10;
var powerUpChance = 0.2;
var touchIdentifiers = {};
// Game elements
var player;
var shootButton;
var playerBullets = [];
var enemies = [];
var enemyBullets = [];
var asteroids = [];
var powerUps = [];
var stars = [];
var homeScreen;
var shopScreen;
// UI elements
var scoreText;
var healthBar;
var waveText;
var coinsDisplay;
// Show home screen
function showHomeScreen() {
// Clear game area
game.removeChildren();
// Set background
game.setBackgroundColor(0x000033);
// Create starfield background
createStarfield();
// Create and show home screen
homeScreen = new HomeScreen();
game.addChild(homeScreen);
// Set game state
gameState = 'home';
}
// Show shop screen
function showShop() {
// Clear game area
game.removeChildren();
// Set background
game.setBackgroundColor(0x000044);
// Create starfield background
createStarfield();
// Create and show shop screen
shopScreen = new ShopScreen();
game.addChild(shopScreen);
// Set game state
gameState = 'shop';
}
// Initialize game
function initGame() {
// Reset game state
gameState = 'playing';
wave = 1;
enemySpawnTimer = 0;
asteroidSpawnTimer = 0;
waveEnemyCount = 0;
waveMaxEnemies = 5 + wave * 5;
playerBullets = [];
enemies = [];
enemyBullets = [];
asteroids = [];
powerUps = [];
touchIdentifiers = {};
// Set background
game.setBackgroundColor(0x000022);
// Create starfield background
createStarfield();
// Create player
player = new PlayerShip();
// Apply upgrades from shop
player.health = 100 + (storage.maxHealthBonus || 0) * 20;
player.maxHealth = player.health;
player.powerLevel = 1 + Math.min((storage.damageBonus || 0) / 2, 2);
player.speed = 8 * (1 + (storage.speedBonus || 0) * 0.2);
player.x = 2048 / 2;
player.y = 2732 - 300;
game.addChild(player);
// No shoot button needed as player shoots when moving
// Create UI
createUI();
// Start music
LK.playMusic('gameMusic');
}
// Create starfield background
function createStarfield() {
for (var i = 0; i < 100; i++) {
var star = LK.getAsset('joystickKnob', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1 + Math.random() * 0.2,
scaleY: 0.1 + Math.random() * 0.2,
alpha: 0.1 + Math.random() * 0.5,
tint: 0xffffff
});
star.x = Math.random() * 2048;
star.y = Math.random() * 2732;
star.speed = 0.5 + Math.random() * 2;
stars.push(star);
game.addChild(star);
}
}
// Create UI elements
function createUI() {
// Score text
scoreText = new Text2('SCORE: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
scoreText.x = 30;
scoreText.y = 30;
LK.gui.addChild(scoreText);
// Coins display
coinsDisplay = new Text2('COINS: ' + (storage.coins || 0), {
size: 60,
fill: 0xffcc33
});
coinsDisplay.anchor.set(0.5, 0);
coinsDisplay.x = 2048 / 2;
coinsDisplay.y = 30;
LK.gui.addChild(coinsDisplay);
// Health text
var healthText = new Text2('HEALTH:', {
size: 40,
fill: 0xFFFFFF
});
healthText.anchor.set(0, 0.5);
healthText.x = 30;
healthText.y = 100;
LK.gui.addChild(healthText);
// Health bar background
var healthBarBg = LK.getAsset('playerBullet', {
anchorX: 0,
anchorY: 0.5,
scaleX: 6,
scaleY: 0.8,
tint: 0x444444
});
healthBarBg.x = 180;
healthBarBg.y = 100;
LK.gui.addChild(healthBarBg);
// Health bar
healthBar = LK.getAsset('playerBullet', {
anchorX: 0,
anchorY: 0.5,
scaleX: 6,
scaleY: 0.8,
tint: 0x33ff33
});
healthBar.x = 180;
healthBar.y = 100;
LK.gui.addChild(healthBar);
// Wave text
waveText = new Text2('WAVE: 1 / 5', {
size: 60,
fill: 0xFFFFFF
});
waveText.anchor.set(1, 0);
waveText.x = 2048 - 30;
waveText.y = 30;
LK.gui.addChild(waveText);
// Wave progress indicator
var waveProgressBg = LK.getAsset('playerBullet', {
anchorX: 1,
anchorY: 0.5,
scaleX: 3,
scaleY: 0.4,
tint: 0x444444
});
waveProgressBg.x = 2048 - 30;
waveProgressBg.y = 100;
LK.gui.addChild(waveProgressBg);
// Wave progress bar
var waveProgress = LK.getAsset('playerBullet', {
anchorX: 1,
anchorY: 0.5,
scaleX: 3 * (1 / 5),
// Start at 1/5 for wave 1
scaleY: 0.4,
tint: 0x3399ff
});
waveProgress.x = 2048 - 30;
waveProgress.y = 100;
LK.gui.addChild(waveProgress);
}
// Spawn enemy
function spawnEnemy() {
var enemy = new EnemyShip();
enemy.x = 100 + Math.random() * (2048 - 200);
enemy.y = -100;
enemy.direction = Math.random() > 0.5 ? 1 : -1;
// Adjust enemy stats based on wave
enemy.health = 30 + wave * 5;
enemy.speed = 2 + Math.min(wave * 0.3, 3);
enemy.fireRate = Math.max(120 - wave * 5, 60);
enemy.points = 100 + wave * 10;
enemies.push(enemy);
game.addChild(enemy);
waveEnemyCount++;
}
// Spawn asteroid
function spawnAsteroid() {
var asteroid = new Asteroid();
asteroid.x = 100 + Math.random() * (2048 - 200);
asteroid.y = -100;
asteroids.push(asteroid);
game.addChild(asteroid);
}
// Spawn power-up
function spawnPowerUp(x, y) {
var powerUp = new PowerUp();
powerUp.x = x;
powerUp.y = y;
powerUps.push(powerUp);
game.addChild(powerUp);
}
// Check collisions
function checkCollisions() {
// Player bullets vs enemies
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
var hitEnemy = false;
// Check against enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
hitEnemy = true;
if (enemy.damage(player.powerLevel * 10)) {
// Enemy destroyed
LK.setScore(LK.getScore() + enemy.points);
scoreText.setText('SCORE: ' + LK.getScore());
// Award 10 coins when enemy is destroyed
storage.coins = (storage.coins || 0) + 10;
// Update coins display if it exists
if (coinsDisplay) {
coinsDisplay.setText('COINS: ' + storage.coins);
}
// Chance to spawn power-up
if (Math.random() < powerUpChance) {
spawnPowerUp(enemy.x, enemy.y);
}
enemy.destroy();
enemies.splice(j, 1);
}
break;
}
}
// Check against asteroids if bullet didn't hit an enemy
if (!hitEnemy) {
for (var j = asteroids.length - 1; j >= 0; j--) {
var asteroid = asteroids[j];
if (bullet.intersects(asteroid)) {
hitEnemy = true;
if (asteroid.damage(player.powerLevel * 10)) {
// Asteroid destroyed
LK.setScore(LK.getScore() + asteroid.points);
scoreText.setText('SCORE: ' + LK.getScore());
// Small chance to spawn power-up
if (Math.random() < powerUpChance * 0.5) {
spawnPowerUp(asteroid.x, asteroid.y);
}
asteroid.destroy();
asteroids.splice(j, 1);
}
break;
}
}
}
// Remove bullet if it hit something or went off-screen
if (hitEnemy || bullet.update()) {
bullet.destroy();
playerBullets.splice(i, 1);
}
}
// Enemy bullets vs player
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
// Check if bullet should be removed
if (bullet.update()) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check against player
if (gameState === 'playing' && bullet.intersects(player)) {
if (player.damage(bullet.damage)) {
// Player destroyed
gameOver();
}
// Update health bar
updateHealthBar();
// Remove bullet
bullet.destroy();
enemyBullets.splice(i, 1);
}
}
// Enemies vs player
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
// Check if enemy should be removed
if (enemy.update()) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
// Check against player
if (gameState === 'playing' && enemy.intersects(player)) {
if (player.damage(30)) {
// Player destroyed
gameOver();
}
// Update health bar
updateHealthBar();
// Destroy enemy
enemy.destroy();
enemies.splice(i, 1);
}
}
// Asteroids vs player
for (var i = asteroids.length - 1; i >= 0; i--) {
var asteroid = asteroids[i];
// Check if asteroid should be removed
if (asteroid.update()) {
asteroid.destroy();
asteroids.splice(i, 1);
continue;
}
// Check against player
if (gameState === 'playing' && asteroid.intersects(player)) {
if (player.damage(20)) {
// Player destroyed
gameOver();
}
// Update health bar
updateHealthBar();
// Destroy asteroid
asteroid.destroy();
asteroids.splice(i, 1);
}
}
// Power-ups vs player
for (var i = powerUps.length - 1; i >= 0; i--) {
var powerUp = powerUps[i];
// Check if power-up should be removed
if (powerUp.update()) {
powerUp.destroy();
powerUps.splice(i, 1);
continue;
}
// Check against player
if (gameState === 'playing' && powerUp.intersects(player)) {
// Apply power-up effect
if (powerUp.type === 'health') {
player.health = Math.min(player.health + 30, 100);
updateHealthBar();
} else if (powerUp.type === 'weapon') {
player.powerLevel = Math.min(player.powerLevel + 1, 3);
}
// Play sound
LK.getSound('powerUpCollect').play();
// Remove power-up
powerUp.destroy();
powerUps.splice(i, 1);
}
}
}
// Update health bar
function updateHealthBar() {
healthBar.scale.x = 3 * (player.health / player.maxHealth);
// Change color based on health percentage
var healthPercent = player.health / player.maxHealth * 100;
if (healthPercent > 60) {
healthBar.tint = 0x33ff33; // Green
} else if (healthPercent > 30) {
healthBar.tint = 0xffcc33; // Yellow
} else {
healthBar.tint = 0xff3333; // Red
}
}
// Game over
function gameOver() {
gameState = 'gameover';
LK.effects.flashScreen(0xff0000, 500);
LK.setTimeout(function () {
LK.showGameOver();
// Return to home screen after game over
LK.setTimeout(function () {
showHomeScreen();
}, 1500);
}, 500);
}
// Handle input
game.down = function (x, y, obj) {
// Store touch identifier to track multiple touches
if (obj.identifier !== undefined) {
touchIdentifiers[obj.identifier] = {
x: x,
y: y
};
}
// Use entire screen for movement
if (player) {
// Set player target position
player.targetX = x;
player.targetY = y;
player.isMoving = true;
}
// No shoot button check needed
};
game.move = function (x, y, obj) {
// Update stored touch position
if (obj.identifier !== undefined && touchIdentifiers[obj.identifier]) {
touchIdentifiers[obj.identifier] = {
x: x,
y: y
};
}
// Update player target position if moving - make sure player exists first
if (player && player.isMoving) {
player.targetX = x;
player.targetY = y;
// Make player shoot when moving
if (LK.ticks % player.fireRate === 0) {
player.shoot();
}
}
};
game.up = function (x, y, obj) {
// Remove touch identifier
if (obj.identifier !== undefined) {
delete touchIdentifiers[obj.identifier];
}
// Stop player movement
if (player) {
player.isMoving = false;
}
// No shoot button to release
};
// Main game loop
game.update = function () {
// Show home screen at start
if (gameState === 'home' && !homeScreen) {
showHomeScreen();
return;
}
// Update player if it exists
if (player && gameState === 'playing') {
player.update();
}
// Spawn enemies
if (gameState === 'playing') {
// Check if wave is complete
if (waveEnemyCount >= waveMaxEnemies && enemies.length === 0) {
// Start new wave
wave++;
waveEnemyCount = 0;
waveMaxEnemies = 5 + wave * 5;
waveText.setText('WAVE: ' + wave);
// Flash wave text
tween(waveText, {
scale: 1.5
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(waveText, {
scale: 1
}, {
duration: 300,
easing: tween.easeIn
});
}
});
// Check for game win condition
if (wave > 5) {
gameState = 'gamewin';
LK.effects.flashScreen(0x33ff33, 500);
LK.setTimeout(function () {
LK.showYouWin();
// Return to home screen after win
LK.setTimeout(function () {
showHomeScreen();
}, 1500);
}, 500);
}
}
// Spawn enemies
if (waveEnemyCount < waveMaxEnemies) {
enemySpawnTimer++;
if (enemySpawnTimer > Math.max(120 - wave * 5, 30)) {
enemySpawnTimer = 0;
spawnEnemy();
}
}
// Update wave progress bar
var waveProgressBar = LK.gui.children.find(function (child) {
return child.tint === 0x3399ff && child.scaleY === 0.4;
});
if (waveProgressBar) {
// Update wave text
waveText.setText('WAVE: ' + wave + ' / 5');
// Show remaining enemies
var remaining = waveMaxEnemies - waveEnemyCount + enemies.length;
var progressScale = Math.min(wave / 5, 1);
waveProgressBar.scale.x = 3 * progressScale;
}
// Spawn asteroids
asteroidSpawnTimer++;
if (asteroidSpawnTimer > Math.max(180 - wave * 3, 60)) {
asteroidSpawnTimer = 0;
spawnAsteroid();
}
}
// Check collisions
checkCollisions();
// Update starfield
for (var i = 0; i < stars.length; i++) {
var star = stars[i];
star.y += star.speed;
if (star.y > 2732) {
star.y = 0;
star.x = Math.random() * 2048;
}
}
};
// Initialize home screen at startup
showHomeScreen(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Asteroid = Container.expand(function () {
var self = Container.call(this);
var size = 60 + Math.floor(Math.random() * 60);
var asteroidGraphics = self.attachAsset('asteroid', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: size / 60,
scaleY: size / 60
});
self.health = 20 + Math.floor(size / 10);
self.speed = 1 + Math.random() * 3;
self.rotationSpeed = (Math.random() - 0.5) * 0.05;
self.points = 50;
self.update = function () {
self.y += self.speed;
asteroidGraphics.rotation += self.rotationSpeed;
// Check if asteroid is off-screen
if (self.y > 2832) {
return true; // Mark for removal
}
return false;
};
self.damage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xffffff, 100);
if (self.health <= 0) {
LK.getSound('explosion').play();
return true; // Asteroid destroyed
}
return false;
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = 10;
self.update = function () {
self.y += self.speed;
// Check if bullet is off-screen
if (self.y > 2832) {
return true; // Mark for removal
}
return false;
};
return self;
});
var EnemyShip = Container.expand(function () {
var self = Container.call(this);
var shipGraphics = self.attachAsset('enemyShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 30;
self.type = 'standard';
self.speed = 2 + Math.random() * 2;
self.points = 100;
self.fireRate = 120; // Frames between shots
self.fireCooldown = Math.floor(Math.random() * 60);
self.update = function () {
self.y += self.speed;
// Check if enemy is off-screen
if (self.y > 2832) {
return true; // Mark for removal
}
if (Math.random() < 0.02) {
self.direction = Math.random() > 0.5 ? 1 : -1;
}
if (self.direction) {
self.x += self.direction * (self.speed * 0.8);
// Keep within bounds
if (self.x < 100) {
self.x = 100;
self.direction = 1;
} else if (self.x > 2048 - 100) {
self.x = 2048 - 100;
self.direction = -1;
}
}
// Shooting
if (self.fireCooldown <= 0) {
self.fireCooldown = self.fireRate;
self.shoot();
} else {
self.fireCooldown--;
}
return false;
};
self.shoot = function () {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + 40;
enemyBullets.push(bullet);
game.addChild(bullet);
LK.getSound('enemyShoot').play();
};
self.damage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xffffff, 100);
if (self.health <= 0) {
LK.getSound('explosion').play();
return true; // Enemy destroyed
}
return false;
};
return self;
});
var HomeScreen = Container.expand(function () {
var self = Container.call(this);
// Title text
var titleText = new Text2('SPACE DEFENDER', {
size: 120,
fill: 0x33ccff
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 600;
self.addChild(titleText);
// Start button
var startButton = LK.getAsset('playerShip', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2,
tint: 0x33ff33
});
startButton.x = 2048 / 2;
startButton.y = 1200;
self.addChild(startButton);
// Start text
var startText = new Text2('START', {
size: 80,
fill: 0xFFFFFF
});
startText.anchor.set(0.5, 0.5);
startText.x = 2048 / 2;
startText.y = 1200;
self.addChild(startText);
// Shop button
var shopButton = LK.getAsset('powerUp', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3,
tint: 0xffcc33
});
shopButton.x = 2048 / 2;
shopButton.y = 1600;
self.addChild(shopButton);
// Shop text
var shopText = new Text2('SHOP', {
size: 80,
fill: 0xFFFFFF
});
shopText.anchor.set(0.5, 0.5);
shopText.x = 2048 / 2;
shopText.y = 1600;
self.addChild(shopText);
// Coins display
var coins = storage.coins || 0;
var coinsText = new Text2('COINS: ' + coins, {
size: 60,
fill: 0xffcc33
});
coinsText.anchor.set(0.5, 0);
coinsText.x = 2048 / 2;
coinsText.y = 1800;
self.addChild(coinsText);
// Event handlers
startButton.interactive = true;
startButton.down = function () {
game.gameState = 'playing';
self.destroy();
initGame();
};
shopButton.interactive = true;
shopButton.down = function () {
game.gameState = 'shop';
self.destroy();
showShop();
};
return self;
});
var PlayerBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 15;
self.power = 10;
self.update = function () {
self.y -= self.speed;
// Check if bullet is off-screen
if (self.y < -50) {
return true; // Mark for removal
}
return false;
};
return self;
});
var PlayerShip = Container.expand(function () {
var self = Container.call(this);
var shipGraphics = self.attachAsset('playerShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 200;
self.maxHealth = 200;
self.fireRate = 20; // Frames between shots
self.fireCooldown = 0;
self.bulletSpeed = 15;
self.speed = 8;
self.powerLevel = 1;
self.targetX = 0;
self.targetY = 0;
self.isMoving = false;
self.update = function () {
if (self.fireCooldown > 0) {
self.fireCooldown--;
}
// Move toward target position if moving
if (self.isMoving) {
// Calculate direction vector
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
// Calculate distance
var distance = Math.sqrt(dx * dx + dy * dy);
// If we're close enough, just set position directly
if (distance < self.speed) {
self.x = self.targetX;
self.y = self.targetY;
} else {
// Normalize direction vector and move
dx = dx / distance * self.speed;
dy = dy / distance * self.speed;
// Update position
self.x += dx;
self.y += dy;
}
// Keep player in bounds
self.x = Math.max(50, Math.min(self.x, 2048 - 50));
self.y = Math.max(50, Math.min(self.y, 2732 - 50));
}
};
self.damage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xff0000, 300);
// Add visual feedback
var damageText = new Text2('-' + amount, {
size: 40,
fill: 0xff3333
});
damageText.anchor.set(0.5, 0.5);
damageText.x = self.x;
damageText.y = self.y - 60;
game.addChild(damageText);
// Animate damage text
tween(damageText, {
y: damageText.y - 80,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
damageText.destroy();
}
});
if (self.health <= 0) {
self.health = 0;
return true; // Ship destroyed
}
return false;
};
self.shoot = function () {
if (self.fireCooldown <= 0) {
self.fireCooldown = self.fireRate;
LK.getSound('playerShoot').play();
// Create bullets based on power level
if (self.powerLevel === 1) {
// Single bullet
createBullet(self.x, self.y - 40);
} else if (self.powerLevel === 2) {
// Double bullets
createBullet(self.x - 20, self.y - 30);
createBullet(self.x + 20, self.y - 30);
} else {
// Triple bullets
createBullet(self.x, self.y - 40);
createBullet(self.x - 25, self.y - 30);
createBullet(self.x + 25, self.y - 30);
}
}
};
// Helper function to create a bullet
function createBullet(x, y) {
var bullet = new PlayerBullet();
bullet.x = x;
bullet.y = y;
playerBullets.push(bullet);
game.addChild(bullet);
}
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerUpGraphics = self.attachAsset('powerUp', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = Math.random() < 0.7 ? 'weapon' : 'health';
self.speed = 3;
// Change color based on type
if (self.type === 'health') {
powerUpGraphics.tint = 0x33ff33; // Green for health
} else {
powerUpGraphics.tint = 0xffcc33; // Yellow for weapon
}
self.update = function () {
self.y += self.speed;
// Slight bobbing motion
self.x += Math.sin(LK.ticks * 0.05) * 0.5;
// Check if power-up is off-screen
if (self.y > 2832) {
return true; // Mark for removal
}
return false;
};
return self;
});
var ShootButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('shootButton', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6
});
self.pressed = false;
self.down = function () {
self.pressed = true;
buttonGraphics.alpha = 0.9;
buttonGraphics.scale.set(0.9);
};
self.up = function () {
self.pressed = false;
buttonGraphics.alpha = 0.6;
buttonGraphics.scale.set(1);
};
return self;
});
var ShopScreen = Container.expand(function () {
var self = Container.call(this);
// Title
var titleText = new Text2('SHOP', {
size: 120,
fill: 0xffcc33
});
titleText.anchor.set(0.5, 0);
titleText.x = 2048 / 2;
titleText.y = 300;
self.addChild(titleText);
// Coins display
var coins = storage.coins || 0;
var coinsText = new Text2('COINS: ' + coins, {
size: 60,
fill: 0xffcc33
});
coinsText.anchor.set(0.5, 0);
coinsText.x = 2048 / 2;
coinsText.y = 450;
self.addChild(coinsText);
// Shop items
var items = [{
name: 'HEALTH+',
cost: 100,
effect: 'Increases max health by 20',
property: 'maxHealthBonus'
}, {
name: 'DAMAGE+',
cost: 150,
effect: 'Increases weapon damage by 25%',
property: 'damageBonus'
}, {
name: 'SPEED+',
cost: 120,
effect: 'Increases ship speed by 20%',
property: 'speedBonus'
}];
var itemY = 600;
for (var i = 0; i < items.length; i++) {
var item = items[i];
// Item container
var itemBg = LK.getAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 6,
scaleY: 2,
tint: 0x333366,
alpha: 0.7
});
itemBg.x = 2048 / 2;
itemBg.y = itemY;
self.addChild(itemBg);
// Item name
var nameText = new Text2(item.name, {
size: 60,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0.5);
nameText.x = 2048 / 2 - 400;
nameText.y = itemY - 40;
self.addChild(nameText);
// Item effect
var effectText = new Text2(item.effect, {
size: 40,
fill: 0xcccccc
});
effectText.anchor.set(0.5, 0.5);
effectText.x = 2048 / 2;
effectText.y = itemY + 40;
self.addChild(effectText);
// Cost
var costText = new Text2(item.cost + ' COINS', {
size: 50,
fill: 0xffcc33
});
costText.anchor.set(0.5, 0.5);
costText.x = 2048 / 2 + 350;
costText.y = itemY;
self.addChild(costText);
// Buy button
var buyButton = LK.getAsset('powerUp', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
tint: coins >= item.cost ? 0x33ff33 : 0x666666
});
buyButton.x = 2048 / 2 + 500;
buyButton.y = itemY;
buyButton.interactive = true;
buyButton.itemIndex = i;
buyButton.down = function () {
var selectedItem = items[this.itemIndex];
if ((storage.coins || 0) >= selectedItem.cost) {
// Purchase item
storage.coins = (storage.coins || 0) - selectedItem.cost;
// Increase the upgrade level
storage[selectedItem.property] = (storage[selectedItem.property] || 0) + 1;
// Show feedback and refresh shop
LK.effects.flashObject(this, 0x33ff33, 300);
self.destroy();
showShop();
} else {
// Not enough coins
LK.effects.flashObject(this, 0xff3333, 300);
}
};
self.addChild(buyButton);
// Level indicator
var level = storage[item.property] || 0;
var levelText = new Text2('LEVEL: ' + level, {
size: 40,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
levelText.x = 2048 / 2 - 400;
levelText.y = itemY + 40;
self.addChild(levelText);
itemY += 250;
}
// Back button
var backButton = LK.getAsset('playerShip', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
rotation: Math.PI,
tint: 0x3399ff
});
backButton.x = 2048 / 2;
backButton.y = 2300;
backButton.interactive = true;
backButton.down = function () {
self.destroy();
showHomeScreen();
};
self.addChild(backButton);
// Back text
var backText = new Text2('BACK', {
size: 60,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = 2048 / 2;
backText.y = 2300;
self.addChild(backText);
return self;
});
var VirtualJoystick = Container.expand(function () {
var self = Container.call(this);
var baseGraphics = self.attachAsset('joystick', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6
});
var knobGraphics = self.attachAsset('joystickKnob', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
self.maxDistance = 80;
self.active = false;
self.dx = 0;
self.dy = 0;
self.down = function (x, y) {
var localPos = self.toLocal({
x: x,
y: y
});
if (Math.sqrt(Math.pow(localPos.x, 2) + Math.pow(localPos.y, 2)) <= baseGraphics.width / 2) {
self.active = true;
self.moveKnob(localPos.x, localPos.y);
}
};
self.moveKnob = function (x, y) {
var dist = Math.sqrt(x * x + y * y);
if (dist > self.maxDistance) {
x = x / dist * self.maxDistance;
y = y / dist * self.maxDistance;
}
knobGraphics.x = x;
knobGraphics.y = y;
// Normalize to -1 to 1 range for movement
self.dx = x / self.maxDistance;
self.dy = y / self.maxDistance;
};
self.move = function (x, y) {
if (self.active) {
var localPos = self.toLocal({
x: x,
y: y
});
self.moveKnob(localPos.x, localPos.y);
}
};
self.up = function () {
self.active = false;
knobGraphics.x = 0;
knobGraphics.y = 0;
self.dx = 0;
self.dy = 0;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game state
var gameState = 'home';
var wave = 1;
var enemySpawnTimer = 0;
var asteroidSpawnTimer = 0;
var waveEnemyCount = 0;
var waveMaxEnemies = 10;
var powerUpChance = 0.2;
var touchIdentifiers = {};
// Game elements
var player;
var shootButton;
var playerBullets = [];
var enemies = [];
var enemyBullets = [];
var asteroids = [];
var powerUps = [];
var stars = [];
var homeScreen;
var shopScreen;
// UI elements
var scoreText;
var healthBar;
var waveText;
var coinsDisplay;
// Show home screen
function showHomeScreen() {
// Clear game area
game.removeChildren();
// Set background
game.setBackgroundColor(0x000033);
// Create starfield background
createStarfield();
// Create and show home screen
homeScreen = new HomeScreen();
game.addChild(homeScreen);
// Set game state
gameState = 'home';
}
// Show shop screen
function showShop() {
// Clear game area
game.removeChildren();
// Set background
game.setBackgroundColor(0x000044);
// Create starfield background
createStarfield();
// Create and show shop screen
shopScreen = new ShopScreen();
game.addChild(shopScreen);
// Set game state
gameState = 'shop';
}
// Initialize game
function initGame() {
// Reset game state
gameState = 'playing';
wave = 1;
enemySpawnTimer = 0;
asteroidSpawnTimer = 0;
waveEnemyCount = 0;
waveMaxEnemies = 5 + wave * 5;
playerBullets = [];
enemies = [];
enemyBullets = [];
asteroids = [];
powerUps = [];
touchIdentifiers = {};
// Set background
game.setBackgroundColor(0x000022);
// Create starfield background
createStarfield();
// Create player
player = new PlayerShip();
// Apply upgrades from shop
player.health = 100 + (storage.maxHealthBonus || 0) * 20;
player.maxHealth = player.health;
player.powerLevel = 1 + Math.min((storage.damageBonus || 0) / 2, 2);
player.speed = 8 * (1 + (storage.speedBonus || 0) * 0.2);
player.x = 2048 / 2;
player.y = 2732 - 300;
game.addChild(player);
// No shoot button needed as player shoots when moving
// Create UI
createUI();
// Start music
LK.playMusic('gameMusic');
}
// Create starfield background
function createStarfield() {
for (var i = 0; i < 100; i++) {
var star = LK.getAsset('joystickKnob', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1 + Math.random() * 0.2,
scaleY: 0.1 + Math.random() * 0.2,
alpha: 0.1 + Math.random() * 0.5,
tint: 0xffffff
});
star.x = Math.random() * 2048;
star.y = Math.random() * 2732;
star.speed = 0.5 + Math.random() * 2;
stars.push(star);
game.addChild(star);
}
}
// Create UI elements
function createUI() {
// Score text
scoreText = new Text2('SCORE: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
scoreText.x = 30;
scoreText.y = 30;
LK.gui.addChild(scoreText);
// Coins display
coinsDisplay = new Text2('COINS: ' + (storage.coins || 0), {
size: 60,
fill: 0xffcc33
});
coinsDisplay.anchor.set(0.5, 0);
coinsDisplay.x = 2048 / 2;
coinsDisplay.y = 30;
LK.gui.addChild(coinsDisplay);
// Health text
var healthText = new Text2('HEALTH:', {
size: 40,
fill: 0xFFFFFF
});
healthText.anchor.set(0, 0.5);
healthText.x = 30;
healthText.y = 100;
LK.gui.addChild(healthText);
// Health bar background
var healthBarBg = LK.getAsset('playerBullet', {
anchorX: 0,
anchorY: 0.5,
scaleX: 6,
scaleY: 0.8,
tint: 0x444444
});
healthBarBg.x = 180;
healthBarBg.y = 100;
LK.gui.addChild(healthBarBg);
// Health bar
healthBar = LK.getAsset('playerBullet', {
anchorX: 0,
anchorY: 0.5,
scaleX: 6,
scaleY: 0.8,
tint: 0x33ff33
});
healthBar.x = 180;
healthBar.y = 100;
LK.gui.addChild(healthBar);
// Wave text
waveText = new Text2('WAVE: 1 / 5', {
size: 60,
fill: 0xFFFFFF
});
waveText.anchor.set(1, 0);
waveText.x = 2048 - 30;
waveText.y = 30;
LK.gui.addChild(waveText);
// Wave progress indicator
var waveProgressBg = LK.getAsset('playerBullet', {
anchorX: 1,
anchorY: 0.5,
scaleX: 3,
scaleY: 0.4,
tint: 0x444444
});
waveProgressBg.x = 2048 - 30;
waveProgressBg.y = 100;
LK.gui.addChild(waveProgressBg);
// Wave progress bar
var waveProgress = LK.getAsset('playerBullet', {
anchorX: 1,
anchorY: 0.5,
scaleX: 3 * (1 / 5),
// Start at 1/5 for wave 1
scaleY: 0.4,
tint: 0x3399ff
});
waveProgress.x = 2048 - 30;
waveProgress.y = 100;
LK.gui.addChild(waveProgress);
}
// Spawn enemy
function spawnEnemy() {
var enemy = new EnemyShip();
enemy.x = 100 + Math.random() * (2048 - 200);
enemy.y = -100;
enemy.direction = Math.random() > 0.5 ? 1 : -1;
// Adjust enemy stats based on wave
enemy.health = 30 + wave * 5;
enemy.speed = 2 + Math.min(wave * 0.3, 3);
enemy.fireRate = Math.max(120 - wave * 5, 60);
enemy.points = 100 + wave * 10;
enemies.push(enemy);
game.addChild(enemy);
waveEnemyCount++;
}
// Spawn asteroid
function spawnAsteroid() {
var asteroid = new Asteroid();
asteroid.x = 100 + Math.random() * (2048 - 200);
asteroid.y = -100;
asteroids.push(asteroid);
game.addChild(asteroid);
}
// Spawn power-up
function spawnPowerUp(x, y) {
var powerUp = new PowerUp();
powerUp.x = x;
powerUp.y = y;
powerUps.push(powerUp);
game.addChild(powerUp);
}
// Check collisions
function checkCollisions() {
// Player bullets vs enemies
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
var hitEnemy = false;
// Check against enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
hitEnemy = true;
if (enemy.damage(player.powerLevel * 10)) {
// Enemy destroyed
LK.setScore(LK.getScore() + enemy.points);
scoreText.setText('SCORE: ' + LK.getScore());
// Award 10 coins when enemy is destroyed
storage.coins = (storage.coins || 0) + 10;
// Update coins display if it exists
if (coinsDisplay) {
coinsDisplay.setText('COINS: ' + storage.coins);
}
// Chance to spawn power-up
if (Math.random() < powerUpChance) {
spawnPowerUp(enemy.x, enemy.y);
}
enemy.destroy();
enemies.splice(j, 1);
}
break;
}
}
// Check against asteroids if bullet didn't hit an enemy
if (!hitEnemy) {
for (var j = asteroids.length - 1; j >= 0; j--) {
var asteroid = asteroids[j];
if (bullet.intersects(asteroid)) {
hitEnemy = true;
if (asteroid.damage(player.powerLevel * 10)) {
// Asteroid destroyed
LK.setScore(LK.getScore() + asteroid.points);
scoreText.setText('SCORE: ' + LK.getScore());
// Small chance to spawn power-up
if (Math.random() < powerUpChance * 0.5) {
spawnPowerUp(asteroid.x, asteroid.y);
}
asteroid.destroy();
asteroids.splice(j, 1);
}
break;
}
}
}
// Remove bullet if it hit something or went off-screen
if (hitEnemy || bullet.update()) {
bullet.destroy();
playerBullets.splice(i, 1);
}
}
// Enemy bullets vs player
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
// Check if bullet should be removed
if (bullet.update()) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check against player
if (gameState === 'playing' && bullet.intersects(player)) {
if (player.damage(bullet.damage)) {
// Player destroyed
gameOver();
}
// Update health bar
updateHealthBar();
// Remove bullet
bullet.destroy();
enemyBullets.splice(i, 1);
}
}
// Enemies vs player
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
// Check if enemy should be removed
if (enemy.update()) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
// Check against player
if (gameState === 'playing' && enemy.intersects(player)) {
if (player.damage(30)) {
// Player destroyed
gameOver();
}
// Update health bar
updateHealthBar();
// Destroy enemy
enemy.destroy();
enemies.splice(i, 1);
}
}
// Asteroids vs player
for (var i = asteroids.length - 1; i >= 0; i--) {
var asteroid = asteroids[i];
// Check if asteroid should be removed
if (asteroid.update()) {
asteroid.destroy();
asteroids.splice(i, 1);
continue;
}
// Check against player
if (gameState === 'playing' && asteroid.intersects(player)) {
if (player.damage(20)) {
// Player destroyed
gameOver();
}
// Update health bar
updateHealthBar();
// Destroy asteroid
asteroid.destroy();
asteroids.splice(i, 1);
}
}
// Power-ups vs player
for (var i = powerUps.length - 1; i >= 0; i--) {
var powerUp = powerUps[i];
// Check if power-up should be removed
if (powerUp.update()) {
powerUp.destroy();
powerUps.splice(i, 1);
continue;
}
// Check against player
if (gameState === 'playing' && powerUp.intersects(player)) {
// Apply power-up effect
if (powerUp.type === 'health') {
player.health = Math.min(player.health + 30, 100);
updateHealthBar();
} else if (powerUp.type === 'weapon') {
player.powerLevel = Math.min(player.powerLevel + 1, 3);
}
// Play sound
LK.getSound('powerUpCollect').play();
// Remove power-up
powerUp.destroy();
powerUps.splice(i, 1);
}
}
}
// Update health bar
function updateHealthBar() {
healthBar.scale.x = 3 * (player.health / player.maxHealth);
// Change color based on health percentage
var healthPercent = player.health / player.maxHealth * 100;
if (healthPercent > 60) {
healthBar.tint = 0x33ff33; // Green
} else if (healthPercent > 30) {
healthBar.tint = 0xffcc33; // Yellow
} else {
healthBar.tint = 0xff3333; // Red
}
}
// Game over
function gameOver() {
gameState = 'gameover';
LK.effects.flashScreen(0xff0000, 500);
LK.setTimeout(function () {
LK.showGameOver();
// Return to home screen after game over
LK.setTimeout(function () {
showHomeScreen();
}, 1500);
}, 500);
}
// Handle input
game.down = function (x, y, obj) {
// Store touch identifier to track multiple touches
if (obj.identifier !== undefined) {
touchIdentifiers[obj.identifier] = {
x: x,
y: y
};
}
// Use entire screen for movement
if (player) {
// Set player target position
player.targetX = x;
player.targetY = y;
player.isMoving = true;
}
// No shoot button check needed
};
game.move = function (x, y, obj) {
// Update stored touch position
if (obj.identifier !== undefined && touchIdentifiers[obj.identifier]) {
touchIdentifiers[obj.identifier] = {
x: x,
y: y
};
}
// Update player target position if moving - make sure player exists first
if (player && player.isMoving) {
player.targetX = x;
player.targetY = y;
// Make player shoot when moving
if (LK.ticks % player.fireRate === 0) {
player.shoot();
}
}
};
game.up = function (x, y, obj) {
// Remove touch identifier
if (obj.identifier !== undefined) {
delete touchIdentifiers[obj.identifier];
}
// Stop player movement
if (player) {
player.isMoving = false;
}
// No shoot button to release
};
// Main game loop
game.update = function () {
// Show home screen at start
if (gameState === 'home' && !homeScreen) {
showHomeScreen();
return;
}
// Update player if it exists
if (player && gameState === 'playing') {
player.update();
}
// Spawn enemies
if (gameState === 'playing') {
// Check if wave is complete
if (waveEnemyCount >= waveMaxEnemies && enemies.length === 0) {
// Start new wave
wave++;
waveEnemyCount = 0;
waveMaxEnemies = 5 + wave * 5;
waveText.setText('WAVE: ' + wave);
// Flash wave text
tween(waveText, {
scale: 1.5
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(waveText, {
scale: 1
}, {
duration: 300,
easing: tween.easeIn
});
}
});
// Check for game win condition
if (wave > 5) {
gameState = 'gamewin';
LK.effects.flashScreen(0x33ff33, 500);
LK.setTimeout(function () {
LK.showYouWin();
// Return to home screen after win
LK.setTimeout(function () {
showHomeScreen();
}, 1500);
}, 500);
}
}
// Spawn enemies
if (waveEnemyCount < waveMaxEnemies) {
enemySpawnTimer++;
if (enemySpawnTimer > Math.max(120 - wave * 5, 30)) {
enemySpawnTimer = 0;
spawnEnemy();
}
}
// Update wave progress bar
var waveProgressBar = LK.gui.children.find(function (child) {
return child.tint === 0x3399ff && child.scaleY === 0.4;
});
if (waveProgressBar) {
// Update wave text
waveText.setText('WAVE: ' + wave + ' / 5');
// Show remaining enemies
var remaining = waveMaxEnemies - waveEnemyCount + enemies.length;
var progressScale = Math.min(wave / 5, 1);
waveProgressBar.scale.x = 3 * progressScale;
}
// Spawn asteroids
asteroidSpawnTimer++;
if (asteroidSpawnTimer > Math.max(180 - wave * 3, 60)) {
asteroidSpawnTimer = 0;
spawnAsteroid();
}
}
// Check collisions
checkCollisions();
// Update starfield
for (var i = 0; i < stars.length; i++) {
var star = stars[i];
star.y += star.speed;
if (star.y > 2732) {
star.y = 0;
star.x = Math.random() * 2048;
}
}
};
// Initialize home screen at startup
showHomeScreen();