/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.active = true;
self.width = enemyGraphics.width;
self.height = enemyGraphics.height;
self.points = 10;
self.shotChance = 0.005; // Chance per frame to shoot
self.type = "basic"; // Default enemy type
self.update = function () {
// Enemy logic will be controlled by the formation
};
return self;
});
var ToughEnemy = Enemy.expand(function () {
var self = Enemy.call(this);
self.children[0].tint = 0x0000ff; // Blue tough enemy
self.type = "tough";
self.points = 25;
self.shotChance = 0.005;
self.health = 2; // Takes 2 hits to destroy
// Override damage method
self.damage = function () {
self.health--;
// Visual feedback for hit
LK.effects.flashObject(self, 0xffffff, 100);
if (self.health <= 0) {
return true; // Enemy is destroyed
}
// Change appearance after hit
self.children[0].alpha = 0.7;
return false; // Enemy is still alive
};
return self;
});
// PowerUpManager has been moved to Classes section
var FastEnemy = Enemy.expand(function () {
var self = Enemy.call(this);
self.children[0].tint = 0xff0000; // Red fast enemy
self.type = "fast";
self.points = 15;
self.shotChance = 0.008; // Higher chance to shoot
self.speedMultiplier = 1.5; // Moves faster than other enemies
// Override update to add custom behavior if needed
var parentUpdate = self.update;
self.update = function () {
parentUpdate.call(self);
// Additional fast enemy behavior can be added here
};
return self;
});
var BomberEnemy = Enemy.expand(function () {
var self = Enemy.call(this);
self.children[0].tint = 0xff00ff; // Purple bomber enemy
self.type = "bomber";
self.points = 20;
self.shotChance = 0.004; // Lower chance to shoot, but shoots multiple bullets
// Bomber enemies will release multiple bullets in a pattern
self.fireBurst = function (shootFunction) {
if (typeof shootFunction === 'function') {
// Fire multiple shots in a spread pattern
shootFunction(self);
// Schedule additional shots with slight delay
LK.setTimeout(function () {
shootFunction(self);
}, 150);
LK.setTimeout(function () {
shootFunction(self);
}, 300);
}
};
return self;
});
var BasicEnemy = Enemy.expand(function () {
var self = Enemy.call(this);
self.children[0].tint = 0x00ff00; // Green basic enemy
self.type = "basic";
self.points = 10;
self.shotChance = 0.005;
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; // Positive because it moves downward
self.active = false;
self.visible = false;
self.width = bulletGraphics.width;
self.height = bulletGraphics.height;
self.reset = function (x, y) {
self.x = x;
self.y = y;
self.active = true;
self.visible = true;
};
self.update = function () {
self.y += self.speed;
// Deactivate when off screen
if (self.y > 2732 + self.height) {
self.active = false;
self.visible = false;
}
};
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 it moves upward
self.active = false;
self.visible = false;
self.width = bulletGraphics.width;
self.height = bulletGraphics.height;
self.reset = function (x, y) {
self.x = x;
self.y = y;
self.active = true;
self.visible = true;
};
self.update = function () {
self.y += self.speed;
// Deactivate when off screen
if (self.y < -self.height) {
self.active = false;
self.visible = 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.speed = 12;
self.width = shipGraphics.width;
self.height = shipGraphics.height;
self.fireRate = 15; // frames between shots
self.lastShot = 0;
self.alive = true;
self.moveLeft = function () {
self.x -= self.speed;
if (self.x < self.width / 2) {
self.x = self.width / 2;
}
};
self.moveRight = function () {
self.x += self.speed;
if (self.x > 2048 - self.width / 2) {
self.x = 2048 - self.width / 2;
}
};
self.canShoot = function () {
return self.lastShot <= 0;
};
self.resetShotTimer = function () {
self.lastShot = self.fireRate;
};
self.update = function () {
if (self.lastShot > 0) {
self.lastShot--;
}
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
// Default values
self.type = "none";
self.speed = 3;
self.active = false;
self.visible = false;
self.width = 80;
self.height = 80;
// Create base graphics container that will hold the powerup visual
var graphics = new Container();
self.addChild(graphics);
// Initialize with specific type
self.init = function (type, x, y) {
self.type = type;
self.x = x;
self.y = y;
self.active = true;
self.visible = true;
// Clear previous graphics
while (graphics.children.length > 0) {
graphics.removeChild(graphics.children[0]);
}
// Create visuals based on type
if (type === "extraLife") {
var heart = LK.getAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xff0000 // Red heart
});
graphics.addChild(heart);
} else if (type === "weaponUpgrade") {
var weapon = LK.getAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xffff00 // Yellow weapon upgrade
});
graphics.addChild(weapon);
} else if (type === "shield") {
var shield = LK.getAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00ffff // Cyan shield
});
graphics.addChild(shield);
}
// Apply a pulsing effect to make powerups stand out
self.pulseEffect();
};
// Pulsing animation effect
self.pulseEffect = function () {
tween(graphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
onFinish: function onComplete() {
tween(graphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 500,
onFinish: self.pulseEffect
});
}
});
};
// Update powerup position - move downward
self.update = function () {
if (self.active) {
self.y += self.speed;
// Deactivate when off screen
if (self.y > GAME_HEIGHT + self.height) {
self.active = false;
self.visible = false;
}
}
};
return self;
});
var Shield = Container.expand(function () {
var self = Container.call(this);
var shieldGraphics = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 3;
self.width = shieldGraphics.width;
self.height = shieldGraphics.height;
self.damage = function () {
self.health--;
shieldGraphics.alpha = self.health / 3;
if (self.health <= 0) {
self.active = false;
self.visible = false;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000022
});
/****
* Game Code
****/
// Game constants
var PowerUpManager = function PowerUpManager() {
var powerups = [];
var self = this;
var maxPowerups = 10;
// Probability settings
self.dropChance = 0.2; // 20% chance an enemy drops a powerup
// Create initial pool of powerups
for (var i = 0; i < maxPowerups; i++) {
var powerup = new PowerUp();
powerup.visible = false;
powerup.active = false;
powerups.push(powerup);
game.addChild(powerup);
}
// Get an inactive powerup from the pool
self.getPowerUp = function () {
for (var i = 0; i < powerups.length; i++) {
if (!powerups[i].active) {
return powerups[i];
}
}
return null; // No powerups available
};
// Create a powerup at the given position with random type
self.createPowerUp = function (x, y) {
if (Math.random() > self.dropChance) return; // Random chance to drop
var powerup = self.getPowerUp();
if (!powerup) return;
// Determine powerup type randomly
var rand = Math.random();
var type;
if (rand < 0.3) {
type = "extraLife";
} else if (rand < 0.7) {
type = "weaponUpgrade";
} else {
type = "shield";
}
powerup.init(type, x, y);
};
// Update all active powerups
self.update = function () {
for (var i = 0; i < powerups.length; i++) {
if (powerups[i].active) {
powerups[i].update();
}
}
};
// Check for collisions with player
self.checkCollisions = function (player) {
for (var i = 0; i < powerups.length; i++) {
var powerup = powerups[i];
if (powerup.active && player.alive && powerup.intersects(player)) {
// Apply powerup effect
self.applyPowerUp(powerup.type);
// Deactivate powerup
powerup.active = false;
powerup.visible = false;
// Visual feedback
LK.effects.flashObject(player, 0x00ff00, 300);
}
}
};
// Apply powerup effects
self.applyPowerUp = function (type) {
if (type === "extraLife") {
// Add extra life
lives++;
livesTxt.setText("Lives: " + lives);
} else if (type === "weaponUpgrade") {
// Temporary weapon upgrade (faster fire rate)
var originalFireRate = player.fireRate;
player.fireRate = Math.max(5, player.fireRate - 5); // Reduce fire rate (faster shots)
// Reset after 10 seconds
LK.setTimeout(function () {
player.fireRate = originalFireRate;
}, 10000);
} else if (type === "shield") {
// Create new shield for player
var shield = new Shield();
shield.x = player.x;
shield.y = SHIELD_Y;
shield.active = true;
shields.push(shield);
game.addChild(shield);
}
};
// Get all powerups
self.getAll = function () {
return powerups;
};
};
var BulletPool = function BulletPool(bulletType, maxSize) {
var pool = [];
var self = this;
// Pre-create bullets
for (var i = 0; i < maxSize; i++) {
var bullet = new bulletType();
bullet.visible = false;
bullet.active = false;
pool.push(bullet);
}
self.getBullet = function () {
for (var i = 0; i < pool.length; i++) {
if (!pool[i].active) {
pool[i].active = true;
pool[i].visible = true;
return pool[i];
}
}
return null; // No bullets available
};
self.getAll = function () {
return pool;
};
};
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var PLAYER_START_Y = GAME_HEIGHT - 200;
var SHIELD_Y = PLAYER_START_Y - 150;
var ENEMY_START_Y = 300;
var ENEMY_ROWS = 5;
var ENEMY_COLS = 10;
var ENEMY_PADDING = 20;
var ENEMIES_MOVE_DOWN_AMOUNT = 30;
var WAVE_SPEED_INCREASE = 0.2;
// Game state
var player;
var playerBulletPool;
var enemyBulletPool;
var powerUpManager;
var activePlayerBullets = 0;
var activeEnemyBullets = 0;
var enemies = [];
var shields = [];
var score = 0;
var lives = 3;
var wave = 1;
var enemiesDirection = 1; // 1 = right, -1 = left
var enemiesSpeed = 1;
var enemyMoveTimer = 0;
var enemyMoveInterval = 30; // Frames between enemy movement
var gameState = "playing"; // playing, gameover
var dragActive = false;
var dragStartX = 0;
var lastFrameTime = Date.now();
var frameTime = 0;
// UI elements
var scoreTxt;
var livesTxt;
var waveTxt;
// Initialize player
player = new PlayerShip();
player.x = GAME_WIDTH / 2;
player.y = PLAYER_START_Y;
game.addChild(player);
// Initialize bullet pools
playerBulletPool = new BulletPool(PlayerBullet, 20);
enemyBulletPool = new BulletPool(EnemyBullet, 50);
// Initialize power-up manager
powerUpManager = new PowerUpManager();
// Add all bullets to the game
var allPlayerBullets = playerBulletPool.getAll();
var allEnemyBullets = enemyBulletPool.getAll();
for (var i = 0; i < allPlayerBullets.length; i++) {
game.addChild(allPlayerBullets[i]);
}
for (var i = 0; i < allEnemyBullets.length; i++) {
game.addChild(allEnemyBullets[i]);
}
// Initialize shields
for (var i = 0; i < 3; i++) {
var shield = new Shield();
shield.x = GAME_WIDTH / 4 + GAME_WIDTH / 2 * (i / 2);
shield.y = SHIELD_Y;
shield.active = true;
shields.push(shield);
game.addChild(shield);
}
// Create UI
scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 20;
scoreTxt.y = 20;
LK.gui.addChild(scoreTxt);
livesTxt = new Text2('Lives: 3', {
size: 60,
fill: 0xFFFFFF
});
livesTxt.anchor.set(1, 0);
livesTxt.x = GAME_WIDTH - 20;
livesTxt.y = 20;
LK.gui.addChild(livesTxt);
waveTxt = new Text2('Wave: 1', {
size: 60,
fill: 0xFFFFFF
});
waveTxt.anchor.set(0.5, 0);
waveTxt.x = GAME_WIDTH / 2;
waveTxt.y = 20;
LK.gui.addChild(waveTxt);
// Create enemies
function createEnemies() {
// Calculate total width of enemy formation
var enemyWidth = new Enemy().width;
var formationWidth = ENEMY_COLS * (enemyWidth + ENEMY_PADDING) - ENEMY_PADDING;
var startX = (GAME_WIDTH - formationWidth) / 2 + enemyWidth / 2;
// Enemy type distribution based on wave number
// Higher waves have more advanced enemy types
for (var row = 0; row < ENEMY_ROWS; row++) {
for (var col = 0; col < ENEMY_COLS; col++) {
var enemy;
var rand = Math.random();
// Different enemy types depending on row and wave number
if (row === 0) {
// Top row - tougher enemies in higher waves
if (wave >= 3 && rand < 0.7) {
enemy = new ToughEnemy();
} else if (wave >= 2 && rand < 0.4) {
enemy = new FastEnemy();
} else {
enemy = new BasicEnemy();
}
} else if (row === 1) {
// Second row - more bombers in higher waves
if (wave >= 3 && rand < 0.5) {
enemy = new BomberEnemy();
} else if (wave >= 2 && rand < 0.3) {
enemy = new ToughEnemy();
} else {
enemy = new FastEnemy();
}
} else if (row === 2) {
// Third row - mixed enemy types
if (rand < 0.4) {
enemy = new FastEnemy();
} else if (rand < 0.7) {
enemy = new BomberEnemy();
} else {
enemy = new BasicEnemy();
}
} else {
// Lower rows - basic enemies with occasional fast ones
if (rand < 0.3 * (wave / 2)) {
enemy = new FastEnemy();
} else {
enemy = new BasicEnemy();
}
}
enemy.x = startX + col * (enemy.width + ENEMY_PADDING);
enemy.y = ENEMY_START_Y + row * (enemy.height + ENEMY_PADDING);
enemy.row = row;
enemy.col = col;
// Modify points based on row position (higher rows worth more)
enemy.points = Math.round(enemy.points * (1 + (ENEMY_ROWS - row) * 0.2));
enemies.push(enemy);
game.addChild(enemy);
}
}
}
createEnemies();
// Play background music
LK.playMusic('bgMusic', {
loop: true
});
// Game control event handlers
game.down = function (x, y, obj) {
dragActive = true;
dragStartX = x;
};
game.up = function (x, y, obj) {
dragActive = false;
};
game.move = function (x, y, obj) {
if (dragActive && gameState === "playing") {
// Move player based on drag
var deltaX = x - dragStartX;
if (Math.abs(deltaX) > 5) {
// Threshold to prevent jitter
if (deltaX > 0) {
player.moveRight();
} else {
player.moveLeft();
}
dragStartX = x;
}
}
};
// Create player bullet
function shootPlayerBullet() {
if (player.canShoot() && gameState === "playing") {
var bullet = playerBulletPool.getBullet();
if (bullet) {
bullet.reset(player.x, player.y - player.height / 2);
activePlayerBullets++;
player.resetShotTimer();
// Play shoot sound
LK.getSound('playerShoot').play();
}
}
}
// Create enemy bullet
function shootEnemyBullet(enemy) {
var bullet = enemyBulletPool.getBullet();
if (bullet) {
bullet.reset(enemy.x, enemy.y + enemy.height / 2);
activeEnemyBullets++;
}
}
// Check if formation needs to move down and change direction
function checkFormationBounds() {
var moveDown = false;
var minX = GAME_WIDTH;
var maxX = 0;
var enemyWidth = 0;
// Check if there are any enemies before proceeding
if (enemies.length === 0) {
return false;
}
enemies.forEach(function (enemy) {
if (enemy.x < minX) minX = enemy.x;
if (enemy.x > maxX) maxX = enemy.x;
enemyWidth = enemy.width; // Store width from any enemy for boundary checking
});
// Using enemyWidth which is now properly set from actual enemies
if (minX < enemyWidth / 2 && enemiesDirection === -1 || maxX > GAME_WIDTH - enemyWidth / 2 && enemiesDirection === 1) {
moveDown = true;
enemiesDirection *= -1;
}
return moveDown;
}
// Move enemy formation
function moveEnemies() {
var moveDown = checkFormationBounds();
enemies.forEach(function (enemy) {
if (moveDown) {
enemy.y += ENEMIES_MOVE_DOWN_AMOUNT;
}
enemy.x += enemiesDirection * enemiesSpeed;
});
}
// Check for collisions
function checkCollisions() {
var allPlayerBullets = playerBulletPool.getAll();
var allEnemyBullets = enemyBulletPool.getAll();
// Player bullets vs enemies
for (var i = 0; i < allPlayerBullets.length; i++) {
var bullet = allPlayerBullets[i];
if (!bullet.active) continue;
// Check if bullet hits any enemy - using a more efficient method
var hitEnemy = false;
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
var enemyDestroyed = true;
var enemyX = enemy.x;
var enemyY = enemy.y;
var enemyType = enemy.type;
// Handle tough enemies differently
if (enemy.type === "tough" && typeof enemy.damage === "function") {
enemyDestroyed = enemy.damage(); // Returns true if enemy is destroyed
}
if (enemyDestroyed) {
// Enemy hit and destroyed
score += enemy.points;
scoreTxt.setText("Score: " + score);
// Remove enemy
game.removeChild(enemy);
enemies.splice(j, 1);
// Try to spawn a power-up at enemy position
powerUpManager.createPowerUp(enemyX, enemyY);
// Play explosion sound
LK.getSound('enemyExplode').play();
// Flash screen effect
LK.effects.flashObject(game, 0xffffff, 100);
}
// Deactivate bullet regardless of whether enemy was destroyed
bullet.active = false;
bullet.visible = false;
activePlayerBullets--;
hitEnemy = true;
break; // Bullet can only hit one enemy
}
}
// Check if bullet hits any shield (only if it didn't hit an enemy)
if (!hitEnemy && bullet.active) {
for (var k = shields.length - 1; k >= 0; k--) {
var shield = shields[k];
if (shield.active && bullet.intersects(shield)) {
// Shield hit by player bullet
shield.damage();
// Deactivate bullet
bullet.active = false;
bullet.visible = false;
activePlayerBullets--;
// Remove shield if destroyed
if (!shield.active) {
game.removeChild(shield);
shields.splice(k, 1);
}
break;
}
}
}
}
// Enemy bullets vs player and shields
for (var i = 0; i < allEnemyBullets.length; i++) {
var bullet = allEnemyBullets[i];
if (!bullet.active) continue;
// Check player collision
if (bullet.intersects(player) && gameState === "playing") {
// Player hit
lives--;
livesTxt.setText("Lives: " + lives);
// Deactivate bullet
bullet.active = false;
bullet.visible = false;
activeEnemyBullets--;
// Play hit sound
LK.getSound('playerHit').play();
// Flash player effect
LK.effects.flashObject(player, 0xff0000, 500);
if (lives <= 0) {
gameState = "gameover";
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
continue;
}
// Check shield collision
if (bullet.active) {
for (var k = shields.length - 1; k >= 0; k--) {
var shield = shields[k];
if (shield.active && bullet.intersects(shield)) {
// Shield hit by enemy bullet
shield.damage();
// Deactivate bullet
bullet.active = false;
bullet.visible = false;
activeEnemyBullets--;
if (!shield.active) {
game.removeChild(shield);
shields.splice(k, 1);
}
break;
}
}
}
}
// Enemies vs bottom of screen
// Only check this less frequently (every 10 frames)
if (LK.ticks % 10 === 0) {
for (var i = 0; i < enemies.length; i++) {
if (enemies[i].y + enemies[i].height / 2 > player.y) {
// Enemies reached player level - game over
gameState = "gameover";
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
break;
}
}
}
}
// Start next wave
function startNextWave() {
wave++;
waveTxt.setText("Wave: " + wave);
// Display wave number with a nice animation
var waveAnnouncement = new Text2('WAVE ' + wave, {
size: 120,
fill: 0xffff00
});
waveAnnouncement.anchor.set(0.5, 0.5);
waveAnnouncement.x = GAME_WIDTH / 2;
waveAnnouncement.y = GAME_HEIGHT / 2;
waveAnnouncement.alpha = 0;
game.addChild(waveAnnouncement);
// Animate the wave announcement
tween(waveAnnouncement, {
alpha: 1
}, {
duration: 500,
onFinish: function onFinish() {
tween(waveAnnouncement, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 800,
onFinish: function onFinish() {
game.removeChild(waveAnnouncement);
}
});
}
});
// Increase enemy speed with each wave
enemiesSpeed += WAVE_SPEED_INCREASE;
// Adjust powerup drop chance based on wave difficulty
powerUpManager.dropChance = Math.min(0.35, 0.2 + wave * 0.03); // Increase drop chance slightly with waves
// Adjust enemy move interval (enemies move faster in higher waves)
enemyMoveInterval = Math.max(15, 30 - wave * 2);
// Reset all enemy bullets without removing them
var allEnemyBullets = enemyBulletPool.getAll();
for (var i = 0; i < allEnemyBullets.length; i++) {
if (allEnemyBullets[i].active) {
allEnemyBullets[i].active = false;
allEnemyBullets[i].visible = false;
}
}
activeEnemyBullets = 0;
// Create new enemy formation with more variety in higher waves
createEnemies();
}
// With object pooling, we no longer need to clean up inactive objects
// Main game update loop
game.update = function () {
// Calculate frame time for consistent speed regardless of framerate
var currentTime = Date.now();
frameTime = currentTime - lastFrameTime;
lastFrameTime = currentTime;
if (gameState === "playing") {
// Update player
player.update();
// Auto shoot - less frequent check
if (LK.ticks % 30 === 0) {
shootPlayerBullet();
}
// Move enemies
enemyMoveTimer++;
if (enemyMoveTimer >= enemyMoveInterval) {
moveEnemies();
enemyMoveTimer = 0;
}
// Enemy shooting - only check every 10 frames
if (enemies.length > 0 && LK.ticks % 10 === 0) {
// Pre-calculate bottom enemies once per update
var bottomEnemies = [];
var columnsWithEnemies = {};
// Find bottom enemies in each column more efficiently
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var column = enemy.col;
if (!columnsWithEnemies[column] || enemies[columnsWithEnemies[column]].row < enemy.row) {
columnsWithEnemies[column] = i;
}
}
// Get bottom enemies
for (var col in columnsWithEnemies) {
bottomEnemies.push(enemies[columnsWithEnemies[col]]);
}
// Random chance for enemies to shoot
if (bottomEnemies.length > 0) {
var maxShots = Math.min(3, bottomEnemies.length);
var shotCount = 0;
for (var i = 0; i < bottomEnemies.length && shotCount < maxShots; i++) {
var enemy = bottomEnemies[i];
if (Math.random() < enemy.shotChance * 2) {
// Special handling for bomber enemies
if (enemy.type === "bomber" && typeof enemy.fireBurst === "function") {
enemy.fireBurst(shootEnemyBullet);
} else {
// Regular enemy shooting
shootEnemyBullet(enemy);
}
shotCount++;
}
}
}
}
// Update bullets from pools
var allPlayerBullets = playerBulletPool.getAll();
var allEnemyBullets = enemyBulletPool.getAll();
// Only update active bullets
for (var i = 0; i < allPlayerBullets.length; i++) {
if (allPlayerBullets[i].active) {
allPlayerBullets[i].update();
}
}
for (var i = 0; i < allEnemyBullets.length; i++) {
if (allEnemyBullets[i].active) {
allEnemyBullets[i].update();
}
}
// Update powerups and check for collisions
powerUpManager.update();
powerUpManager.checkCollisions(player);
// Check collisions with optimized method
checkCollisions();
// Check if wave cleared
if (enemies.length === 0) {
startNextWave();
}
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.active = true;
self.width = enemyGraphics.width;
self.height = enemyGraphics.height;
self.points = 10;
self.shotChance = 0.005; // Chance per frame to shoot
self.type = "basic"; // Default enemy type
self.update = function () {
// Enemy logic will be controlled by the formation
};
return self;
});
var ToughEnemy = Enemy.expand(function () {
var self = Enemy.call(this);
self.children[0].tint = 0x0000ff; // Blue tough enemy
self.type = "tough";
self.points = 25;
self.shotChance = 0.005;
self.health = 2; // Takes 2 hits to destroy
// Override damage method
self.damage = function () {
self.health--;
// Visual feedback for hit
LK.effects.flashObject(self, 0xffffff, 100);
if (self.health <= 0) {
return true; // Enemy is destroyed
}
// Change appearance after hit
self.children[0].alpha = 0.7;
return false; // Enemy is still alive
};
return self;
});
// PowerUpManager has been moved to Classes section
var FastEnemy = Enemy.expand(function () {
var self = Enemy.call(this);
self.children[0].tint = 0xff0000; // Red fast enemy
self.type = "fast";
self.points = 15;
self.shotChance = 0.008; // Higher chance to shoot
self.speedMultiplier = 1.5; // Moves faster than other enemies
// Override update to add custom behavior if needed
var parentUpdate = self.update;
self.update = function () {
parentUpdate.call(self);
// Additional fast enemy behavior can be added here
};
return self;
});
var BomberEnemy = Enemy.expand(function () {
var self = Enemy.call(this);
self.children[0].tint = 0xff00ff; // Purple bomber enemy
self.type = "bomber";
self.points = 20;
self.shotChance = 0.004; // Lower chance to shoot, but shoots multiple bullets
// Bomber enemies will release multiple bullets in a pattern
self.fireBurst = function (shootFunction) {
if (typeof shootFunction === 'function') {
// Fire multiple shots in a spread pattern
shootFunction(self);
// Schedule additional shots with slight delay
LK.setTimeout(function () {
shootFunction(self);
}, 150);
LK.setTimeout(function () {
shootFunction(self);
}, 300);
}
};
return self;
});
var BasicEnemy = Enemy.expand(function () {
var self = Enemy.call(this);
self.children[0].tint = 0x00ff00; // Green basic enemy
self.type = "basic";
self.points = 10;
self.shotChance = 0.005;
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; // Positive because it moves downward
self.active = false;
self.visible = false;
self.width = bulletGraphics.width;
self.height = bulletGraphics.height;
self.reset = function (x, y) {
self.x = x;
self.y = y;
self.active = true;
self.visible = true;
};
self.update = function () {
self.y += self.speed;
// Deactivate when off screen
if (self.y > 2732 + self.height) {
self.active = false;
self.visible = false;
}
};
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 it moves upward
self.active = false;
self.visible = false;
self.width = bulletGraphics.width;
self.height = bulletGraphics.height;
self.reset = function (x, y) {
self.x = x;
self.y = y;
self.active = true;
self.visible = true;
};
self.update = function () {
self.y += self.speed;
// Deactivate when off screen
if (self.y < -self.height) {
self.active = false;
self.visible = 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.speed = 12;
self.width = shipGraphics.width;
self.height = shipGraphics.height;
self.fireRate = 15; // frames between shots
self.lastShot = 0;
self.alive = true;
self.moveLeft = function () {
self.x -= self.speed;
if (self.x < self.width / 2) {
self.x = self.width / 2;
}
};
self.moveRight = function () {
self.x += self.speed;
if (self.x > 2048 - self.width / 2) {
self.x = 2048 - self.width / 2;
}
};
self.canShoot = function () {
return self.lastShot <= 0;
};
self.resetShotTimer = function () {
self.lastShot = self.fireRate;
};
self.update = function () {
if (self.lastShot > 0) {
self.lastShot--;
}
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
// Default values
self.type = "none";
self.speed = 3;
self.active = false;
self.visible = false;
self.width = 80;
self.height = 80;
// Create base graphics container that will hold the powerup visual
var graphics = new Container();
self.addChild(graphics);
// Initialize with specific type
self.init = function (type, x, y) {
self.type = type;
self.x = x;
self.y = y;
self.active = true;
self.visible = true;
// Clear previous graphics
while (graphics.children.length > 0) {
graphics.removeChild(graphics.children[0]);
}
// Create visuals based on type
if (type === "extraLife") {
var heart = LK.getAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xff0000 // Red heart
});
graphics.addChild(heart);
} else if (type === "weaponUpgrade") {
var weapon = LK.getAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xffff00 // Yellow weapon upgrade
});
graphics.addChild(weapon);
} else if (type === "shield") {
var shield = LK.getAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00ffff // Cyan shield
});
graphics.addChild(shield);
}
// Apply a pulsing effect to make powerups stand out
self.pulseEffect();
};
// Pulsing animation effect
self.pulseEffect = function () {
tween(graphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
onFinish: function onComplete() {
tween(graphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 500,
onFinish: self.pulseEffect
});
}
});
};
// Update powerup position - move downward
self.update = function () {
if (self.active) {
self.y += self.speed;
// Deactivate when off screen
if (self.y > GAME_HEIGHT + self.height) {
self.active = false;
self.visible = false;
}
}
};
return self;
});
var Shield = Container.expand(function () {
var self = Container.call(this);
var shieldGraphics = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 3;
self.width = shieldGraphics.width;
self.height = shieldGraphics.height;
self.damage = function () {
self.health--;
shieldGraphics.alpha = self.health / 3;
if (self.health <= 0) {
self.active = false;
self.visible = false;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000022
});
/****
* Game Code
****/
// Game constants
var PowerUpManager = function PowerUpManager() {
var powerups = [];
var self = this;
var maxPowerups = 10;
// Probability settings
self.dropChance = 0.2; // 20% chance an enemy drops a powerup
// Create initial pool of powerups
for (var i = 0; i < maxPowerups; i++) {
var powerup = new PowerUp();
powerup.visible = false;
powerup.active = false;
powerups.push(powerup);
game.addChild(powerup);
}
// Get an inactive powerup from the pool
self.getPowerUp = function () {
for (var i = 0; i < powerups.length; i++) {
if (!powerups[i].active) {
return powerups[i];
}
}
return null; // No powerups available
};
// Create a powerup at the given position with random type
self.createPowerUp = function (x, y) {
if (Math.random() > self.dropChance) return; // Random chance to drop
var powerup = self.getPowerUp();
if (!powerup) return;
// Determine powerup type randomly
var rand = Math.random();
var type;
if (rand < 0.3) {
type = "extraLife";
} else if (rand < 0.7) {
type = "weaponUpgrade";
} else {
type = "shield";
}
powerup.init(type, x, y);
};
// Update all active powerups
self.update = function () {
for (var i = 0; i < powerups.length; i++) {
if (powerups[i].active) {
powerups[i].update();
}
}
};
// Check for collisions with player
self.checkCollisions = function (player) {
for (var i = 0; i < powerups.length; i++) {
var powerup = powerups[i];
if (powerup.active && player.alive && powerup.intersects(player)) {
// Apply powerup effect
self.applyPowerUp(powerup.type);
// Deactivate powerup
powerup.active = false;
powerup.visible = false;
// Visual feedback
LK.effects.flashObject(player, 0x00ff00, 300);
}
}
};
// Apply powerup effects
self.applyPowerUp = function (type) {
if (type === "extraLife") {
// Add extra life
lives++;
livesTxt.setText("Lives: " + lives);
} else if (type === "weaponUpgrade") {
// Temporary weapon upgrade (faster fire rate)
var originalFireRate = player.fireRate;
player.fireRate = Math.max(5, player.fireRate - 5); // Reduce fire rate (faster shots)
// Reset after 10 seconds
LK.setTimeout(function () {
player.fireRate = originalFireRate;
}, 10000);
} else if (type === "shield") {
// Create new shield for player
var shield = new Shield();
shield.x = player.x;
shield.y = SHIELD_Y;
shield.active = true;
shields.push(shield);
game.addChild(shield);
}
};
// Get all powerups
self.getAll = function () {
return powerups;
};
};
var BulletPool = function BulletPool(bulletType, maxSize) {
var pool = [];
var self = this;
// Pre-create bullets
for (var i = 0; i < maxSize; i++) {
var bullet = new bulletType();
bullet.visible = false;
bullet.active = false;
pool.push(bullet);
}
self.getBullet = function () {
for (var i = 0; i < pool.length; i++) {
if (!pool[i].active) {
pool[i].active = true;
pool[i].visible = true;
return pool[i];
}
}
return null; // No bullets available
};
self.getAll = function () {
return pool;
};
};
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var PLAYER_START_Y = GAME_HEIGHT - 200;
var SHIELD_Y = PLAYER_START_Y - 150;
var ENEMY_START_Y = 300;
var ENEMY_ROWS = 5;
var ENEMY_COLS = 10;
var ENEMY_PADDING = 20;
var ENEMIES_MOVE_DOWN_AMOUNT = 30;
var WAVE_SPEED_INCREASE = 0.2;
// Game state
var player;
var playerBulletPool;
var enemyBulletPool;
var powerUpManager;
var activePlayerBullets = 0;
var activeEnemyBullets = 0;
var enemies = [];
var shields = [];
var score = 0;
var lives = 3;
var wave = 1;
var enemiesDirection = 1; // 1 = right, -1 = left
var enemiesSpeed = 1;
var enemyMoveTimer = 0;
var enemyMoveInterval = 30; // Frames between enemy movement
var gameState = "playing"; // playing, gameover
var dragActive = false;
var dragStartX = 0;
var lastFrameTime = Date.now();
var frameTime = 0;
// UI elements
var scoreTxt;
var livesTxt;
var waveTxt;
// Initialize player
player = new PlayerShip();
player.x = GAME_WIDTH / 2;
player.y = PLAYER_START_Y;
game.addChild(player);
// Initialize bullet pools
playerBulletPool = new BulletPool(PlayerBullet, 20);
enemyBulletPool = new BulletPool(EnemyBullet, 50);
// Initialize power-up manager
powerUpManager = new PowerUpManager();
// Add all bullets to the game
var allPlayerBullets = playerBulletPool.getAll();
var allEnemyBullets = enemyBulletPool.getAll();
for (var i = 0; i < allPlayerBullets.length; i++) {
game.addChild(allPlayerBullets[i]);
}
for (var i = 0; i < allEnemyBullets.length; i++) {
game.addChild(allEnemyBullets[i]);
}
// Initialize shields
for (var i = 0; i < 3; i++) {
var shield = new Shield();
shield.x = GAME_WIDTH / 4 + GAME_WIDTH / 2 * (i / 2);
shield.y = SHIELD_Y;
shield.active = true;
shields.push(shield);
game.addChild(shield);
}
// Create UI
scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 20;
scoreTxt.y = 20;
LK.gui.addChild(scoreTxt);
livesTxt = new Text2('Lives: 3', {
size: 60,
fill: 0xFFFFFF
});
livesTxt.anchor.set(1, 0);
livesTxt.x = GAME_WIDTH - 20;
livesTxt.y = 20;
LK.gui.addChild(livesTxt);
waveTxt = new Text2('Wave: 1', {
size: 60,
fill: 0xFFFFFF
});
waveTxt.anchor.set(0.5, 0);
waveTxt.x = GAME_WIDTH / 2;
waveTxt.y = 20;
LK.gui.addChild(waveTxt);
// Create enemies
function createEnemies() {
// Calculate total width of enemy formation
var enemyWidth = new Enemy().width;
var formationWidth = ENEMY_COLS * (enemyWidth + ENEMY_PADDING) - ENEMY_PADDING;
var startX = (GAME_WIDTH - formationWidth) / 2 + enemyWidth / 2;
// Enemy type distribution based on wave number
// Higher waves have more advanced enemy types
for (var row = 0; row < ENEMY_ROWS; row++) {
for (var col = 0; col < ENEMY_COLS; col++) {
var enemy;
var rand = Math.random();
// Different enemy types depending on row and wave number
if (row === 0) {
// Top row - tougher enemies in higher waves
if (wave >= 3 && rand < 0.7) {
enemy = new ToughEnemy();
} else if (wave >= 2 && rand < 0.4) {
enemy = new FastEnemy();
} else {
enemy = new BasicEnemy();
}
} else if (row === 1) {
// Second row - more bombers in higher waves
if (wave >= 3 && rand < 0.5) {
enemy = new BomberEnemy();
} else if (wave >= 2 && rand < 0.3) {
enemy = new ToughEnemy();
} else {
enemy = new FastEnemy();
}
} else if (row === 2) {
// Third row - mixed enemy types
if (rand < 0.4) {
enemy = new FastEnemy();
} else if (rand < 0.7) {
enemy = new BomberEnemy();
} else {
enemy = new BasicEnemy();
}
} else {
// Lower rows - basic enemies with occasional fast ones
if (rand < 0.3 * (wave / 2)) {
enemy = new FastEnemy();
} else {
enemy = new BasicEnemy();
}
}
enemy.x = startX + col * (enemy.width + ENEMY_PADDING);
enemy.y = ENEMY_START_Y + row * (enemy.height + ENEMY_PADDING);
enemy.row = row;
enemy.col = col;
// Modify points based on row position (higher rows worth more)
enemy.points = Math.round(enemy.points * (1 + (ENEMY_ROWS - row) * 0.2));
enemies.push(enemy);
game.addChild(enemy);
}
}
}
createEnemies();
// Play background music
LK.playMusic('bgMusic', {
loop: true
});
// Game control event handlers
game.down = function (x, y, obj) {
dragActive = true;
dragStartX = x;
};
game.up = function (x, y, obj) {
dragActive = false;
};
game.move = function (x, y, obj) {
if (dragActive && gameState === "playing") {
// Move player based on drag
var deltaX = x - dragStartX;
if (Math.abs(deltaX) > 5) {
// Threshold to prevent jitter
if (deltaX > 0) {
player.moveRight();
} else {
player.moveLeft();
}
dragStartX = x;
}
}
};
// Create player bullet
function shootPlayerBullet() {
if (player.canShoot() && gameState === "playing") {
var bullet = playerBulletPool.getBullet();
if (bullet) {
bullet.reset(player.x, player.y - player.height / 2);
activePlayerBullets++;
player.resetShotTimer();
// Play shoot sound
LK.getSound('playerShoot').play();
}
}
}
// Create enemy bullet
function shootEnemyBullet(enemy) {
var bullet = enemyBulletPool.getBullet();
if (bullet) {
bullet.reset(enemy.x, enemy.y + enemy.height / 2);
activeEnemyBullets++;
}
}
// Check if formation needs to move down and change direction
function checkFormationBounds() {
var moveDown = false;
var minX = GAME_WIDTH;
var maxX = 0;
var enemyWidth = 0;
// Check if there are any enemies before proceeding
if (enemies.length === 0) {
return false;
}
enemies.forEach(function (enemy) {
if (enemy.x < minX) minX = enemy.x;
if (enemy.x > maxX) maxX = enemy.x;
enemyWidth = enemy.width; // Store width from any enemy for boundary checking
});
// Using enemyWidth which is now properly set from actual enemies
if (minX < enemyWidth / 2 && enemiesDirection === -1 || maxX > GAME_WIDTH - enemyWidth / 2 && enemiesDirection === 1) {
moveDown = true;
enemiesDirection *= -1;
}
return moveDown;
}
// Move enemy formation
function moveEnemies() {
var moveDown = checkFormationBounds();
enemies.forEach(function (enemy) {
if (moveDown) {
enemy.y += ENEMIES_MOVE_DOWN_AMOUNT;
}
enemy.x += enemiesDirection * enemiesSpeed;
});
}
// Check for collisions
function checkCollisions() {
var allPlayerBullets = playerBulletPool.getAll();
var allEnemyBullets = enemyBulletPool.getAll();
// Player bullets vs enemies
for (var i = 0; i < allPlayerBullets.length; i++) {
var bullet = allPlayerBullets[i];
if (!bullet.active) continue;
// Check if bullet hits any enemy - using a more efficient method
var hitEnemy = false;
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
var enemyDestroyed = true;
var enemyX = enemy.x;
var enemyY = enemy.y;
var enemyType = enemy.type;
// Handle tough enemies differently
if (enemy.type === "tough" && typeof enemy.damage === "function") {
enemyDestroyed = enemy.damage(); // Returns true if enemy is destroyed
}
if (enemyDestroyed) {
// Enemy hit and destroyed
score += enemy.points;
scoreTxt.setText("Score: " + score);
// Remove enemy
game.removeChild(enemy);
enemies.splice(j, 1);
// Try to spawn a power-up at enemy position
powerUpManager.createPowerUp(enemyX, enemyY);
// Play explosion sound
LK.getSound('enemyExplode').play();
// Flash screen effect
LK.effects.flashObject(game, 0xffffff, 100);
}
// Deactivate bullet regardless of whether enemy was destroyed
bullet.active = false;
bullet.visible = false;
activePlayerBullets--;
hitEnemy = true;
break; // Bullet can only hit one enemy
}
}
// Check if bullet hits any shield (only if it didn't hit an enemy)
if (!hitEnemy && bullet.active) {
for (var k = shields.length - 1; k >= 0; k--) {
var shield = shields[k];
if (shield.active && bullet.intersects(shield)) {
// Shield hit by player bullet
shield.damage();
// Deactivate bullet
bullet.active = false;
bullet.visible = false;
activePlayerBullets--;
// Remove shield if destroyed
if (!shield.active) {
game.removeChild(shield);
shields.splice(k, 1);
}
break;
}
}
}
}
// Enemy bullets vs player and shields
for (var i = 0; i < allEnemyBullets.length; i++) {
var bullet = allEnemyBullets[i];
if (!bullet.active) continue;
// Check player collision
if (bullet.intersects(player) && gameState === "playing") {
// Player hit
lives--;
livesTxt.setText("Lives: " + lives);
// Deactivate bullet
bullet.active = false;
bullet.visible = false;
activeEnemyBullets--;
// Play hit sound
LK.getSound('playerHit').play();
// Flash player effect
LK.effects.flashObject(player, 0xff0000, 500);
if (lives <= 0) {
gameState = "gameover";
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
continue;
}
// Check shield collision
if (bullet.active) {
for (var k = shields.length - 1; k >= 0; k--) {
var shield = shields[k];
if (shield.active && bullet.intersects(shield)) {
// Shield hit by enemy bullet
shield.damage();
// Deactivate bullet
bullet.active = false;
bullet.visible = false;
activeEnemyBullets--;
if (!shield.active) {
game.removeChild(shield);
shields.splice(k, 1);
}
break;
}
}
}
}
// Enemies vs bottom of screen
// Only check this less frequently (every 10 frames)
if (LK.ticks % 10 === 0) {
for (var i = 0; i < enemies.length; i++) {
if (enemies[i].y + enemies[i].height / 2 > player.y) {
// Enemies reached player level - game over
gameState = "gameover";
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
break;
}
}
}
}
// Start next wave
function startNextWave() {
wave++;
waveTxt.setText("Wave: " + wave);
// Display wave number with a nice animation
var waveAnnouncement = new Text2('WAVE ' + wave, {
size: 120,
fill: 0xffff00
});
waveAnnouncement.anchor.set(0.5, 0.5);
waveAnnouncement.x = GAME_WIDTH / 2;
waveAnnouncement.y = GAME_HEIGHT / 2;
waveAnnouncement.alpha = 0;
game.addChild(waveAnnouncement);
// Animate the wave announcement
tween(waveAnnouncement, {
alpha: 1
}, {
duration: 500,
onFinish: function onFinish() {
tween(waveAnnouncement, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 800,
onFinish: function onFinish() {
game.removeChild(waveAnnouncement);
}
});
}
});
// Increase enemy speed with each wave
enemiesSpeed += WAVE_SPEED_INCREASE;
// Adjust powerup drop chance based on wave difficulty
powerUpManager.dropChance = Math.min(0.35, 0.2 + wave * 0.03); // Increase drop chance slightly with waves
// Adjust enemy move interval (enemies move faster in higher waves)
enemyMoveInterval = Math.max(15, 30 - wave * 2);
// Reset all enemy bullets without removing them
var allEnemyBullets = enemyBulletPool.getAll();
for (var i = 0; i < allEnemyBullets.length; i++) {
if (allEnemyBullets[i].active) {
allEnemyBullets[i].active = false;
allEnemyBullets[i].visible = false;
}
}
activeEnemyBullets = 0;
// Create new enemy formation with more variety in higher waves
createEnemies();
}
// With object pooling, we no longer need to clean up inactive objects
// Main game update loop
game.update = function () {
// Calculate frame time for consistent speed regardless of framerate
var currentTime = Date.now();
frameTime = currentTime - lastFrameTime;
lastFrameTime = currentTime;
if (gameState === "playing") {
// Update player
player.update();
// Auto shoot - less frequent check
if (LK.ticks % 30 === 0) {
shootPlayerBullet();
}
// Move enemies
enemyMoveTimer++;
if (enemyMoveTimer >= enemyMoveInterval) {
moveEnemies();
enemyMoveTimer = 0;
}
// Enemy shooting - only check every 10 frames
if (enemies.length > 0 && LK.ticks % 10 === 0) {
// Pre-calculate bottom enemies once per update
var bottomEnemies = [];
var columnsWithEnemies = {};
// Find bottom enemies in each column more efficiently
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var column = enemy.col;
if (!columnsWithEnemies[column] || enemies[columnsWithEnemies[column]].row < enemy.row) {
columnsWithEnemies[column] = i;
}
}
// Get bottom enemies
for (var col in columnsWithEnemies) {
bottomEnemies.push(enemies[columnsWithEnemies[col]]);
}
// Random chance for enemies to shoot
if (bottomEnemies.length > 0) {
var maxShots = Math.min(3, bottomEnemies.length);
var shotCount = 0;
for (var i = 0; i < bottomEnemies.length && shotCount < maxShots; i++) {
var enemy = bottomEnemies[i];
if (Math.random() < enemy.shotChance * 2) {
// Special handling for bomber enemies
if (enemy.type === "bomber" && typeof enemy.fireBurst === "function") {
enemy.fireBurst(shootEnemyBullet);
} else {
// Regular enemy shooting
shootEnemyBullet(enemy);
}
shotCount++;
}
}
}
}
// Update bullets from pools
var allPlayerBullets = playerBulletPool.getAll();
var allEnemyBullets = enemyBulletPool.getAll();
// Only update active bullets
for (var i = 0; i < allPlayerBullets.length; i++) {
if (allPlayerBullets[i].active) {
allPlayerBullets[i].update();
}
}
for (var i = 0; i < allEnemyBullets.length; i++) {
if (allEnemyBullets[i].active) {
allEnemyBullets[i].update();
}
}
// Update powerups and check for collisions
powerUpManager.update();
powerUpManager.checkCollisions(player);
// Check collisions with optimized method
checkCollisions();
// Check if wave cleared
if (enemies.length === 0) {
startNextWave();
}
}
};
Top down 2d pixilated spaceship. In-Game asset. 2d. High contrast. No shadows
Enemy spaceship top down 2d pixilated and looking downwards. In-Game asset. 2d. High contrast. No shadows
Bullet 2d top down pixilated. In-Game asset. 2d. High contrast. No shadows
Bullet facing down and top Down 2d pixilated. In-Game asset. 2d. High contrast. No shadows
Sheid top down 2d pixilated. In-Game asset. 2d. High contrast. No shadows