/****
* 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 asteroidGraphics = self.attachAsset('asteroid', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
self.speed = 6;
self.rotationSpeed = Math.random() * 0.05 - 0.025;
self.scoreValue = 5;
self.lastY = -100; // Track last position for collision detection
self.update = function () {
self.lastY = self.y;
self.y += self.speed;
self.rotation += self.rotationSpeed;
// Add side-to-side movement to make dodging more challenging
self.x += Math.sin(LK.ticks * 0.02) * 2;
};
return self;
});
var BadOil = Container.expand(function () {
var self = Container.call(this);
var badOilGraphics = self.attachAsset('Bad-oil', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
self.speed = 5;
self.lastY = -100;
self.update = function () {
self.lastY = self.y;
self.y += self.speed;
// Add some side movement
self.x += Math.sin(LK.ticks * 0.03) * 2;
};
return self;
});
var BossEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.5,
tint: 0xff0000
});
self.speed = 2;
self.health = 5;
self.moveDirection = 1; // 1 for right, -1 for left
self.shootChance = 0.03; // Higher chance to shoot than regular enemies
self.scoreValue = 50;
self.lastY = -100;
self.update = function () {
self.lastY = self.y;
// Move down more slowly
self.y += self.speed;
// Move in a more complex pattern
self.x += self.moveDirection * 3 * Math.sin(LK.ticks * 0.02);
// Change direction at edges
if (self.x < 200 || self.x > 2048 - 200) {
self.moveDirection *= -1;
}
};
return self;
});
var EarthMonster = Container.expand(function () {
var self = Container.call(this);
var monsterGraphics = self.attachAsset('Earth-monster', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.0,
scaleY: 3.0
});
self.speed = 1.5;
self.health = 20; // Earth-monster has 20 hearts
self.moveDirection = 1; // 1 for right, -1 for left
self.shootChance = 0.05; // Higher chance to shoot than other enemies
self.scoreValue = 200;
self.lastY = -100;
self.update = function () {
self.lastY = self.y;
// Move down more slowly
self.y += self.speed;
// Move in a complex pattern
self.x += self.moveDirection * 2 * Math.sin(LK.ticks * 0.03);
// Change direction at edges
if (self.x < 300 || self.x > 2048 - 300) {
self.moveDirection *= -1;
}
};
return self;
});
var EarthMonsterBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
tint: 0x00ffff
});
self.speed = 12; // Faster than regular enemy bullets
self.damage = 3; // Does 3x damage
self.update = function () {
self.y += self.speed;
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.moveDirection = 1; // 1 for right, -1 for left
self.shootChance = 0.01; // Chance to shoot per update
self.scoreValue = 10;
self.update = function () {
// Move down
self.y += self.speed;
// Move side to side
self.x += self.moveDirection * 2;
// Change direction at edges
if (self.x < 100 || self.x > 2048 - 100) {
self.moveDirection *= -1;
}
};
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.update = function () {
self.y += self.speed;
};
return self;
});
var Heart = Container.expand(function () {
var self = Container.call(this);
var heartGraphics = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xff0000,
scaleX: 0.8,
scaleY: 0.8
});
self.setActive = function (active) {
heartGraphics.alpha = active ? 1 : 0.3;
};
return self;
});
var NewAsteroid = Container.expand(function () {
var self = Container.call(this);
var asteroidGraphics = self.attachAsset('New-ast', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
self.speed = 7; // Faster than regular asteroid
self.rotationSpeed = Math.random() * 0.08 - 0.04; // Faster rotation
self.scoreValue = 0; // No points for this one since it's deadly
self.lastY = -100; // Track last position for collision detection
self.update = function () {
self.lastY = self.y;
self.y += self.speed;
self.rotation += self.rotationSpeed;
// More erratic movement pattern
self.x += Math.sin(LK.ticks * 0.03) * 3;
};
return self;
});
var NewEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('New-enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4; // Faster than regular enemy
self.moveDirection = 1; // 1 for right, -1 for left
self.shootChance = 0.02; // Higher chance to shoot than regular enemies
self.scoreValue = 20; // Worth more points than regular enemies
self.update = function () {
// Only move left and right, no vertical movement
// Move side to side more aggressively
self.x += self.moveDirection * 3;
// Change direction at edges
if (self.x < 100 || self.x > 2048 - 100) {
self.moveDirection *= -1;
}
};
return self;
});
var OilFood = Container.expand(function () {
var self = Container.call(this);
var oilFoodGraphics = self.attachAsset('Oil-food', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
self.speed = 5;
self.lastY = -100;
self.update = function () {
self.lastY = self.y;
self.y += self.speed;
// Add some side movement
self.x += Math.sin(LK.ticks * 0.03) * 2;
};
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.update = function () {
self.y += self.speed;
};
return self;
});
var PlayerShip = Container.expand(function () {
var self = Container.call(this);
var shipGraphics = self.attachAsset('playerShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.shield = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.hasShield = false;
self.fireRate = 20; // Time between shots in ticks
self.fireCooldown = 0;
self.rapidFire = false;
self.rapidFireTimer = 0;
self.lives = 3; // Keep this for backward compatibility
self.activateShield = function () {
self.hasShield = true;
self.shield.alpha = 0.5;
};
self.deactivateShield = function () {
self.hasShield = false;
self.shield.alpha = 0;
};
self.activateRapidFire = function () {
self.rapidFire = true;
self.rapidFireTimer = 300; // 5 seconds at 60 FPS
};
self.canFire = function () {
if (self.fireCooldown <= 0) {
self.fireCooldown = self.rapidFire ? Math.floor(self.fireRate / 2) : self.fireRate;
return true;
}
return false;
};
self.hit = function () {
if (self.hasShield) {
self.deactivateShield();
return false; // Shield absorbed the hit
} else {
// Use the hearts system instead of lives
if (currentHearts > 0) {
currentHearts--;
updateHearts();
LK.effects.flashObject(self, 0xff0000, 500);
return true; // Ship took damage
}
return false;
}
};
self.update = function () {
if (self.fireCooldown > 0) {
self.fireCooldown--;
}
if (self.rapidFire && self.rapidFireTimer > 0) {
self.rapidFireTimer--;
if (self.rapidFireTimer <= 0) {
self.rapidFire = false;
}
}
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerupGraphics = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.type = Math.floor(Math.random() * 2); // 0 for shield, 1 for rapid fire
// Set color based on type
if (self.type === 0) {
powerupGraphics.tint = 0x3498db; // Blue for shield
} else {
powerupGraphics.tint = 0xf39c12; // Orange for rapid fire
}
self.update = function () {
self.y += self.speed;
// Floating animation
self.x += Math.sin(LK.ticks * 0.05) * 1;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Save the current game state
function saveGame() {
storage.set('saveSpace_level', gameLevel);
storage.set('saveSpace_score', scoreValue);
storage.set('saveSpace_hearts', currentHearts);
storage.set('saveSpace_oil', oilPercentage);
}
// Load the saved game state
function loadGame() {
var savedLevel = storage.get('saveSpace_level', 1);
var savedScore = storage.get('saveSpace_score', 0);
var savedHearts = storage.get('saveSpace_hearts', 10);
var savedOil = storage.get('saveSpace_oil', 20);
gameLevel = savedLevel;
scoreValue = savedScore;
currentHearts = savedHearts;
oilPercentage = savedOil;
// Update UI elements
levelTxt.setText('Level: ' + gameLevel);
scoreTxt.setText('Score: ' + scoreValue);
oilTxt.setText('Oil: ' + oilPercentage + '/' + maxOilPercentage);
updateHearts();
// Set game difficulty based on loaded level
difficultyMultiplier = 1.0 + gameLevel * 0.15;
enemySpawnRate = Math.max(60, Math.floor(240 / difficultyMultiplier));
asteroidSpawnRate = Math.max(45, Math.floor(90 / difficultyMultiplier));
oilFoodSpawnRate = Math.min(600, Math.floor(360 * (1 + gameLevel * 0.1)));
}
// Auto-save every minute
LK.setInterval(saveGame, 60000);
// Save when player takes damage
var originalHit = PlayerShip.prototype.hit;
PlayerShip.prototype.hit = function () {
var result = originalHit.apply(this, arguments);
if (result) {
saveGame();
}
return result;
};
// Save on level up
var originalCheckLevelProgress = checkLevelProgress;
checkLevelProgress = function checkLevelProgress() {
var oldLevel = gameLevel;
originalCheckLevelProgress.apply(this, arguments);
if (gameLevel > oldLevel) {
saveGame();
}
};
// Try to load saved game at start
loadGame();
game.setBackgroundColor(0x0c1445);
// Game variables
var player;
var playerBullets = [];
var enemies = [];
var bossEnemies = []; // Track boss enemies separately
var enemyBullets = [];
var asteroids = [];
var powerups = [];
var hearts = [];
var oilFoods = []; // Array to track oil foods
var heartsContainer;
var maxHearts = 10;
var currentHearts = 10;
var gameLevel = 1;
var enemySpawnRate = 240; // Reduced enemy spawn rate to focus on dodging asteroids
var asteroidSpawnRate = 90; // Increased asteroid spawn rate to create dodging challenge
var powerupSpawnRate = 900; // Every 15 seconds - rarer powerups
var bossSpawnRate = 600; // How often to check for boss spawn when at level 10
var newEnemySpawnRate = 180; // More frequent than regular enemies
var oilFoodSpawnRate = 360; // Every 6 seconds - slightly rarer
var gameState = "playing";
var scoreValue = 0;
var oilPercentage = 20; // Track oil percentage, starting at 20 out of 40
var maxOilPercentage = 40; // Maximum oil percentage
var powerupChance = 0.7; // 70% chance to actually spawn a powerup when the timer triggers
var difficultyMultiplier = 1.0; // Base difficulty multiplier that increases with level
// Setup score display
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
scoreTxt.x = -300; // Offset from right edge
// Hearts display - text version as fallback
var livesTxt = new Text2('Hearts: 10/10', {
size: 60,
fill: 0xFFFFFF
});
livesTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(livesTxt);
livesTxt.x = -300;
livesTxt.y = 70; // Below score text
// Function to update hearts display
function updateHearts() {
for (var i = 0; i < hearts.length; i++) {
hearts[i].setActive(i < currentHearts);
}
livesTxt.setText('Hearts: ' + currentHearts + '/' + maxHearts);
// Game over condition
if (currentHearts <= 0) {
gameState = "over";
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
}
// Level display
var levelTxt = new Text2('Level: 1', {
size: 60,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(levelTxt);
levelTxt.x = -300;
levelTxt.y = 140; // Below lives text
// Oil percentage display
var oilTxt = new Text2('Oil: 20/40', {
size: 60,
fill: 0xFFFFFF
});
oilTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(oilTxt);
oilTxt.x = -300;
oilTxt.y = 210; // Below level text
// Initialize player
function initializePlayer() {
player = new PlayerShip();
player.x = 2048 / 2;
player.y = 2732 - 200;
game.addChild(player);
// Initialize hearts display
initializeHearts();
}
// Initialize hearts display
function initializeHearts() {
// Remove existing hearts if any
if (heartsContainer) {
heartsContainer.destroy();
hearts = [];
}
// Create container for hearts
heartsContainer = new Container();
game.addChild(heartsContainer);
heartsContainer.x = 100;
heartsContainer.y = 100;
// Create hearts
currentHearts = maxHearts;
for (var i = 0; i < maxHearts; i++) {
var heart = new Heart();
heart.x = i % 5 * 40;
heart.y = Math.floor(i / 5) * 40;
heart.setActive(true);
hearts.push(heart);
heartsContainer.addChild(heart);
}
}
// Spawn enemy
function spawnEnemy() {
var enemy = new Enemy();
enemy.x = Math.random() * (2048 - 200) + 100;
enemy.y = -100;
enemy.speed = 2 + gameLevel * 0.5 * difficultyMultiplier; // Scale with difficulty multiplier
enemy.shootChance = Math.min(0.05, 0.01 + gameLevel * 0.004); // Increase shoot chance with level
enemy.health = 1 + Math.floor(gameLevel / 5); // Tougher enemies at higher levels
enemies.push(enemy);
game.addChild(enemy);
}
// Spawn asteroid
function spawnAsteroid() {
// At level 2 and above, randomly spawn the deadly asteroid
// Chance increases with level
if (gameLevel >= 2 && Math.random() < 0.2 + gameLevel * 0.03) {
var deadlyAsteroid = new NewAsteroid();
deadlyAsteroid.x = Math.random() * (2048 - 200) + 100;
deadlyAsteroid.y = -100;
deadlyAsteroid.speed = 7 + gameLevel * 0.4; // Faster with level
asteroids.push(deadlyAsteroid);
game.addChild(deadlyAsteroid);
} else {
var asteroid = new Asteroid();
asteroid.x = Math.random() * (2048 - 200) + 100;
asteroid.y = -100;
asteroid.speed = 3 + gameLevel * 0.3 * difficultyMultiplier; // Scale with difficulty multiplier
asteroids.push(asteroid);
game.addChild(asteroid);
}
}
// Spawn power-up
function spawnPowerUp() {
// Only spawn powerup based on chance - gets rarer with higher levels
if (Math.random() < powerupChance - gameLevel * 0.05) {
var powerup = new PowerUp();
powerup.x = Math.random() * (2048 - 200) + 100;
powerup.y = -100;
powerups.push(powerup);
game.addChild(powerup);
}
}
// Spawn oil food
function spawnOilFood() {
var oilFood = new OilFood();
oilFood.x = Math.random() * (2048 - 200) + 100;
oilFood.y = -100;
oilFoods.push(oilFood);
game.addChild(oilFood);
}
// Spawn bad oil
function spawnBadOil() {
var badOil = new BadOil();
badOil.x = Math.random() * (2048 - 200) + 100;
badOil.y = -100;
oilFoods.push(badOil);
game.addChild(badOil);
}
// Spawn new enemy
function spawnNewEnemy() {
var newEnemy = new NewEnemy();
newEnemy.x = Math.random() * (2048 - 200) + 100;
newEnemy.y = -100;
newEnemy.speed = 3 + gameLevel * 0.5; // Increase speed with level
enemies.push(newEnemy);
game.addChild(newEnemy);
}
// Spawn boss enemy
function spawnBossEnemy() {
// At level 10, spawn Earth-monster instead of regular boss
var boss;
if (gameLevel >= 10) {
boss = new EarthMonster();
// Create a special alert for Earth-monster
var bossAlert = new Text2('EARTH-MONSTER INCOMING!', {
size: 100,
fill: 0x00FFFF
});
} else {
boss = new BossEnemy();
// Create a regular boss alert
var bossAlert = new Text2('BOSS INCOMING!', {
size: 100,
fill: 0xFF0000
});
}
boss.x = 2048 / 2;
boss.y = -200;
bossEnemies.push(boss);
game.addChild(boss);
bossAlert.anchor.set(0.5, 0.5);
LK.gui.center.addChild(bossAlert);
// Fade out the alert after 2 seconds
LK.setTimeout(function () {
tween(bossAlert, {
alpha: 0
}, {
duration: 1000,
onComplete: function onComplete() {
bossAlert.destroy();
}
});
}, 2000);
}
// Fire player bullet
function firePlayerBullet() {
if (player && player.canFire()) {
var bullet = new PlayerBullet();
bullet.x = player.x;
bullet.y = player.y - 50;
playerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('laser').play();
}
}
// Fire enemy bullet
function fireEnemyBullet(enemy) {
// Check if enemy is Earth-monster to create special bullets
if (enemy instanceof EarthMonster) {
// Earth-monster fires 5 bullets at once in a spread pattern
for (var i = 0; i < 5; i++) {
var bullet = new EarthMonsterBullet();
// Position bullets in a spread pattern
bullet.x = enemy.x + (i - 2) * 50; // -100, -50, 0, 50, 100 offset
bullet.y = enemy.y + 50;
enemyBullets.push(bullet);
game.addChild(bullet);
}
} else {
var bullet = new EnemyBullet();
bullet.x = enemy.x;
bullet.y = enemy.y + 50;
enemyBullets.push(bullet);
game.addChild(bullet);
}
}
// Update score
function updateScore(points) {
scoreValue += points;
LK.setScore(scoreValue);
scoreTxt.setText('Score: ' + scoreValue);
}
// Check level progression
function checkLevelProgress() {
if (scoreValue >= gameLevel * 100) {
gameLevel++;
levelTxt.setText('Level: ' + gameLevel);
// Increase difficulty multiplier
difficultyMultiplier = 1.0 + gameLevel * 0.15;
// Make game more difficult
enemySpawnRate = Math.max(60, Math.floor(240 / difficultyMultiplier));
asteroidSpawnRate = Math.max(45, Math.floor(90 / difficultyMultiplier));
// Decrease oil food spawn rate
oilFoodSpawnRate = Math.min(600, Math.floor(360 * (1 + gameLevel * 0.1)));
// Make enemies faster and stronger via game update loop
// Show level up message
var levelUpTxt = new Text2('LEVEL ' + gameLevel + '!', {
size: 100,
fill: 0xFFFF00
});
levelUpTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(levelUpTxt);
// Fade out the message after 1.5 seconds
LK.setTimeout(function () {
tween(levelUpTxt, {
alpha: 0
}, {
duration: 800,
onComplete: function onComplete() {
levelUpTxt.destroy();
}
});
}, 1500);
}
}
// Drag handling
var isDragging = false;
function handleMove(x, y, obj) {
if (isDragging && player) {
// Constrain player to screen bounds
player.x = Math.max(50, Math.min(2048 - 50, x));
player.y = Math.max(50, Math.min(2732 - 50, y));
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
isDragging = true;
handleMove(x, y, obj);
// Auto-fire when touching
firePlayerBullet();
};
game.up = function (x, y, obj) {
isDragging = false;
};
// Main game loop
game.update = function () {
if (gameState !== "playing") {
return;
}
// Spawn enemies - spawn multiple based on level
if (LK.ticks % enemySpawnRate === 0) {
// At higher levels, spawn multiple enemies at once
var enemiesToSpawn = 1 + Math.floor(gameLevel / 3);
for (var i = 0; i < enemiesToSpawn && i < 4; i++) {
// Cap at 4 enemies per spawn
spawnEnemy();
}
}
// Spawn asteroids - more frequent at higher levels
if (LK.ticks % asteroidSpawnRate === 0) {
// At higher levels, spawn multiple asteroids
var asteroidsToSpawn = 1 + Math.floor(gameLevel / 4);
for (var i = 0; i < asteroidsToSpawn && i < 3; i++) {
// Cap at 3 asteroids per spawn
spawnAsteroid();
}
}
// Spawn powerups - rare chance based on level
if (LK.ticks % powerupSpawnRate === 0) {
spawnPowerUp();
}
// Spawn oil food - less frequent at higher levels
if (LK.ticks % oilFoodSpawnRate === 0) {
spawnOilFood();
}
// Spawn bad oil - more frequent at higher levels
if (LK.ticks % Math.max(180, 360 - gameLevel * 15) === 0) {
spawnBadOil();
}
// Spawn boss enemies at level 5+ (earlier) and more frequently at level 10
if (gameLevel >= 5 && gameLevel < 10 && LK.ticks % bossSpawnRate === 0 && bossEnemies.length < 1 || gameLevel >= 10 && LK.ticks % (bossSpawnRate / 2) === 0 && bossEnemies.length < 1) {
spawnBossEnemy();
}
// Spawn new enemies at level 3 (earlier)
if (gameLevel >= 3 && LK.ticks % Math.max(60, newEnemySpawnRate - gameLevel * 10) === 0) {
spawnNewEnemy();
}
// Auto fire for enemies, not asteroids
if (player && LK.ticks % 15 === 0) {
firePlayerBullet();
}
// Update player bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
// Remove bullets that go off screen
if (bullet.y < -50) {
bullet.destroy();
playerBullets.splice(i, 1);
continue;
}
// Check collisions with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
// Destroy enemy and bullet
updateScore(enemy.scoreValue);
LK.effects.flashObject(enemy, 0xff0000, 200);
LK.getSound('explosion').play();
enemy.destroy();
enemies.splice(j, 1);
bullet.destroy();
playerBullets.splice(i, 1);
checkLevelProgress();
break;
}
}
// Check collisions with boss enemies
if (playerBullets[i]) {
for (var j = bossEnemies.length - 1; j >= 0; j--) {
var boss = bossEnemies[j];
if (bullet.intersects(boss)) {
// Damage boss
boss.health--;
LK.effects.flashObject(boss, 0xff0000, 200);
// Destroy bullet
bullet.destroy();
playerBullets.splice(i, 1);
// Check if boss is defeated
if (boss.health <= 0) {
updateScore(boss.scoreValue);
LK.getSound('explosion').play();
boss.destroy();
bossEnemies.splice(j, 1);
checkLevelProgress();
// If it's Earth-monster, player wins
if (boss instanceof EarthMonster) {
// Display victory message
var victoryTxt = new Text2('YOU DEFEATED THE EARTH-MONSTER!', {
size: 80,
fill: 0x00FF00
});
victoryTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(victoryTxt);
// Game won
gameState = "won";
LK.effects.flashScreen(0x00FF00, 1000);
LK.setTimeout(function () {
LK.showYouWin();
}, 2000);
}
}
break;
}
}
}
// Check bullet collisions with asteroids
if (playerBullets[i]) {
for (var k = asteroids.length - 1; k >= 0; k--) {
if (playerBullets[i].intersects(asteroids[k])) {
// Destroy only the bullet when it hits an asteroid
LK.effects.flashObject(playerBullets[i], 0xff0000, 200);
playerBullets[i].destroy();
playerBullets.splice(i, 1);
break;
}
}
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
// Remove bullets that go off screen
if (bullet.y > 2732 + 50) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check collisions with player
if (player && bullet.intersects(player)) {
// Check if it's an Earth-monster bullet for 3x damage
if (bullet instanceof EarthMonsterBullet) {
// Apply damage three times for Earth-monster bullets
for (var dmg = 0; dmg < 3; dmg++) {
var hitSuccess = player.hit();
}
LK.effects.flashObject(player, 0x00ffff, 700); // Special effect for Earth-monster hit
} else {
// Regular bullet damage
var hitSuccess = player.hit();
}
// Destroy bullet
bullet.destroy();
enemyBullets.splice(i, 1);
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
// Remove enemies that go off screen
if (enemy.y > 2732 + 50) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
// Enemy shooting
if (Math.random() < enemy.shootChance) {
fireEnemyBullet(enemy);
}
// Check collisions with player
if (player && enemy.intersects(player)) {
// Player takes damage
var hitSuccess = player.hit();
// New collision behavior - check if it's a NewEnemy
if (enemy instanceof NewEnemy) {
// Always lose one heart for NewEnemy
currentHearts--;
updateHearts();
}
// Destroy enemy
enemy.destroy();
enemies.splice(i, 1);
LK.getSound('explosion').play();
}
}
// Update boss enemies
for (var i = bossEnemies.length - 1; i >= 0; i--) {
var boss = bossEnemies[i];
// Remove boss that go off screen
if (boss.y > 2732 + 50) {
boss.destroy();
bossEnemies.splice(i, 1);
continue;
}
// Boss shooting (more frequent)
if (Math.random() < boss.shootChance) {
fireEnemyBullet(boss);
// Boss shoots additional bullets in different directions
if (Math.random() < 0.5) {
var bulletLeft = new EnemyBullet();
bulletLeft.x = boss.x - 50;
bulletLeft.y = boss.y + 50;
enemyBullets.push(bulletLeft);
game.addChild(bulletLeft);
var bulletRight = new EnemyBullet();
bulletRight.x = boss.x + 50;
bulletRight.y = boss.y + 50;
enemyBullets.push(bulletRight);
game.addChild(bulletRight);
}
}
// Check collisions with player
if (player && boss.intersects(player)) {
// Player always takes damage from boss collision
var hitSuccess = player.hit();
// Boss is not destroyed on collision, just damaged
boss.health--;
LK.effects.flashObject(boss, 0xff0000, 200);
if (boss.health <= 0) {
updateScore(boss.scoreValue);
boss.destroy();
bossEnemies.splice(i, 1);
LK.getSound('explosion').play();
}
}
}
// Update asteroids
for (var i = asteroids.length - 1; i >= 0; i--) {
var asteroid = asteroids[i];
// Track if asteroid just passed the player
if (asteroid.lastY < player.y - 100 && asteroid.y >= player.y - 100) {
// Award points for successful dodge
updateScore(10);
checkLevelProgress();
}
// Remove asteroids that go off screen
if (asteroid.y > 2732 + 50) {
asteroid.destroy();
asteroids.splice(i, 1);
continue;
}
// Check collisions with player
if (player && asteroid.intersects(player)) {
// Check if it's a new deadly asteroid
if (asteroid instanceof NewAsteroid) {
// Game over immediately
gameState = "over";
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
} else {
// Regular asteroid - player takes damage
var hitSuccess = player.hit();
}
// Destroy asteroid
asteroid.destroy();
asteroids.splice(i, 1);
LK.getSound('explosion').play();
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
// Remove powerups that go off screen
if (powerup.y > 2732 + 50) {
powerup.destroy();
powerups.splice(i, 1);
continue;
}
// Check collisions with player
if (player && powerup.intersects(player)) {
// Apply powerup effect
if (powerup.type === 0) {
player.activateShield();
} else {
player.activateRapidFire();
}
// Destroy powerup
powerup.destroy();
powerups.splice(i, 1);
LK.getSound('powerupCollect').play();
updateScore(25); // Bonus points for collecting powerup
}
}
// Update oil foods
for (var i = oilFoods.length - 1; i >= 0; i--) {
var oilFood = oilFoods[i];
// Remove oil foods that go off screen
if (oilFood.y > 2732 + 50) {
oilFood.destroy();
oilFoods.splice(i, 1);
continue;
}
// Check collisions with player
if (player && oilFood.intersects(player)) {
// Check if it's a bad oil or good oil food
if (oilFood instanceof BadOil) {
// Decrease oil percentage by 10%
oilPercentage -= 10;
// Flash red to indicate bad effect
LK.effects.flashObject(player, 0xff0000, 500);
} else {
// Increase oil percentage by 1%
oilPercentage += 1;
// Cap oil percentage at max value
if (oilPercentage > maxOilPercentage) {
oilPercentage = maxOilPercentage;
}
}
oilTxt.setText('Oil: ' + oilPercentage + '/' + maxOilPercentage);
// If player has no oil, enemies win
if (oilPercentage <= 0) {
// Display enemies win message
var enemiesWinTxt = new Text2('ENEMIES WIN!', {
size: 100,
fill: 0xFF0000
});
enemiesWinTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(enemiesWinTxt);
// Game over
gameState = "over";
LK.effects.flashScreen(0xff0000, 1000);
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
}
// Destroy oil food
oilFood.destroy();
oilFoods.splice(i, 1);
}
}
};
// Create instructions text
var instructionsTxt = new Text2('DODGE THE ASTEROIDS TO SURVIVE!', {
size: 80,
fill: 0xFFFFFF
});
instructionsTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(instructionsTxt);
instructionsTxt.y = -150;
// Hide instructions after 5 seconds
LK.setTimeout(function () {
tween(instructionsTxt, {
alpha: 0
}, {
duration: 1000
});
}, 5000);
// Initialize the game
initializePlayer();
// Set initial oil text
oilTxt.setText('Oil: ' + oilPercentage + '/' + maxOilPercentage);
// Play background music
LK.playMusic('bgmusic');
; /****
* 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 asteroidGraphics = self.attachAsset('asteroid', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
self.speed = 6;
self.rotationSpeed = Math.random() * 0.05 - 0.025;
self.scoreValue = 5;
self.lastY = -100; // Track last position for collision detection
self.update = function () {
self.lastY = self.y;
self.y += self.speed;
self.rotation += self.rotationSpeed;
// Add side-to-side movement to make dodging more challenging
self.x += Math.sin(LK.ticks * 0.02) * 2;
};
return self;
});
var BadOil = Container.expand(function () {
var self = Container.call(this);
var badOilGraphics = self.attachAsset('Bad-oil', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
self.speed = 5;
self.lastY = -100;
self.update = function () {
self.lastY = self.y;
self.y += self.speed;
// Add some side movement
self.x += Math.sin(LK.ticks * 0.03) * 2;
};
return self;
});
var BossEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.5,
tint: 0xff0000
});
self.speed = 2;
self.health = 5;
self.moveDirection = 1; // 1 for right, -1 for left
self.shootChance = 0.03; // Higher chance to shoot than regular enemies
self.scoreValue = 50;
self.lastY = -100;
self.update = function () {
self.lastY = self.y;
// Move down more slowly
self.y += self.speed;
// Move in a more complex pattern
self.x += self.moveDirection * 3 * Math.sin(LK.ticks * 0.02);
// Change direction at edges
if (self.x < 200 || self.x > 2048 - 200) {
self.moveDirection *= -1;
}
};
return self;
});
var EarthMonster = Container.expand(function () {
var self = Container.call(this);
var monsterGraphics = self.attachAsset('Earth-monster', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.0,
scaleY: 3.0
});
self.speed = 1.5;
self.health = 20; // Earth-monster has 20 hearts
self.moveDirection = 1; // 1 for right, -1 for left
self.shootChance = 0.05; // Higher chance to shoot than other enemies
self.scoreValue = 200;
self.lastY = -100;
self.update = function () {
self.lastY = self.y;
// Move down more slowly
self.y += self.speed;
// Move in a complex pattern
self.x += self.moveDirection * 2 * Math.sin(LK.ticks * 0.03);
// Change direction at edges
if (self.x < 300 || self.x > 2048 - 300) {
self.moveDirection *= -1;
}
};
return self;
});
var EarthMonsterBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
tint: 0x00ffff
});
self.speed = 12; // Faster than regular enemy bullets
self.damage = 3; // Does 3x damage
self.update = function () {
self.y += self.speed;
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.moveDirection = 1; // 1 for right, -1 for left
self.shootChance = 0.01; // Chance to shoot per update
self.scoreValue = 10;
self.update = function () {
// Move down
self.y += self.speed;
// Move side to side
self.x += self.moveDirection * 2;
// Change direction at edges
if (self.x < 100 || self.x > 2048 - 100) {
self.moveDirection *= -1;
}
};
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.update = function () {
self.y += self.speed;
};
return self;
});
var Heart = Container.expand(function () {
var self = Container.call(this);
var heartGraphics = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xff0000,
scaleX: 0.8,
scaleY: 0.8
});
self.setActive = function (active) {
heartGraphics.alpha = active ? 1 : 0.3;
};
return self;
});
var NewAsteroid = Container.expand(function () {
var self = Container.call(this);
var asteroidGraphics = self.attachAsset('New-ast', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
self.speed = 7; // Faster than regular asteroid
self.rotationSpeed = Math.random() * 0.08 - 0.04; // Faster rotation
self.scoreValue = 0; // No points for this one since it's deadly
self.lastY = -100; // Track last position for collision detection
self.update = function () {
self.lastY = self.y;
self.y += self.speed;
self.rotation += self.rotationSpeed;
// More erratic movement pattern
self.x += Math.sin(LK.ticks * 0.03) * 3;
};
return self;
});
var NewEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('New-enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4; // Faster than regular enemy
self.moveDirection = 1; // 1 for right, -1 for left
self.shootChance = 0.02; // Higher chance to shoot than regular enemies
self.scoreValue = 20; // Worth more points than regular enemies
self.update = function () {
// Only move left and right, no vertical movement
// Move side to side more aggressively
self.x += self.moveDirection * 3;
// Change direction at edges
if (self.x < 100 || self.x > 2048 - 100) {
self.moveDirection *= -1;
}
};
return self;
});
var OilFood = Container.expand(function () {
var self = Container.call(this);
var oilFoodGraphics = self.attachAsset('Oil-food', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
self.speed = 5;
self.lastY = -100;
self.update = function () {
self.lastY = self.y;
self.y += self.speed;
// Add some side movement
self.x += Math.sin(LK.ticks * 0.03) * 2;
};
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.update = function () {
self.y += self.speed;
};
return self;
});
var PlayerShip = Container.expand(function () {
var self = Container.call(this);
var shipGraphics = self.attachAsset('playerShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.shield = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.hasShield = false;
self.fireRate = 20; // Time between shots in ticks
self.fireCooldown = 0;
self.rapidFire = false;
self.rapidFireTimer = 0;
self.lives = 3; // Keep this for backward compatibility
self.activateShield = function () {
self.hasShield = true;
self.shield.alpha = 0.5;
};
self.deactivateShield = function () {
self.hasShield = false;
self.shield.alpha = 0;
};
self.activateRapidFire = function () {
self.rapidFire = true;
self.rapidFireTimer = 300; // 5 seconds at 60 FPS
};
self.canFire = function () {
if (self.fireCooldown <= 0) {
self.fireCooldown = self.rapidFire ? Math.floor(self.fireRate / 2) : self.fireRate;
return true;
}
return false;
};
self.hit = function () {
if (self.hasShield) {
self.deactivateShield();
return false; // Shield absorbed the hit
} else {
// Use the hearts system instead of lives
if (currentHearts > 0) {
currentHearts--;
updateHearts();
LK.effects.flashObject(self, 0xff0000, 500);
return true; // Ship took damage
}
return false;
}
};
self.update = function () {
if (self.fireCooldown > 0) {
self.fireCooldown--;
}
if (self.rapidFire && self.rapidFireTimer > 0) {
self.rapidFireTimer--;
if (self.rapidFireTimer <= 0) {
self.rapidFire = false;
}
}
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerupGraphics = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.type = Math.floor(Math.random() * 2); // 0 for shield, 1 for rapid fire
// Set color based on type
if (self.type === 0) {
powerupGraphics.tint = 0x3498db; // Blue for shield
} else {
powerupGraphics.tint = 0xf39c12; // Orange for rapid fire
}
self.update = function () {
self.y += self.speed;
// Floating animation
self.x += Math.sin(LK.ticks * 0.05) * 1;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Save the current game state
function saveGame() {
storage.set('saveSpace_level', gameLevel);
storage.set('saveSpace_score', scoreValue);
storage.set('saveSpace_hearts', currentHearts);
storage.set('saveSpace_oil', oilPercentage);
}
// Load the saved game state
function loadGame() {
var savedLevel = storage.get('saveSpace_level', 1);
var savedScore = storage.get('saveSpace_score', 0);
var savedHearts = storage.get('saveSpace_hearts', 10);
var savedOil = storage.get('saveSpace_oil', 20);
gameLevel = savedLevel;
scoreValue = savedScore;
currentHearts = savedHearts;
oilPercentage = savedOil;
// Update UI elements
levelTxt.setText('Level: ' + gameLevel);
scoreTxt.setText('Score: ' + scoreValue);
oilTxt.setText('Oil: ' + oilPercentage + '/' + maxOilPercentage);
updateHearts();
// Set game difficulty based on loaded level
difficultyMultiplier = 1.0 + gameLevel * 0.15;
enemySpawnRate = Math.max(60, Math.floor(240 / difficultyMultiplier));
asteroidSpawnRate = Math.max(45, Math.floor(90 / difficultyMultiplier));
oilFoodSpawnRate = Math.min(600, Math.floor(360 * (1 + gameLevel * 0.1)));
}
// Auto-save every minute
LK.setInterval(saveGame, 60000);
// Save when player takes damage
var originalHit = PlayerShip.prototype.hit;
PlayerShip.prototype.hit = function () {
var result = originalHit.apply(this, arguments);
if (result) {
saveGame();
}
return result;
};
// Save on level up
var originalCheckLevelProgress = checkLevelProgress;
checkLevelProgress = function checkLevelProgress() {
var oldLevel = gameLevel;
originalCheckLevelProgress.apply(this, arguments);
if (gameLevel > oldLevel) {
saveGame();
}
};
// Try to load saved game at start
loadGame();
game.setBackgroundColor(0x0c1445);
// Game variables
var player;
var playerBullets = [];
var enemies = [];
var bossEnemies = []; // Track boss enemies separately
var enemyBullets = [];
var asteroids = [];
var powerups = [];
var hearts = [];
var oilFoods = []; // Array to track oil foods
var heartsContainer;
var maxHearts = 10;
var currentHearts = 10;
var gameLevel = 1;
var enemySpawnRate = 240; // Reduced enemy spawn rate to focus on dodging asteroids
var asteroidSpawnRate = 90; // Increased asteroid spawn rate to create dodging challenge
var powerupSpawnRate = 900; // Every 15 seconds - rarer powerups
var bossSpawnRate = 600; // How often to check for boss spawn when at level 10
var newEnemySpawnRate = 180; // More frequent than regular enemies
var oilFoodSpawnRate = 360; // Every 6 seconds - slightly rarer
var gameState = "playing";
var scoreValue = 0;
var oilPercentage = 20; // Track oil percentage, starting at 20 out of 40
var maxOilPercentage = 40; // Maximum oil percentage
var powerupChance = 0.7; // 70% chance to actually spawn a powerup when the timer triggers
var difficultyMultiplier = 1.0; // Base difficulty multiplier that increases with level
// Setup score display
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
scoreTxt.x = -300; // Offset from right edge
// Hearts display - text version as fallback
var livesTxt = new Text2('Hearts: 10/10', {
size: 60,
fill: 0xFFFFFF
});
livesTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(livesTxt);
livesTxt.x = -300;
livesTxt.y = 70; // Below score text
// Function to update hearts display
function updateHearts() {
for (var i = 0; i < hearts.length; i++) {
hearts[i].setActive(i < currentHearts);
}
livesTxt.setText('Hearts: ' + currentHearts + '/' + maxHearts);
// Game over condition
if (currentHearts <= 0) {
gameState = "over";
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
}
// Level display
var levelTxt = new Text2('Level: 1', {
size: 60,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(levelTxt);
levelTxt.x = -300;
levelTxt.y = 140; // Below lives text
// Oil percentage display
var oilTxt = new Text2('Oil: 20/40', {
size: 60,
fill: 0xFFFFFF
});
oilTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(oilTxt);
oilTxt.x = -300;
oilTxt.y = 210; // Below level text
// Initialize player
function initializePlayer() {
player = new PlayerShip();
player.x = 2048 / 2;
player.y = 2732 - 200;
game.addChild(player);
// Initialize hearts display
initializeHearts();
}
// Initialize hearts display
function initializeHearts() {
// Remove existing hearts if any
if (heartsContainer) {
heartsContainer.destroy();
hearts = [];
}
// Create container for hearts
heartsContainer = new Container();
game.addChild(heartsContainer);
heartsContainer.x = 100;
heartsContainer.y = 100;
// Create hearts
currentHearts = maxHearts;
for (var i = 0; i < maxHearts; i++) {
var heart = new Heart();
heart.x = i % 5 * 40;
heart.y = Math.floor(i / 5) * 40;
heart.setActive(true);
hearts.push(heart);
heartsContainer.addChild(heart);
}
}
// Spawn enemy
function spawnEnemy() {
var enemy = new Enemy();
enemy.x = Math.random() * (2048 - 200) + 100;
enemy.y = -100;
enemy.speed = 2 + gameLevel * 0.5 * difficultyMultiplier; // Scale with difficulty multiplier
enemy.shootChance = Math.min(0.05, 0.01 + gameLevel * 0.004); // Increase shoot chance with level
enemy.health = 1 + Math.floor(gameLevel / 5); // Tougher enemies at higher levels
enemies.push(enemy);
game.addChild(enemy);
}
// Spawn asteroid
function spawnAsteroid() {
// At level 2 and above, randomly spawn the deadly asteroid
// Chance increases with level
if (gameLevel >= 2 && Math.random() < 0.2 + gameLevel * 0.03) {
var deadlyAsteroid = new NewAsteroid();
deadlyAsteroid.x = Math.random() * (2048 - 200) + 100;
deadlyAsteroid.y = -100;
deadlyAsteroid.speed = 7 + gameLevel * 0.4; // Faster with level
asteroids.push(deadlyAsteroid);
game.addChild(deadlyAsteroid);
} else {
var asteroid = new Asteroid();
asteroid.x = Math.random() * (2048 - 200) + 100;
asteroid.y = -100;
asteroid.speed = 3 + gameLevel * 0.3 * difficultyMultiplier; // Scale with difficulty multiplier
asteroids.push(asteroid);
game.addChild(asteroid);
}
}
// Spawn power-up
function spawnPowerUp() {
// Only spawn powerup based on chance - gets rarer with higher levels
if (Math.random() < powerupChance - gameLevel * 0.05) {
var powerup = new PowerUp();
powerup.x = Math.random() * (2048 - 200) + 100;
powerup.y = -100;
powerups.push(powerup);
game.addChild(powerup);
}
}
// Spawn oil food
function spawnOilFood() {
var oilFood = new OilFood();
oilFood.x = Math.random() * (2048 - 200) + 100;
oilFood.y = -100;
oilFoods.push(oilFood);
game.addChild(oilFood);
}
// Spawn bad oil
function spawnBadOil() {
var badOil = new BadOil();
badOil.x = Math.random() * (2048 - 200) + 100;
badOil.y = -100;
oilFoods.push(badOil);
game.addChild(badOil);
}
// Spawn new enemy
function spawnNewEnemy() {
var newEnemy = new NewEnemy();
newEnemy.x = Math.random() * (2048 - 200) + 100;
newEnemy.y = -100;
newEnemy.speed = 3 + gameLevel * 0.5; // Increase speed with level
enemies.push(newEnemy);
game.addChild(newEnemy);
}
// Spawn boss enemy
function spawnBossEnemy() {
// At level 10, spawn Earth-monster instead of regular boss
var boss;
if (gameLevel >= 10) {
boss = new EarthMonster();
// Create a special alert for Earth-monster
var bossAlert = new Text2('EARTH-MONSTER INCOMING!', {
size: 100,
fill: 0x00FFFF
});
} else {
boss = new BossEnemy();
// Create a regular boss alert
var bossAlert = new Text2('BOSS INCOMING!', {
size: 100,
fill: 0xFF0000
});
}
boss.x = 2048 / 2;
boss.y = -200;
bossEnemies.push(boss);
game.addChild(boss);
bossAlert.anchor.set(0.5, 0.5);
LK.gui.center.addChild(bossAlert);
// Fade out the alert after 2 seconds
LK.setTimeout(function () {
tween(bossAlert, {
alpha: 0
}, {
duration: 1000,
onComplete: function onComplete() {
bossAlert.destroy();
}
});
}, 2000);
}
// Fire player bullet
function firePlayerBullet() {
if (player && player.canFire()) {
var bullet = new PlayerBullet();
bullet.x = player.x;
bullet.y = player.y - 50;
playerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('laser').play();
}
}
// Fire enemy bullet
function fireEnemyBullet(enemy) {
// Check if enemy is Earth-monster to create special bullets
if (enemy instanceof EarthMonster) {
// Earth-monster fires 5 bullets at once in a spread pattern
for (var i = 0; i < 5; i++) {
var bullet = new EarthMonsterBullet();
// Position bullets in a spread pattern
bullet.x = enemy.x + (i - 2) * 50; // -100, -50, 0, 50, 100 offset
bullet.y = enemy.y + 50;
enemyBullets.push(bullet);
game.addChild(bullet);
}
} else {
var bullet = new EnemyBullet();
bullet.x = enemy.x;
bullet.y = enemy.y + 50;
enemyBullets.push(bullet);
game.addChild(bullet);
}
}
// Update score
function updateScore(points) {
scoreValue += points;
LK.setScore(scoreValue);
scoreTxt.setText('Score: ' + scoreValue);
}
// Check level progression
function checkLevelProgress() {
if (scoreValue >= gameLevel * 100) {
gameLevel++;
levelTxt.setText('Level: ' + gameLevel);
// Increase difficulty multiplier
difficultyMultiplier = 1.0 + gameLevel * 0.15;
// Make game more difficult
enemySpawnRate = Math.max(60, Math.floor(240 / difficultyMultiplier));
asteroidSpawnRate = Math.max(45, Math.floor(90 / difficultyMultiplier));
// Decrease oil food spawn rate
oilFoodSpawnRate = Math.min(600, Math.floor(360 * (1 + gameLevel * 0.1)));
// Make enemies faster and stronger via game update loop
// Show level up message
var levelUpTxt = new Text2('LEVEL ' + gameLevel + '!', {
size: 100,
fill: 0xFFFF00
});
levelUpTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(levelUpTxt);
// Fade out the message after 1.5 seconds
LK.setTimeout(function () {
tween(levelUpTxt, {
alpha: 0
}, {
duration: 800,
onComplete: function onComplete() {
levelUpTxt.destroy();
}
});
}, 1500);
}
}
// Drag handling
var isDragging = false;
function handleMove(x, y, obj) {
if (isDragging && player) {
// Constrain player to screen bounds
player.x = Math.max(50, Math.min(2048 - 50, x));
player.y = Math.max(50, Math.min(2732 - 50, y));
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
isDragging = true;
handleMove(x, y, obj);
// Auto-fire when touching
firePlayerBullet();
};
game.up = function (x, y, obj) {
isDragging = false;
};
// Main game loop
game.update = function () {
if (gameState !== "playing") {
return;
}
// Spawn enemies - spawn multiple based on level
if (LK.ticks % enemySpawnRate === 0) {
// At higher levels, spawn multiple enemies at once
var enemiesToSpawn = 1 + Math.floor(gameLevel / 3);
for (var i = 0; i < enemiesToSpawn && i < 4; i++) {
// Cap at 4 enemies per spawn
spawnEnemy();
}
}
// Spawn asteroids - more frequent at higher levels
if (LK.ticks % asteroidSpawnRate === 0) {
// At higher levels, spawn multiple asteroids
var asteroidsToSpawn = 1 + Math.floor(gameLevel / 4);
for (var i = 0; i < asteroidsToSpawn && i < 3; i++) {
// Cap at 3 asteroids per spawn
spawnAsteroid();
}
}
// Spawn powerups - rare chance based on level
if (LK.ticks % powerupSpawnRate === 0) {
spawnPowerUp();
}
// Spawn oil food - less frequent at higher levels
if (LK.ticks % oilFoodSpawnRate === 0) {
spawnOilFood();
}
// Spawn bad oil - more frequent at higher levels
if (LK.ticks % Math.max(180, 360 - gameLevel * 15) === 0) {
spawnBadOil();
}
// Spawn boss enemies at level 5+ (earlier) and more frequently at level 10
if (gameLevel >= 5 && gameLevel < 10 && LK.ticks % bossSpawnRate === 0 && bossEnemies.length < 1 || gameLevel >= 10 && LK.ticks % (bossSpawnRate / 2) === 0 && bossEnemies.length < 1) {
spawnBossEnemy();
}
// Spawn new enemies at level 3 (earlier)
if (gameLevel >= 3 && LK.ticks % Math.max(60, newEnemySpawnRate - gameLevel * 10) === 0) {
spawnNewEnemy();
}
// Auto fire for enemies, not asteroids
if (player && LK.ticks % 15 === 0) {
firePlayerBullet();
}
// Update player bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
// Remove bullets that go off screen
if (bullet.y < -50) {
bullet.destroy();
playerBullets.splice(i, 1);
continue;
}
// Check collisions with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
// Destroy enemy and bullet
updateScore(enemy.scoreValue);
LK.effects.flashObject(enemy, 0xff0000, 200);
LK.getSound('explosion').play();
enemy.destroy();
enemies.splice(j, 1);
bullet.destroy();
playerBullets.splice(i, 1);
checkLevelProgress();
break;
}
}
// Check collisions with boss enemies
if (playerBullets[i]) {
for (var j = bossEnemies.length - 1; j >= 0; j--) {
var boss = bossEnemies[j];
if (bullet.intersects(boss)) {
// Damage boss
boss.health--;
LK.effects.flashObject(boss, 0xff0000, 200);
// Destroy bullet
bullet.destroy();
playerBullets.splice(i, 1);
// Check if boss is defeated
if (boss.health <= 0) {
updateScore(boss.scoreValue);
LK.getSound('explosion').play();
boss.destroy();
bossEnemies.splice(j, 1);
checkLevelProgress();
// If it's Earth-monster, player wins
if (boss instanceof EarthMonster) {
// Display victory message
var victoryTxt = new Text2('YOU DEFEATED THE EARTH-MONSTER!', {
size: 80,
fill: 0x00FF00
});
victoryTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(victoryTxt);
// Game won
gameState = "won";
LK.effects.flashScreen(0x00FF00, 1000);
LK.setTimeout(function () {
LK.showYouWin();
}, 2000);
}
}
break;
}
}
}
// Check bullet collisions with asteroids
if (playerBullets[i]) {
for (var k = asteroids.length - 1; k >= 0; k--) {
if (playerBullets[i].intersects(asteroids[k])) {
// Destroy only the bullet when it hits an asteroid
LK.effects.flashObject(playerBullets[i], 0xff0000, 200);
playerBullets[i].destroy();
playerBullets.splice(i, 1);
break;
}
}
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
// Remove bullets that go off screen
if (bullet.y > 2732 + 50) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check collisions with player
if (player && bullet.intersects(player)) {
// Check if it's an Earth-monster bullet for 3x damage
if (bullet instanceof EarthMonsterBullet) {
// Apply damage three times for Earth-monster bullets
for (var dmg = 0; dmg < 3; dmg++) {
var hitSuccess = player.hit();
}
LK.effects.flashObject(player, 0x00ffff, 700); // Special effect for Earth-monster hit
} else {
// Regular bullet damage
var hitSuccess = player.hit();
}
// Destroy bullet
bullet.destroy();
enemyBullets.splice(i, 1);
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
// Remove enemies that go off screen
if (enemy.y > 2732 + 50) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
// Enemy shooting
if (Math.random() < enemy.shootChance) {
fireEnemyBullet(enemy);
}
// Check collisions with player
if (player && enemy.intersects(player)) {
// Player takes damage
var hitSuccess = player.hit();
// New collision behavior - check if it's a NewEnemy
if (enemy instanceof NewEnemy) {
// Always lose one heart for NewEnemy
currentHearts--;
updateHearts();
}
// Destroy enemy
enemy.destroy();
enemies.splice(i, 1);
LK.getSound('explosion').play();
}
}
// Update boss enemies
for (var i = bossEnemies.length - 1; i >= 0; i--) {
var boss = bossEnemies[i];
// Remove boss that go off screen
if (boss.y > 2732 + 50) {
boss.destroy();
bossEnemies.splice(i, 1);
continue;
}
// Boss shooting (more frequent)
if (Math.random() < boss.shootChance) {
fireEnemyBullet(boss);
// Boss shoots additional bullets in different directions
if (Math.random() < 0.5) {
var bulletLeft = new EnemyBullet();
bulletLeft.x = boss.x - 50;
bulletLeft.y = boss.y + 50;
enemyBullets.push(bulletLeft);
game.addChild(bulletLeft);
var bulletRight = new EnemyBullet();
bulletRight.x = boss.x + 50;
bulletRight.y = boss.y + 50;
enemyBullets.push(bulletRight);
game.addChild(bulletRight);
}
}
// Check collisions with player
if (player && boss.intersects(player)) {
// Player always takes damage from boss collision
var hitSuccess = player.hit();
// Boss is not destroyed on collision, just damaged
boss.health--;
LK.effects.flashObject(boss, 0xff0000, 200);
if (boss.health <= 0) {
updateScore(boss.scoreValue);
boss.destroy();
bossEnemies.splice(i, 1);
LK.getSound('explosion').play();
}
}
}
// Update asteroids
for (var i = asteroids.length - 1; i >= 0; i--) {
var asteroid = asteroids[i];
// Track if asteroid just passed the player
if (asteroid.lastY < player.y - 100 && asteroid.y >= player.y - 100) {
// Award points for successful dodge
updateScore(10);
checkLevelProgress();
}
// Remove asteroids that go off screen
if (asteroid.y > 2732 + 50) {
asteroid.destroy();
asteroids.splice(i, 1);
continue;
}
// Check collisions with player
if (player && asteroid.intersects(player)) {
// Check if it's a new deadly asteroid
if (asteroid instanceof NewAsteroid) {
// Game over immediately
gameState = "over";
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
} else {
// Regular asteroid - player takes damage
var hitSuccess = player.hit();
}
// Destroy asteroid
asteroid.destroy();
asteroids.splice(i, 1);
LK.getSound('explosion').play();
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
// Remove powerups that go off screen
if (powerup.y > 2732 + 50) {
powerup.destroy();
powerups.splice(i, 1);
continue;
}
// Check collisions with player
if (player && powerup.intersects(player)) {
// Apply powerup effect
if (powerup.type === 0) {
player.activateShield();
} else {
player.activateRapidFire();
}
// Destroy powerup
powerup.destroy();
powerups.splice(i, 1);
LK.getSound('powerupCollect').play();
updateScore(25); // Bonus points for collecting powerup
}
}
// Update oil foods
for (var i = oilFoods.length - 1; i >= 0; i--) {
var oilFood = oilFoods[i];
// Remove oil foods that go off screen
if (oilFood.y > 2732 + 50) {
oilFood.destroy();
oilFoods.splice(i, 1);
continue;
}
// Check collisions with player
if (player && oilFood.intersects(player)) {
// Check if it's a bad oil or good oil food
if (oilFood instanceof BadOil) {
// Decrease oil percentage by 10%
oilPercentage -= 10;
// Flash red to indicate bad effect
LK.effects.flashObject(player, 0xff0000, 500);
} else {
// Increase oil percentage by 1%
oilPercentage += 1;
// Cap oil percentage at max value
if (oilPercentage > maxOilPercentage) {
oilPercentage = maxOilPercentage;
}
}
oilTxt.setText('Oil: ' + oilPercentage + '/' + maxOilPercentage);
// If player has no oil, enemies win
if (oilPercentage <= 0) {
// Display enemies win message
var enemiesWinTxt = new Text2('ENEMIES WIN!', {
size: 100,
fill: 0xFF0000
});
enemiesWinTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(enemiesWinTxt);
// Game over
gameState = "over";
LK.effects.flashScreen(0xff0000, 1000);
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
}
// Destroy oil food
oilFood.destroy();
oilFoods.splice(i, 1);
}
}
};
// Create instructions text
var instructionsTxt = new Text2('DODGE THE ASTEROIDS TO SURVIVE!', {
size: 80,
fill: 0xFFFFFF
});
instructionsTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(instructionsTxt);
instructionsTxt.y = -150;
// Hide instructions after 5 seconds
LK.setTimeout(function () {
tween(instructionsTxt, {
alpha: 0
}, {
duration: 1000
});
}, 5000);
// Initialize the game
initializePlayer();
// Set initial oil text
oilTxt.setText('Oil: ' + oilPercentage + '/' + maxOilPercentage);
// Play background music
LK.playMusic('bgmusic');
;
Rocky rock. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A space ship with guns. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A spaceship with weapons and four wings. Single Game Texture. In-Game asset. Blank background. High contrast. No shadows
A spaceship with engines at the back and lots of mini spaceships next to the big one. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Rocky green rock with green venom dripping off it. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Black oil dripping out a container. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Red oil floating in space with skulls flowing too. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Super scary earth with sharp teeth. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Energy ball. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows