/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
level: 1
});
/****
* Classes
****/
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = enemyGraphics.width;
self.height = enemyGraphics.height;
self.hp = 1;
self.scoreValue = 100;
self.canShoot = true;
self.movingRight = true;
self.moveSpeed = 2;
self.dropDistance = 40;
self.shoot = function () {
if (!self.canShoot || Math.random() > 0.01) {
return;
} // Random chance to shoot
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + self.height / 2;
game.addChild(bullet);
enemyBullets.push(bullet);
// Cooldown between shots
self.canShoot = false;
LK.setTimeout(function () {
self.canShoot = true;
}, 2000);
};
self.takeDamage = function () {
self.hp--;
if (self.hp <= 0) {
// Update score
LK.setScore(LK.getScore() + self.scoreValue);
scoreTxt.setText(LK.getScore());
// Chance to drop power-up
if (Math.random() < 0.1) {
// 10% chance
var powerUp = new PowerUp();
powerUp.x = self.x;
powerUp.y = self.y;
game.addChild(powerUp);
powerUps.push(powerUp);
}
// Play sound
LK.getSound('enemyDestroyed').play();
// Visual effect
LK.effects.flashObject(self, 0xffff00, 300);
// Remove enemy
self.destroy();
var index = enemies.indexOf(self);
if (index > -1) {
enemies.splice(index, 1);
}
// Check if level complete
if (enemies.length === 0) {
levelComplete();
}
} else {
// Visual effect for damage
LK.effects.flashObject(self, 0xffff00, 200);
}
};
return self;
});
var Boss = Enemy.expand(function () {
var self = Enemy.call(this);
// Replace graphics with boss graphics
self.removeChildAt(0);
var bossGraphics = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = bossGraphics.width;
self.height = bossGraphics.height;
self.hp = 20;
self.scoreValue = 2000;
self.moveSpeed = 3;
self.attackPattern = 0;
self.attackTimer = null;
self.startAttackPattern = function () {
self.attackTimer = LK.setInterval(function () {
self.performAttack();
}, 2000);
};
self.performAttack = function () {
self.attackPattern = (self.attackPattern + 1) % 3;
switch (self.attackPattern) {
case 0:
// Single strong shot
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + self.height / 2;
bullet.speed = 8;
bullet.scale.set(2, 2);
game.addChild(bullet);
enemyBullets.push(bullet);
break;
case 1:
// Multiple shots in a spread
for (var i = -2; i <= 2; i++) {
var bullet = new EnemyBullet();
bullet.x = self.x + i * 40;
bullet.y = self.y + self.height / 2;
game.addChild(bullet);
enemyBullets.push(bullet);
}
break;
case 2:
// Wave pattern of bullets
var interval = LK.setInterval(function () {
var bullet = new EnemyBullet();
bullet.x = self.x + (Math.random() * 200 - 100);
bullet.y = self.y + self.height / 2;
game.addChild(bullet);
enemyBullets.push(bullet);
}, 200);
LK.setTimeout(function () {
LK.clearInterval(interval);
}, 1000);
break;
}
};
var parentTakeDamage = self.takeDamage;
self.takeDamage = function () {
parentTakeDamage.call(self);
// When boss is destroyed, clear the attack timer
if (self.hp <= 0 && self.attackTimer) {
LK.clearInterval(self.attackTimer);
}
};
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 = 6;
self.update = function () {
self.y += self.speed;
// Destroy if out of bounds
if (self.y > 2732 + 50) {
self.destroy();
var index = enemyBullets.indexOf(self);
if (index > -1) {
enemyBullets.splice(index, 1);
}
}
};
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; // Negative because moving upward
self.update = function () {
self.y += self.speed;
// Destroy if out of bounds
if (self.y < -50) {
self.destroy();
var index = bullets.indexOf(self);
if (index > -1) {
bullets.splice(index, 1);
}
}
};
return self;
});
var PlayerShip = Container.expand(function () {
var self = Container.call(this);
var shipGraphics = self.attachAsset('playerShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = shipGraphics.width;
self.height = shipGraphics.height;
self.lives = 3;
self.speed = 15;
self.canShoot = true;
self.shootCooldown = 300; // milliseconds
self.invulnerable = false;
self.powerUpActive = false;
self.startShootCooldown = function () {
self.canShoot = false;
LK.setTimeout(function () {
self.canShoot = true;
}, self.shootCooldown);
};
self.shoot = function () {
if (!self.canShoot) {
return;
}
var bullet = new PlayerBullet();
bullet.x = self.x;
bullet.y = self.y - self.height / 2;
game.addChild(bullet);
bullets.push(bullet);
LK.getSound('shoot').play();
self.startShootCooldown();
// Create double bullet when power-up is active
if (self.powerUpActive) {
var bulletLeft = new PlayerBullet();
bulletLeft.x = self.x - 20;
bulletLeft.y = self.y - self.height / 2;
var bulletRight = new PlayerBullet();
bulletRight.x = self.x + 20;
bulletRight.y = self.y - self.height / 2;
game.addChild(bulletLeft);
game.addChild(bulletRight);
bullets.push(bulletLeft);
bullets.push(bulletRight);
}
};
self.hit = function () {
if (self.invulnerable) {
return;
}
self.lives--;
LK.getSound('playerHit').play();
// Flash player ship to indicate damage
LK.effects.flashObject(self, 0xff0000, 1000);
// Make player temporarily invulnerable
self.invulnerable = true;
LK.setTimeout(function () {
self.invulnerable = false;
}, 2000);
// Check for game over
if (self.lives <= 0) {
LK.showGameOver();
}
};
self.activatePowerUp = function (type) {
LK.getSound('powerUpCollected').play();
switch (type) {
case 'rapidFire':
var oldCooldown = self.shootCooldown;
self.shootCooldown = 150;
LK.setTimeout(function () {
self.shootCooldown = oldCooldown;
}, 5000);
break;
case 'doubleBullet':
self.powerUpActive = true;
LK.setTimeout(function () {
self.powerUpActive = false;
}, 8000);
break;
case 'shield':
// Create shield
createShield();
break;
}
};
self.update = function () {
// Movement logic handled in game.update
};
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 = ['rapidFire', 'doubleBullet', 'shield'][Math.floor(Math.random() * 3)];
// Set color based on power-up type
switch (self.type) {
case 'rapidFire':
powerUpGraphics.tint = 0xff0000; // Red
break;
case 'doubleBullet':
powerUpGraphics.tint = 0x00ff00; // Green
break;
case 'shield':
powerUpGraphics.tint = 0x0000ff; // Blue
break;
}
self.update = function () {
self.y += self.speed;
// Destroy if out of bounds
if (self.y > 2732 + 50) {
self.destroy();
var index = powerUps.indexOf(self);
if (index > -1) {
powerUps.splice(index, 1);
}
}
};
return self;
});
var Shield = Container.expand(function () {
var self = Container.call(this);
var shieldGraphics = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5
});
self.hp = 3;
self.takeDamage = function () {
self.hp--;
// Visual feedback
LK.effects.flashObject(self, 0x2980b9, 200);
// Adjust opacity based on remaining HP
self.alpha = self.hp / 3;
if (self.hp <= 0) {
self.destroy();
var index = shields.indexOf(self);
if (index > -1) {
shields.splice(index, 1);
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var GAME_MARGIN = 100;
var ENEMY_ROWS = 5;
var ENEMY_COLUMNS = 8;
var ENEMY_SPACING_X = 150;
var ENEMY_SPACING_Y = 120;
var ENEMY_START_Y = 300;
var GROUP_MOVE_AMOUNT = 15;
var GROUP_DROP_AMOUNT = 40;
var MOVE_INTERVAL = 800; // ms
// Game variables
var level = storage.level || 1;
var player;
var bullets = [];
var enemies = [];
var enemyBullets = [];
var shields = [];
var powerUps = [];
var enemyMoveTimer;
var movingRight = true;
var bossActive = false;
var touchX = null;
// Create UI elements
var scoreTxt = new Text2('0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
scoreTxt.x = -250; // Offset from the right edge
var levelTxt = new Text2('LEVEL ' + level, {
size: 60,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(levelTxt);
var livesTxt = new Text2('LIVES: 3', {
size: 60,
fill: 0xFFFFFF
});
livesTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(livesTxt);
livesTxt.x = 120; // Ensure we don't place this in the top-left 100x100 area
// Game functions
function initializeGame() {
// Reset game state
clearArrays();
// Set difficulty based on level
var enemyHp = Math.min(Math.floor(level / 3) + 1, 3);
var enemySpeed = Math.min(2 + level * 0.5, 8);
var moveInterval = Math.max(MOVE_INTERVAL - level * 50, 300);
// Add space background
var spaceBackground = LK.getAsset('spaceBackground', {
anchorX: 0.5,
anchorY: 0.5
});
spaceBackground.x = GAME_WIDTH / 2;
spaceBackground.y = GAME_HEIGHT / 2;
game.addChild(spaceBackground);
// Create player
player = new PlayerShip();
player.x = GAME_WIDTH / 2;
player.y = GAME_HEIGHT - 200;
game.addChild(player);
// Create enemies
createEnemies(enemyHp);
// Start enemy movement
startEnemyMovement(moveInterval);
// Reset score if starting a new game
if (level === 1) {
LK.setScore(0);
}
// Update UI
scoreTxt.setText(LK.getScore());
levelTxt.setText('LEVEL ' + level);
// Play music
LK.playMusic('gameMusic', {
fade: {
start: 0,
end: 0.8,
duration: 1000
}
});
}
function clearArrays() {
// Clear existing game objects
for (var i = bullets.length - 1; i >= 0; i--) {
bullets[i].destroy();
}
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
}
for (var i = enemyBullets.length - 1; i >= 0; i--) {
enemyBullets[i].destroy();
}
for (var i = shields.length - 1; i >= 0; i--) {
shields[i].destroy();
}
for (var i = powerUps.length - 1; i >= 0; i--) {
powerUps[i].destroy();
}
bullets = [];
enemies = [];
enemyBullets = [];
shields = [];
powerUps = [];
// Clear any existing timers
if (enemyMoveTimer) {
LK.clearInterval(enemyMoveTimer);
}
}
function createEnemies(hp) {
bossActive = false;
// Boss level every 5 levels
if (level % 5 === 0) {
createBoss();
return;
}
// Create grid of enemies
var startX = (GAME_WIDTH - (ENEMY_COLUMNS - 1) * ENEMY_SPACING_X) / 2;
for (var row = 0; row < ENEMY_ROWS; row++) {
for (var col = 0; col < ENEMY_COLUMNS; col++) {
var enemy = new Enemy();
// Different types/strength based on row
if (row === 0) {
enemy.hp = hp + 1;
enemy.scoreValue = 300;
enemy.tint = 0xff0000; // Red top row
} else if (row === 1 || row === 2) {
enemy.hp = hp;
enemy.scoreValue = 200;
enemy.tint = 0x00ff00; // Green middle rows
} else {
enemy.hp = Math.max(1, hp - 1);
enemy.scoreValue = 100;
enemy.tint = 0x0000ff; // Blue bottom rows
}
enemy.x = startX + col * ENEMY_SPACING_X;
enemy.y = ENEMY_START_Y + row * ENEMY_SPACING_Y;
game.addChild(enemy);
enemies.push(enemy);
}
}
}
function createBoss() {
bossActive = true;
var boss = new Boss();
boss.x = GAME_WIDTH / 2;
boss.y = 300;
boss.hp = 15 + level / 5 * 5; // Increases with level
game.addChild(boss);
enemies.push(boss);
boss.startAttackPattern();
}
function startEnemyMovement(interval) {
enemyMoveTimer = LK.setInterval(function () {
moveEnemyGroup();
}, interval);
}
function moveEnemyGroup() {
if (enemies.length === 0) {
return;
}
var reachedEdge = false;
var moveAmount = bossActive ? GROUP_MOVE_AMOUNT * 1.5 : GROUP_MOVE_AMOUNT;
// Check if any enemy would go beyond screen edge
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (movingRight && enemy.x + enemy.width / 2 + moveAmount > GAME_WIDTH - GAME_MARGIN || !movingRight && enemy.x - enemy.width / 2 - moveAmount < GAME_MARGIN) {
reachedEdge = true;
break;
}
}
// Move group horizontally or drop and change direction
if (reachedEdge) {
movingRight = !movingRight;
// Move all enemies down
for (var i = 0; i < enemies.length; i++) {
enemies[i].y += GROUP_DROP_AMOUNT;
}
// Check if enemies reached player's area
checkEnemiesBottom();
} else {
// Move enemies sideways
for (var i = 0; i < enemies.length; i++) {
enemies[i].x += movingRight ? moveAmount : -moveAmount;
}
}
// Try to make some enemies shoot
for (var i = 0; i < enemies.length; i++) {
enemies[i].shoot();
}
}
function checkEnemiesBottom() {
for (var i = 0; i < enemies.length; i++) {
if (enemies[i].y + enemies[i].height / 2 > player.y - 100) {
// Game over if enemies get too close to bottom
LK.showGameOver();
return;
}
}
}
function createShield() {
var shield = new Shield();
shield.x = player.x;
shield.y = player.y - 80;
game.addChild(shield);
shields.push(shield);
}
function levelComplete() {
LK.getSound('levelComplete').play();
// Save high score if it's better than previous
if (LK.getScore() > storage.highScore) {
storage.highScore = LK.getScore();
}
// Increment level
level++;
storage.level = level;
// Show level transition
var levelCompleteTxt = new Text2('LEVEL COMPLETE!', {
size: 120,
fill: 0xFFFFFF
});
levelCompleteTxt.anchor.set(0.5, 0.5);
levelCompleteTxt.x = GAME_WIDTH / 2;
levelCompleteTxt.y = GAME_HEIGHT / 2;
game.addChild(levelCompleteTxt);
// Fade out current level
tween(levelCompleteTxt, {
alpha: 0
}, {
duration: 2000,
onFinish: function onFinish() {
levelCompleteTxt.destroy();
// Start next level
initializeGame();
}
});
}
function checkCollisions() {
// Player bullets vs enemies
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
// Handle hit
enemy.takeDamage();
// Remove bullet
bullet.destroy();
bullets.splice(i, 1);
break;
}
}
}
// Enemy bullets vs player
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
// Check for shield hits first
var hitShield = false;
for (var j = shields.length - 1; j >= 0; j--) {
var shield = shields[j];
if (bullet.intersects(shield)) {
shield.takeDamage();
// Remove bullet
bullet.destroy();
enemyBullets.splice(i, 1);
hitShield = true;
break;
}
}
if (hitShield) {
continue;
}
// Check for player hit
if (bullet.intersects(player)) {
player.hit();
// Remove bullet
bullet.destroy();
enemyBullets.splice(i, 1);
}
}
// Player vs power-ups
for (var i = powerUps.length - 1; i >= 0; i--) {
var powerUp = powerUps[i];
if (powerUp.intersects(player)) {
player.activatePowerUp(powerUp.type);
// Remove power-up
powerUp.destroy();
powerUps.splice(i, 1);
}
}
// Update lives display
livesTxt.setText('LIVES: ' + player.lives);
}
function movePlayer() {
// If touch input is active, move towards it
if (touchX !== null) {
var moveSpeed = player.speed;
var distanceToTouch = touchX - player.x;
if (Math.abs(distanceToTouch) < moveSpeed) {
player.x = touchX;
} else if (distanceToTouch > 0) {
player.x += moveSpeed;
} else {
player.x -= moveSpeed;
}
}
// Keep player within screen bounds
if (player.x < GAME_MARGIN + player.width / 2) {
player.x = GAME_MARGIN + player.width / 2;
} else if (player.x > GAME_WIDTH - GAME_MARGIN - player.width / 2) {
player.x = GAME_WIDTH - GAME_MARGIN - player.width / 2;
}
}
// Handle touch/mouse input
game.down = function (x, y, obj) {
touchX = x;
// Shoot when tapping
player.shoot();
};
game.move = function (x, y, obj) {
touchX = x;
};
game.up = function (x, y, obj) {
touchX = null;
};
// Game update loop
game.update = function () {
movePlayer();
// Update all game objects
for (var i = 0; i < bullets.length; i++) {
bullets[i].update();
}
for (var i = 0; i < enemyBullets.length; i++) {
enemyBullets[i].update();
}
for (var i = 0; i < powerUps.length; i++) {
powerUps[i].update();
}
// Check for collisions
checkCollisions();
};
// Initialize the game
initializeGame(); ===================================================================
--- original.js
+++ change.js
@@ -392,8 +392,16 @@
// Set difficulty based on level
var enemyHp = Math.min(Math.floor(level / 3) + 1, 3);
var enemySpeed = Math.min(2 + level * 0.5, 8);
var moveInterval = Math.max(MOVE_INTERVAL - level * 50, 300);
+ // Add space background
+ var spaceBackground = LK.getAsset('spaceBackground', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ spaceBackground.x = GAME_WIDTH / 2;
+ spaceBackground.y = GAME_HEIGHT / 2;
+ game.addChild(spaceBackground);
// Create player
player = new PlayerShip();
player.x = GAME_WIDTH / 2;
player.y = GAME_HEIGHT - 200;