/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossNumber = Math.ceil(currentLevel / 10);
var assetName = bossNumber === 1 ? 'boss' : 'boss' + bossNumber;
var graphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
// Boss health scaling based on specific progression table
function getBossHealthForLevel(level) {
if (level <= 5) return 5;
if (level <= 10) return 10;
if (level <= 15) return 20;
if (level <= 20) return 35;
if (level <= 25) return 50;
if (level <= 30) return 70;
if (level <= 35) return 90;
if (level <= 40) return 115;
if (level <= 45) return 140;
if (level <= 50) return 170;
if (level <= 55) return 200;
if (level <= 60) return 235;
if (level <= 65) return 270;
if (level <= 70) return 310;
if (level <= 75) return 350;
if (level <= 80) return 395;
if (level <= 85) return 440;
if (level <= 90) return 460;
if (level <= 95) return 480;
if (level <= 100) return 500;
return 500; // Default fallback
}
self.maxHealth = getBossHealthForLevel(currentLevel);
self.health = self.maxHealth;
self.lastShot = 0;
self.shootCooldown = 240; // 4 seconds at 60fps for better balance
self.direction = 1;
self.defeated = false;
self.update = function () {
// Boss movement pattern
self.x += self.speed * self.direction;
if (self.x <= 200 || self.x >= 1848) {
self.direction *= -1;
}
// Boss shooting pattern - can shoot back-to-back
self.lastShot++;
if (self.lastShot >= self.shootCooldown) {
self.lastShot = Math.max(0, self.shootCooldown - 120); // Allow back-to-back shooting by reducing cooldown
// Shoot 3 bullets at the same position
for (var i = 0; i < 3; i++) {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + 75;
enemyBullets.push(bullet);
game.addChild(bullet);
}
}
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1.5;
self.health = 1;
self.maxHealth = 1;
self.lastShot = 0;
self.shootCooldown = 300; // 5 seconds at 60fps for better balance with faster player
self.direction = Math.random() < 0.5 ? -1 : 1; // Random initial direction
self.horizontalSpeed = 2;
self.update = function () {
self.y += self.speed;
// Enhanced horizontal movement with direction changes
self.x += self.horizontalSpeed * self.direction;
// Change direction when hitting screen edges or randomly
if (self.x <= 200 || self.x >= 1848 || Math.random() < 0.005) {
self.direction *= -1;
}
// Bullet dodging behavior - check for nearby player bullets
for (var b = 0; b < playerBullets.length; b++) {
var bullet = playerBullets[b];
if (bullet && bullet.parent) {
// Calculate distance to bullet
var distX = Math.abs(bullet.x - self.x);
var distY = Math.abs(bullet.y - self.y);
// If bullet is close and approaching, try to dodge
if (distX < 100 && distY < 150 && bullet.y < self.y) {
// Determine dodge direction based on bullet position
if (bullet.x < self.x && self.x < 1800) {
// Bullet is to the left, move right
self.x += 8;
} else if (bullet.x > self.x && self.x > 248) {
// Bullet is to the right, move left
self.x -= 8;
}
// Slight vertical evasion
if (Math.random() < 0.3) {
self.y += Math.random() < 0.5 ? -3 : 3;
}
break; // Only dodge one bullet per frame
}
}
}
// Faster shooting - single shot but more frequent
self.lastShot++;
if (self.lastShot >= 180 && Math.random() < 0.08) {
// Reduced cooldown from 300 to 180, increased chance from 0.04 to 0.08
self.lastShot = 0; // Reset cooldown completely to prevent back-to-back shooting
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + 30;
enemyBullets.push(bullet);
game.addChild(bullet);
}
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = maxPlayerHealth;
self.maxHealth = maxPlayerHealth;
self.lastShot = 0;
self.invulnerable = false;
self.invulnerableTimer = 0;
self.shoot = function () {
if (self.lastShot > 0) return;
self.lastShot = getShootCooldown();
// Shooting sound removed
if (currentLevel >= 80) {
// Rapid fire - single bullet but faster
var bullet = new PlayerBullet();
bullet.x = self.x;
bullet.y = self.y - 40;
playerBullets.push(bullet);
game.addChild(bullet);
} else if (currentLevel >= 50) {
// Triple fire
for (var i = -1; i <= 1; i++) {
var bullet = new PlayerBullet();
bullet.x = self.x + i * 25;
bullet.y = self.y - 40;
playerBullets.push(bullet);
game.addChild(bullet);
}
} else if (currentLevel >= 20) {
// Dual fire
for (var i = -1; i <= 1; i += 2) {
var bullet = new PlayerBullet();
bullet.x = self.x + i * 20;
bullet.y = self.y - 40;
playerBullets.push(bullet);
game.addChild(bullet);
}
} else {
// Single fire
var bullet = new PlayerBullet();
bullet.x = self.x;
bullet.y = self.y - 40;
playerBullets.push(bullet);
game.addChild(bullet);
}
};
self.takeDamage = function () {
if (self.invulnerable) return;
self.health--;
self.invulnerable = true;
self.invulnerableTimer = 120; // 2 seconds invulnerability
LK.getSound('playerHit').play();
LK.effects.flashObject(self, 0xff0000, 500);
if (self.health <= 0) {
playerLives--;
storage.playerLives = playerLives;
self.health = self.maxHealth; // Reset health for next life
if (playerLives <= 0) {
LK.showGameOver();
} else {
// Respawn at starting position
self.x = 1024;
self.y = 2400;
// Restore from last checkpoint
var checkpointScore = storage.checkpointScore || 0;
var checkpointLevel = storage.checkpointLevel || 1;
// Reset to checkpoint level and score
currentLevel = checkpointLevel;
storage.currentLevel = currentLevel;
LK.setScore(checkpointScore);
// Reset level state
enemiesKilled = 0;
enemiesNeededForLevel = getEnemyCountForLevel(currentLevel);
isBossLevel = isBossLevelCheck(currentLevel);
levelCompleteTimer = 0;
// Update UI
levelTxt.setText('Level: ' + currentLevel);
updateWeaponDisplay();
updatePointsNeededDisplay();
livesTxt.setText('Lives: ' + playerLives);
// Clear all enemies and bullets
for (var e = enemies.length - 1; e >= 0; e--) {
enemies[e].destroy();
enemies.splice(e, 1);
}
for (var eb = enemyBullets.length - 1; eb >= 0; eb--) {
enemyBullets[eb].destroy();
enemyBullets.splice(eb, 1);
}
// Clear boss if exists
if (currentBoss) {
currentBoss.destroy();
currentBoss = null;
if (bossHealthBar) {
bossHealthBar.destroy();
bossHealthBar = null;
}
if (bossHealthBarBg) {
bossHealthBarBg.destroy();
bossHealthBarBg = null;
}
if (bossHealthTxt) {
bossHealthTxt.destroy();
bossHealthTxt = null;
}
if (bossHealthTextBg) {
bossHealthTextBg.destroy();
bossHealthTextBg = null;
}
}
}
}
};
self.update = function () {
if (self.lastShot > 0) {
self.lastShot--;
}
if (self.invulnerable) {
self.invulnerableTimer--;
if (self.invulnerableTimer <= 0) {
self.invulnerable = false;
}
}
};
return self;
});
var PlayerBullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -12;
self.update = function () {
self.y += self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game state variables
var currentLevel = storage.currentLevel || 1;
var playerLives = storage.playerLives || 3;
var playerCoins = storage.playerCoins || 0;
var maxPlayerHealth = storage.maxPlayerHealth || 50;
var marketOpen = false;
var marketOverlay = null;
var marketButtons = [];
// Initialize checkpoint data
if (!storage.checkpointScore) storage.checkpointScore = 0;
if (!storage.checkpointLevel) storage.checkpointLevel = 1;
var playerBullets = [];
var enemyBullets = [];
var enemies = [];
var currentBoss = null;
var bossHealthBar = null;
var bossHealthBarBg = null;
var bossHealthTxt = null;
var bossHealthTextBg = null;
var isBossLevel = false;
var enemySpawnTimer = 0;
var levelCompleteTimer = 0;
var enemiesKilled = 0;
// Set exact enemy count per level based on specific requirements
function getEnemyCountForLevel(level) {
if (level <= 0) return 5;
if (level === 1) return 5;
if (level === 2) return 7;
if (level === 3) return 9;
if (level === 4) return 10;
if (level === 5) return 12;
if (level === 6) return 14;
if (level === 7) return 16;
if (level === 8) return 17;
if (level === 9) return 19;
if (level === 10) return 21;
if (level === 11) return 23;
if (level === 12) return 24;
if (level === 13) return 26;
if (level === 14) return 28;
if (level === 15) return 30;
if (level === 16) return 31;
if (level === 17) return 33;
if (level === 18) return 35;
if (level === 19) return 37;
if (level === 20) return 38;
if (level === 21) return 40;
if (level === 22) return 42;
if (level === 23) return 44;
if (level === 24) return 45;
if (level === 25) return 47;
if (level === 26) return 49;
if (level === 27) return 51;
if (level === 28) return 52;
if (level === 29) return 54;
if (level === 30) return 56;
if (level === 31) return 58;
if (level === 32) return 59;
if (level === 33) return 61;
if (level === 34) return 63;
if (level === 35) return 65;
if (level === 36) return 66;
if (level === 37) return 68;
if (level === 38) return 70;
if (level === 39) return 72;
if (level === 40) return 73;
if (level === 41) return 75;
if (level === 42) return 77;
if (level === 43) return 79;
if (level === 44) return 80;
if (level === 45) return 82;
if (level === 46) return 84;
if (level === 47) return 86;
if (level === 48) return 87;
if (level === 49) return 89;
if (level === 50) return 91;
if (level === 51) return 93;
if (level === 52) return 94;
if (level === 53) return 96;
if (level === 54) return 98;
if (level === 55) return 100;
if (level === 56) return 101;
if (level === 57) return 103;
if (level === 58) return 105;
if (level === 59) return 107;
if (level === 60) return 108;
if (level === 61) return 110;
if (level === 62) return 112;
if (level === 63) return 114;
if (level === 64) return 115;
if (level === 65) return 117;
if (level === 66) return 119;
if (level === 67) return 121;
if (level === 68) return 122;
if (level === 69) return 124;
if (level === 70) return 126;
if (level === 71) return 128;
if (level === 72) return 129;
if (level === 73) return 131;
if (level === 74) return 133;
if (level === 75) return 135;
if (level === 76) return 136;
if (level === 77) return 138;
if (level === 78) return 140;
if (level === 79) return 142;
if (level === 80) return 143;
if (level === 81) return 145;
if (level === 82) return 147;
if (level === 83) return 149;
// Levels 84-100 all have 150 enemies
if (level >= 84) return 150;
return 150; // Default fallback
}
var enemiesNeededForLevel = getEnemyCountForLevel(currentLevel);
// Create player
var player = game.addChild(new Player());
player.x = 1024;
player.y = 2400;
// Create UI elements
// Add lives display above ship health
var shipLivesTxt = new Text2('Lives: ' + playerLives, {
size: 36,
fill: 0x00FF00
});
shipLivesTxt.anchor.set(0.5, 0);
shipLivesTxt.x = 270;
shipLivesTxt.y = 30;
LK.gui.topLeft.addChild(shipLivesTxt);
var shipHealthTxt = new Text2('Ship Health', {
size: 36,
fill: 0x00FF00
});
shipHealthTxt.anchor.set(0, 0);
shipHealthTxt.x = 150;
shipHealthTxt.y = 90;
LK.gui.topLeft.addChild(shipHealthTxt);
// Create player health bar positioned below the text with spacing
var playerHealthBarBg = LK.getAsset('healthBarBg', {
anchorX: 0,
anchorY: 0,
scaleX: 0.4,
scaleY: 0.8
});
playerHealthBarBg.x = 150;
playerHealthBarBg.y = 160;
LK.gui.topLeft.addChild(playerHealthBarBg);
var playerHealthBar = LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0,
scaleX: 0.4,
scaleY: 0.8
});
playerHealthBar.x = 150;
playerHealthBar.y = 160;
LK.gui.topLeft.addChild(playerHealthBar);
// Create HP text inside the health bar
var shipHPTxt = new Text2(player.health + '/' + player.maxHealth, {
size: 36,
fill: 0x000000
});
shipHPTxt.anchor.set(0.5, 0.5);
shipHPTxt.x = 270;
shipHPTxt.y = 180;
LK.gui.topLeft.addChild(shipHPTxt);
var levelTxt = new Text2('Level: 1', {
size: 84,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0.5, 0);
levelTxt.x = 1024;
levelTxt.y = 140;
game.addChild(levelTxt);
var scoreTxt = new Text2('Score: 0', {
size: 36,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(1, 0);
scoreTxt.x = -80;
scoreTxt.y = 90;
LK.gui.topRight.addChild(scoreTxt);
var pointsNeededTxt = new Text2('Next Level: 10', {
size: 36,
fill: 0xFFFF00
});
pointsNeededTxt.anchor.set(0.5, 0);
pointsNeededTxt.x = 1024;
pointsNeededTxt.y = 240;
game.addChild(pointsNeededTxt);
var weaponTxt = new Text2('Weapon: Single', {
size: 36,
fill: 0xFFFF00
});
weaponTxt.anchor.set(0.5, 0);
weaponTxt.x = 1024;
weaponTxt.y = 330;
game.addChild(weaponTxt);
var coinsTxt = new Text2('Coins: 0', {
size: 36,
fill: 0xFFD700
});
coinsTxt.anchor.set(1, 0);
coinsTxt.x = -80;
coinsTxt.y = 50;
LK.gui.topRight.addChild(coinsTxt);
// Helper functions
function getShootCooldown() {
if (currentLevel >= 80) return 15; // Rapid fire - much faster
if (currentLevel >= 50) return 25; // Triple fire - faster
if (currentLevel >= 20) return 20; // Dual fire - faster
return 15; // Single fire - much faster
}
function updateWeaponDisplay() {
if (currentLevel >= 80) {
weaponTxt.setText('Weapon: Rapid Fire');
} else if (currentLevel >= 50) {
weaponTxt.setText('Weapon: Triple Fire');
} else if (currentLevel >= 20) {
weaponTxt.setText('Weapon: Dual Fire');
} else {
weaponTxt.setText('Weapon: Single Fire');
}
}
function updatePointsNeededDisplay() {
if (isBossLevel) {
pointsNeededTxt.setText('BOSS LEVEL');
} else {
var pointsNeeded = enemiesNeededForLevel - enemiesKilled;
pointsNeededTxt.setText('Enemies Left: ' + pointsNeeded);
}
}
function isBossLevelCheck(level) {
return level % 5 === 0;
}
function openMarket() {
if (marketOpen) return;
marketOpen = true;
gamePaused = true;
// Create market overlay
marketOverlay = LK.getAsset('vo', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 15,
scaleY: 20
});
marketOverlay.x = 1024;
marketOverlay.y = 1366;
game.addChild(marketOverlay);
// Market title
var marketTitle = new Text2('MARKET - Level ' + currentLevel, {
size: 60,
fill: 0x000000
});
marketTitle.anchor.set(0.5, 0.5);
marketTitle.x = 1024;
marketTitle.y = 800;
game.addChild(marketTitle);
marketButtons.push(marketTitle);
// Health upgrade button
var healthUpgrade = new Text2('Health +1 (50 coins)', {
size: 36,
fill: 0x000000
});
healthUpgrade.anchor.set(0.5, 0.5);
healthUpgrade.x = 1024;
healthUpgrade.y = 1000;
healthUpgrade.interactive = true;
healthUpgrade.down = function () {
if (playerCoins >= 50) {
playerCoins -= 50;
maxPlayerHealth++;
player.maxHealth = maxPlayerHealth;
player.health = Math.min(player.health + 1, maxPlayerHealth);
storage.playerCoins = playerCoins;
storage.maxPlayerHealth = maxPlayerHealth;
closeMarket();
}
};
game.addChild(healthUpgrade);
marketButtons.push(healthUpgrade);
// Damage upgrade button
var damageUpgrade = new Text2('Damage +1 (100 coins)', {
size: 36,
fill: 0x000000
});
damageUpgrade.anchor.set(0.5, 0.5);
damageUpgrade.x = 1024;
damageUpgrade.y = 1200;
damageUpgrade.interactive = true;
damageUpgrade.down = function () {
if (playerCoins >= 100) {
playerCoins -= 100;
storage.playerCoins = playerCoins;
closeMarket();
}
};
game.addChild(damageUpgrade);
marketButtons.push(damageUpgrade);
// Close button
var closeButton = new Text2('Close Market', {
size: 36,
fill: 0xFF0000
});
closeButton.anchor.set(0.5, 0.5);
closeButton.x = 1024;
closeButton.y = 1600;
closeButton.interactive = true;
closeButton.down = function () {
closeMarket();
};
game.addChild(closeButton);
marketButtons.push(closeButton);
}
function closeMarket() {
if (!marketOpen) return;
marketOpen = false;
gamePaused = false;
if (marketOverlay) {
marketOverlay.destroy();
marketOverlay = null;
}
for (var i = 0; i < marketButtons.length; i++) {
marketButtons[i].destroy();
}
marketButtons = [];
}
function spawnBoss() {
if (currentBoss) return;
currentBoss = new Boss();
currentBoss.x = 1024;
currentBoss.y = 400;
game.addChild(currentBoss);
// Create boss health bar (green bar only in text area)
bossHealthBar = LK.getAsset('bossHealthBar', {
anchorX: 0,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.8
});
bossHealthBar.x = currentBoss.x - 150;
bossHealthBar.y = currentBoss.y - 170;
game.addChild(bossHealthBar);
// Create boss health text
bossHealthTxt = new Text2(currentBoss.health + '/' + currentBoss.maxHealth, {
size: 84,
fill: 0x000000
});
bossHealthTxt.anchor.set(0.5, 0.5);
bossHealthTxt.x = currentBoss.x;
bossHealthTxt.y = currentBoss.y - 170;
game.addChild(bossHealthTxt);
// Spawn enemies alongside boss
var enemyCount = Math.min(Math.floor(currentLevel / 5) + 2, 6);
for (var e = 0; e < enemyCount; e++) {
var enemy = new Enemy();
enemy.x = Math.random() * 1648 + 200;
enemy.y = Math.random() * 300 + 50;
enemies.push(enemy);
game.addChild(enemy);
}
}
function spawnEnemy() {
if (isBossLevel) return;
var enemy = new Enemy();
enemy.x = Math.random() * 1648 + 200;
enemy.y = -50;
enemies.push(enemy);
game.addChild(enemy);
}
function nextLevel() {
currentLevel++;
storage.currentLevel = currentLevel;
// Check if we've reached the maximum level
if (currentLevel > 100) {
LK.showYouWin();
return;
}
levelTxt.setText('Level: ' + currentLevel);
isBossLevel = isBossLevelCheck(currentLevel);
enemiesKilled = 0;
levelCompleteTimer = 0;
enemiesNeededForLevel = getEnemyCountForLevel(currentLevel);
updateWeaponDisplay();
updatePointsNeededDisplay();
// Award life at checkpoints (every 5 levels)
if (currentLevel % 5 === 0) {
playerLives++;
storage.playerLives = playerLives;
// Save score at checkpoint
storage.checkpointScore = LK.getScore();
storage.checkpointLevel = currentLevel;
LK.effects.flashScreen(0x00ff00, 500); // Green flash for life gained
}
if (isBossLevel) {
spawnBoss();
}
}
// Input handling
var dragActive = false;
var gamePaused = false;
var pauseOverlay = null;
game.down = function (x, y, obj) {
// Right click pause - check for right mouse button (button 2)
if (obj && obj.event && obj.event.button === 2) {
obj.event.preventDefault(); // Prevent context menu
gamePaused = !gamePaused;
if (gamePaused) {
// Create pause overlay
pauseOverlay = new Text2('GAME PAUSED\nRight-click to resume', {
size: 36,
fill: 0xFFFFFF
});
pauseOverlay.anchor.set(0.5, 0.5);
pauseOverlay.x = 1024;
pauseOverlay.y = 1366;
game.addChild(pauseOverlay);
} else {
// Remove pause overlay
if (pauseOverlay) {
pauseOverlay.destroy();
pauseOverlay = null;
}
}
return;
}
if (gamePaused) return;
dragActive = true;
var targetX = Math.max(40, Math.min(2008, x));
var targetY = Math.max(200, Math.min(2600, y));
player.x = targetX;
player.y = targetY;
player.shoot();
};
game.move = function (x, y, obj) {
if (gamePaused) return;
// Direct mouse movement - ship follows mouse cursor immediately
var targetX = Math.max(40, Math.min(2008, x));
var targetY = Math.max(200, Math.min(2600, y));
player.x = targetX;
player.y = targetY;
};
game.up = function (x, y, obj) {
if (gamePaused) return;
dragActive = false;
};
// Initialize level
updateWeaponDisplay();
updatePointsNeededDisplay();
levelTxt.setText('Level: ' + currentLevel);
isBossLevel = isBossLevelCheck(currentLevel);
if (isBossLevel) {
spawnBoss();
}
// Play background music
LK.playMusic('gamemusic');
// Main game loop
game.update = function () {
// Skip updates if game is paused
if (gamePaused) return;
// Update player
player.update();
// Update UI
scoreTxt.setText('Score: ' + LK.getScore());
coinsTxt.setText('Coins: ' + playerCoins);
shipLivesTxt.setText('Lives: ' + playerLives);
shipHPTxt.setText(player.health + '/' + player.maxHealth);
// Update player health bar
var healthPercent = player.health / player.maxHealth;
playerHealthBar.scaleX = healthPercent * 0.4;
// Set color based on health
if (player.health <= 10) {
shipHealthTxt.fill = 0xff0000; // Red for low health
} else if (player.health <= 25) {
shipHealthTxt.fill = 0xffff00; // Yellow for medium health
} else {
shipHealthTxt.fill = 0x00ff00; // Green for full health
}
updatePointsNeededDisplay();
// Check for market opening (every 10 levels)
if (currentLevel % 10 === 0 && !marketOpen && !isBossLevel) {
openMarket();
return;
}
// Auto-shoot continuously
player.shoot();
// Spawn enemies for non-boss levels
if (!isBossLevel) {
enemySpawnTimer++;
var spawnRate = Math.max(20, 60 - Math.floor(currentLevel / 5) * 5);
if (enemySpawnTimer >= spawnRate) {
enemySpawnTimer = 0;
spawnEnemy();
}
} else if (isBossLevel && currentBoss) {
// Spawn additional enemies during boss levels at reduced rate
enemySpawnTimer++;
if (enemySpawnTimer >= 90) {
enemySpawnTimer = 0;
spawnEnemy();
}
}
// Update player bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
if (!bullet || !bullet.update) {
playerBullets.splice(i, 1);
continue;
}
bullet.update(); // Update bullet position
if (bullet.lastY === undefined) bullet.lastY = bullet.y;
// Remove bullets that go off screen
if (bullet.lastY >= -10 && bullet.y < -10) {
bullet.destroy();
playerBullets.splice(i, 1);
continue;
}
// Check collision with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
LK.getSound('enemyHit').play();
LK.setScore(LK.getScore() + 10);
playerCoins += 2;
storage.playerCoins = playerCoins;
bullet.destroy();
playerBullets.splice(i, 1);
enemy.destroy();
enemies.splice(j, 1);
enemiesKilled++;
break;
}
}
// Check collision with boss
if (currentBoss && bullet.intersects(currentBoss)) {
LK.getSound('bossHit').play();
LK.setScore(LK.getScore() + 50);
playerCoins += 5;
storage.playerCoins = playerCoins;
bullet.destroy();
playerBullets.splice(i, 1);
currentBoss.health--;
// Update boss health bar
if (bossHealthBar && bossHealthTxt) {
var healthPercent = currentBoss.health / currentBoss.maxHealth;
bossHealthBar.scaleX = healthPercent * 0.5;
bossHealthTxt.setText(currentBoss.health + '/' + currentBoss.maxHealth);
}
if (currentBoss.health <= 0) {
currentBoss.defeated = true;
currentBoss.destroy();
currentBoss = null;
if (bossHealthBar) {
bossHealthBar.destroy();
bossHealthBar = null;
}
if (bossHealthBarBg) {
bossHealthBarBg.destroy();
bossHealthBarBg = null;
}
if (bossHealthTxt) {
bossHealthTxt.destroy();
bossHealthTxt = null;
}
if (bossHealthTextBg) {
bossHealthTextBg.destroy();
bossHealthTextBg = null;
}
LK.setScore(LK.getScore() + 500);
levelCompleteTimer = 180; // 3 seconds delay before next level
}
break;
}
if (bullet.parent) {
bullet.lastY = bullet.y;
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
if (!bullet || !bullet.update) {
enemyBullets.splice(i, 1);
continue;
}
bullet.update(); // Update bullet position
if (bullet.lastY === undefined) bullet.lastY = bullet.y;
// Remove bullets that go off screen
if (bullet.lastY <= 2742 && bullet.y > 2742) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check collision with player
if (bullet.intersects(player)) {
player.takeDamage();
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
if (bullet.parent) {
bullet.lastY = bullet.y;
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (!enemy || !enemy.update) {
enemies.splice(i, 1);
continue;
}
// Additional safety check before calling update
if (enemy && typeof enemy.update === 'function' && enemy.parent) {
enemy.update(); // Update enemy position and behavior
if (enemy.lastY === undefined) enemy.lastY = enemy.y;
// Remove enemies that go off screen
if (enemy.lastY <= 2742 && enemy.y > 2742) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
// Check collision with player
if (enemy.intersects(player)) {
player.takeDamage();
enemy.destroy();
enemies.splice(i, 1);
continue;
}
if (enemy.parent) {
enemy.lastY = enemy.y;
}
} else {
// Remove invalid enemy objects
enemies.splice(i, 1);
}
}
// Update boss
if (currentBoss && currentBoss.parent) {
currentBoss.update();
// Update boss health bar position to follow boss
if (bossHealthBar) {
bossHealthBar.x = currentBoss.x - 150;
bossHealthBar.y = currentBoss.y - 170;
}
if (bossHealthTxt) {
bossHealthTxt.x = currentBoss.x;
bossHealthTxt.y = currentBoss.y - 170;
}
// Check collision with player
if (currentBoss.intersects(player)) {
player.takeDamage();
}
}
// Level progression
if (!isBossLevel && enemiesKilled >= enemiesNeededForLevel) {
levelCompleteTimer++;
if (levelCompleteTimer >= 180) {
// 3 seconds delay
nextLevel();
}
} else if (isBossLevel && currentBoss && currentBoss.defeated) {
if (levelCompleteTimer > 0) {
levelCompleteTimer--;
if (levelCompleteTimer <= 0) {
nextLevel();
}
}
} else if (isBossLevel && !currentBoss && levelCompleteTimer > 0) {
levelCompleteTimer--;
if (levelCompleteTimer <= 0) {
nextLevel();
}
} else if (isBossLevel && !currentBoss) {
// Boss level but no boss exists, spawn one
spawnBoss();
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossNumber = Math.ceil(currentLevel / 10);
var assetName = bossNumber === 1 ? 'boss' : 'boss' + bossNumber;
var graphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
// Boss health scaling based on specific progression table
function getBossHealthForLevel(level) {
if (level <= 5) return 5;
if (level <= 10) return 10;
if (level <= 15) return 20;
if (level <= 20) return 35;
if (level <= 25) return 50;
if (level <= 30) return 70;
if (level <= 35) return 90;
if (level <= 40) return 115;
if (level <= 45) return 140;
if (level <= 50) return 170;
if (level <= 55) return 200;
if (level <= 60) return 235;
if (level <= 65) return 270;
if (level <= 70) return 310;
if (level <= 75) return 350;
if (level <= 80) return 395;
if (level <= 85) return 440;
if (level <= 90) return 460;
if (level <= 95) return 480;
if (level <= 100) return 500;
return 500; // Default fallback
}
self.maxHealth = getBossHealthForLevel(currentLevel);
self.health = self.maxHealth;
self.lastShot = 0;
self.shootCooldown = 240; // 4 seconds at 60fps for better balance
self.direction = 1;
self.defeated = false;
self.update = function () {
// Boss movement pattern
self.x += self.speed * self.direction;
if (self.x <= 200 || self.x >= 1848) {
self.direction *= -1;
}
// Boss shooting pattern - can shoot back-to-back
self.lastShot++;
if (self.lastShot >= self.shootCooldown) {
self.lastShot = Math.max(0, self.shootCooldown - 120); // Allow back-to-back shooting by reducing cooldown
// Shoot 3 bullets at the same position
for (var i = 0; i < 3; i++) {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + 75;
enemyBullets.push(bullet);
game.addChild(bullet);
}
}
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1.5;
self.health = 1;
self.maxHealth = 1;
self.lastShot = 0;
self.shootCooldown = 300; // 5 seconds at 60fps for better balance with faster player
self.direction = Math.random() < 0.5 ? -1 : 1; // Random initial direction
self.horizontalSpeed = 2;
self.update = function () {
self.y += self.speed;
// Enhanced horizontal movement with direction changes
self.x += self.horizontalSpeed * self.direction;
// Change direction when hitting screen edges or randomly
if (self.x <= 200 || self.x >= 1848 || Math.random() < 0.005) {
self.direction *= -1;
}
// Bullet dodging behavior - check for nearby player bullets
for (var b = 0; b < playerBullets.length; b++) {
var bullet = playerBullets[b];
if (bullet && bullet.parent) {
// Calculate distance to bullet
var distX = Math.abs(bullet.x - self.x);
var distY = Math.abs(bullet.y - self.y);
// If bullet is close and approaching, try to dodge
if (distX < 100 && distY < 150 && bullet.y < self.y) {
// Determine dodge direction based on bullet position
if (bullet.x < self.x && self.x < 1800) {
// Bullet is to the left, move right
self.x += 8;
} else if (bullet.x > self.x && self.x > 248) {
// Bullet is to the right, move left
self.x -= 8;
}
// Slight vertical evasion
if (Math.random() < 0.3) {
self.y += Math.random() < 0.5 ? -3 : 3;
}
break; // Only dodge one bullet per frame
}
}
}
// Faster shooting - single shot but more frequent
self.lastShot++;
if (self.lastShot >= 180 && Math.random() < 0.08) {
// Reduced cooldown from 300 to 180, increased chance from 0.04 to 0.08
self.lastShot = 0; // Reset cooldown completely to prevent back-to-back shooting
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + 30;
enemyBullets.push(bullet);
game.addChild(bullet);
}
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = maxPlayerHealth;
self.maxHealth = maxPlayerHealth;
self.lastShot = 0;
self.invulnerable = false;
self.invulnerableTimer = 0;
self.shoot = function () {
if (self.lastShot > 0) return;
self.lastShot = getShootCooldown();
// Shooting sound removed
if (currentLevel >= 80) {
// Rapid fire - single bullet but faster
var bullet = new PlayerBullet();
bullet.x = self.x;
bullet.y = self.y - 40;
playerBullets.push(bullet);
game.addChild(bullet);
} else if (currentLevel >= 50) {
// Triple fire
for (var i = -1; i <= 1; i++) {
var bullet = new PlayerBullet();
bullet.x = self.x + i * 25;
bullet.y = self.y - 40;
playerBullets.push(bullet);
game.addChild(bullet);
}
} else if (currentLevel >= 20) {
// Dual fire
for (var i = -1; i <= 1; i += 2) {
var bullet = new PlayerBullet();
bullet.x = self.x + i * 20;
bullet.y = self.y - 40;
playerBullets.push(bullet);
game.addChild(bullet);
}
} else {
// Single fire
var bullet = new PlayerBullet();
bullet.x = self.x;
bullet.y = self.y - 40;
playerBullets.push(bullet);
game.addChild(bullet);
}
};
self.takeDamage = function () {
if (self.invulnerable) return;
self.health--;
self.invulnerable = true;
self.invulnerableTimer = 120; // 2 seconds invulnerability
LK.getSound('playerHit').play();
LK.effects.flashObject(self, 0xff0000, 500);
if (self.health <= 0) {
playerLives--;
storage.playerLives = playerLives;
self.health = self.maxHealth; // Reset health for next life
if (playerLives <= 0) {
LK.showGameOver();
} else {
// Respawn at starting position
self.x = 1024;
self.y = 2400;
// Restore from last checkpoint
var checkpointScore = storage.checkpointScore || 0;
var checkpointLevel = storage.checkpointLevel || 1;
// Reset to checkpoint level and score
currentLevel = checkpointLevel;
storage.currentLevel = currentLevel;
LK.setScore(checkpointScore);
// Reset level state
enemiesKilled = 0;
enemiesNeededForLevel = getEnemyCountForLevel(currentLevel);
isBossLevel = isBossLevelCheck(currentLevel);
levelCompleteTimer = 0;
// Update UI
levelTxt.setText('Level: ' + currentLevel);
updateWeaponDisplay();
updatePointsNeededDisplay();
livesTxt.setText('Lives: ' + playerLives);
// Clear all enemies and bullets
for (var e = enemies.length - 1; e >= 0; e--) {
enemies[e].destroy();
enemies.splice(e, 1);
}
for (var eb = enemyBullets.length - 1; eb >= 0; eb--) {
enemyBullets[eb].destroy();
enemyBullets.splice(eb, 1);
}
// Clear boss if exists
if (currentBoss) {
currentBoss.destroy();
currentBoss = null;
if (bossHealthBar) {
bossHealthBar.destroy();
bossHealthBar = null;
}
if (bossHealthBarBg) {
bossHealthBarBg.destroy();
bossHealthBarBg = null;
}
if (bossHealthTxt) {
bossHealthTxt.destroy();
bossHealthTxt = null;
}
if (bossHealthTextBg) {
bossHealthTextBg.destroy();
bossHealthTextBg = null;
}
}
}
}
};
self.update = function () {
if (self.lastShot > 0) {
self.lastShot--;
}
if (self.invulnerable) {
self.invulnerableTimer--;
if (self.invulnerableTimer <= 0) {
self.invulnerable = false;
}
}
};
return self;
});
var PlayerBullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -12;
self.update = function () {
self.y += self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game state variables
var currentLevel = storage.currentLevel || 1;
var playerLives = storage.playerLives || 3;
var playerCoins = storage.playerCoins || 0;
var maxPlayerHealth = storage.maxPlayerHealth || 50;
var marketOpen = false;
var marketOverlay = null;
var marketButtons = [];
// Initialize checkpoint data
if (!storage.checkpointScore) storage.checkpointScore = 0;
if (!storage.checkpointLevel) storage.checkpointLevel = 1;
var playerBullets = [];
var enemyBullets = [];
var enemies = [];
var currentBoss = null;
var bossHealthBar = null;
var bossHealthBarBg = null;
var bossHealthTxt = null;
var bossHealthTextBg = null;
var isBossLevel = false;
var enemySpawnTimer = 0;
var levelCompleteTimer = 0;
var enemiesKilled = 0;
// Set exact enemy count per level based on specific requirements
function getEnemyCountForLevel(level) {
if (level <= 0) return 5;
if (level === 1) return 5;
if (level === 2) return 7;
if (level === 3) return 9;
if (level === 4) return 10;
if (level === 5) return 12;
if (level === 6) return 14;
if (level === 7) return 16;
if (level === 8) return 17;
if (level === 9) return 19;
if (level === 10) return 21;
if (level === 11) return 23;
if (level === 12) return 24;
if (level === 13) return 26;
if (level === 14) return 28;
if (level === 15) return 30;
if (level === 16) return 31;
if (level === 17) return 33;
if (level === 18) return 35;
if (level === 19) return 37;
if (level === 20) return 38;
if (level === 21) return 40;
if (level === 22) return 42;
if (level === 23) return 44;
if (level === 24) return 45;
if (level === 25) return 47;
if (level === 26) return 49;
if (level === 27) return 51;
if (level === 28) return 52;
if (level === 29) return 54;
if (level === 30) return 56;
if (level === 31) return 58;
if (level === 32) return 59;
if (level === 33) return 61;
if (level === 34) return 63;
if (level === 35) return 65;
if (level === 36) return 66;
if (level === 37) return 68;
if (level === 38) return 70;
if (level === 39) return 72;
if (level === 40) return 73;
if (level === 41) return 75;
if (level === 42) return 77;
if (level === 43) return 79;
if (level === 44) return 80;
if (level === 45) return 82;
if (level === 46) return 84;
if (level === 47) return 86;
if (level === 48) return 87;
if (level === 49) return 89;
if (level === 50) return 91;
if (level === 51) return 93;
if (level === 52) return 94;
if (level === 53) return 96;
if (level === 54) return 98;
if (level === 55) return 100;
if (level === 56) return 101;
if (level === 57) return 103;
if (level === 58) return 105;
if (level === 59) return 107;
if (level === 60) return 108;
if (level === 61) return 110;
if (level === 62) return 112;
if (level === 63) return 114;
if (level === 64) return 115;
if (level === 65) return 117;
if (level === 66) return 119;
if (level === 67) return 121;
if (level === 68) return 122;
if (level === 69) return 124;
if (level === 70) return 126;
if (level === 71) return 128;
if (level === 72) return 129;
if (level === 73) return 131;
if (level === 74) return 133;
if (level === 75) return 135;
if (level === 76) return 136;
if (level === 77) return 138;
if (level === 78) return 140;
if (level === 79) return 142;
if (level === 80) return 143;
if (level === 81) return 145;
if (level === 82) return 147;
if (level === 83) return 149;
// Levels 84-100 all have 150 enemies
if (level >= 84) return 150;
return 150; // Default fallback
}
var enemiesNeededForLevel = getEnemyCountForLevel(currentLevel);
// Create player
var player = game.addChild(new Player());
player.x = 1024;
player.y = 2400;
// Create UI elements
// Add lives display above ship health
var shipLivesTxt = new Text2('Lives: ' + playerLives, {
size: 36,
fill: 0x00FF00
});
shipLivesTxt.anchor.set(0.5, 0);
shipLivesTxt.x = 270;
shipLivesTxt.y = 30;
LK.gui.topLeft.addChild(shipLivesTxt);
var shipHealthTxt = new Text2('Ship Health', {
size: 36,
fill: 0x00FF00
});
shipHealthTxt.anchor.set(0, 0);
shipHealthTxt.x = 150;
shipHealthTxt.y = 90;
LK.gui.topLeft.addChild(shipHealthTxt);
// Create player health bar positioned below the text with spacing
var playerHealthBarBg = LK.getAsset('healthBarBg', {
anchorX: 0,
anchorY: 0,
scaleX: 0.4,
scaleY: 0.8
});
playerHealthBarBg.x = 150;
playerHealthBarBg.y = 160;
LK.gui.topLeft.addChild(playerHealthBarBg);
var playerHealthBar = LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0,
scaleX: 0.4,
scaleY: 0.8
});
playerHealthBar.x = 150;
playerHealthBar.y = 160;
LK.gui.topLeft.addChild(playerHealthBar);
// Create HP text inside the health bar
var shipHPTxt = new Text2(player.health + '/' + player.maxHealth, {
size: 36,
fill: 0x000000
});
shipHPTxt.anchor.set(0.5, 0.5);
shipHPTxt.x = 270;
shipHPTxt.y = 180;
LK.gui.topLeft.addChild(shipHPTxt);
var levelTxt = new Text2('Level: 1', {
size: 84,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0.5, 0);
levelTxt.x = 1024;
levelTxt.y = 140;
game.addChild(levelTxt);
var scoreTxt = new Text2('Score: 0', {
size: 36,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(1, 0);
scoreTxt.x = -80;
scoreTxt.y = 90;
LK.gui.topRight.addChild(scoreTxt);
var pointsNeededTxt = new Text2('Next Level: 10', {
size: 36,
fill: 0xFFFF00
});
pointsNeededTxt.anchor.set(0.5, 0);
pointsNeededTxt.x = 1024;
pointsNeededTxt.y = 240;
game.addChild(pointsNeededTxt);
var weaponTxt = new Text2('Weapon: Single', {
size: 36,
fill: 0xFFFF00
});
weaponTxt.anchor.set(0.5, 0);
weaponTxt.x = 1024;
weaponTxt.y = 330;
game.addChild(weaponTxt);
var coinsTxt = new Text2('Coins: 0', {
size: 36,
fill: 0xFFD700
});
coinsTxt.anchor.set(1, 0);
coinsTxt.x = -80;
coinsTxt.y = 50;
LK.gui.topRight.addChild(coinsTxt);
// Helper functions
function getShootCooldown() {
if (currentLevel >= 80) return 15; // Rapid fire - much faster
if (currentLevel >= 50) return 25; // Triple fire - faster
if (currentLevel >= 20) return 20; // Dual fire - faster
return 15; // Single fire - much faster
}
function updateWeaponDisplay() {
if (currentLevel >= 80) {
weaponTxt.setText('Weapon: Rapid Fire');
} else if (currentLevel >= 50) {
weaponTxt.setText('Weapon: Triple Fire');
} else if (currentLevel >= 20) {
weaponTxt.setText('Weapon: Dual Fire');
} else {
weaponTxt.setText('Weapon: Single Fire');
}
}
function updatePointsNeededDisplay() {
if (isBossLevel) {
pointsNeededTxt.setText('BOSS LEVEL');
} else {
var pointsNeeded = enemiesNeededForLevel - enemiesKilled;
pointsNeededTxt.setText('Enemies Left: ' + pointsNeeded);
}
}
function isBossLevelCheck(level) {
return level % 5 === 0;
}
function openMarket() {
if (marketOpen) return;
marketOpen = true;
gamePaused = true;
// Create market overlay
marketOverlay = LK.getAsset('vo', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 15,
scaleY: 20
});
marketOverlay.x = 1024;
marketOverlay.y = 1366;
game.addChild(marketOverlay);
// Market title
var marketTitle = new Text2('MARKET - Level ' + currentLevel, {
size: 60,
fill: 0x000000
});
marketTitle.anchor.set(0.5, 0.5);
marketTitle.x = 1024;
marketTitle.y = 800;
game.addChild(marketTitle);
marketButtons.push(marketTitle);
// Health upgrade button
var healthUpgrade = new Text2('Health +1 (50 coins)', {
size: 36,
fill: 0x000000
});
healthUpgrade.anchor.set(0.5, 0.5);
healthUpgrade.x = 1024;
healthUpgrade.y = 1000;
healthUpgrade.interactive = true;
healthUpgrade.down = function () {
if (playerCoins >= 50) {
playerCoins -= 50;
maxPlayerHealth++;
player.maxHealth = maxPlayerHealth;
player.health = Math.min(player.health + 1, maxPlayerHealth);
storage.playerCoins = playerCoins;
storage.maxPlayerHealth = maxPlayerHealth;
closeMarket();
}
};
game.addChild(healthUpgrade);
marketButtons.push(healthUpgrade);
// Damage upgrade button
var damageUpgrade = new Text2('Damage +1 (100 coins)', {
size: 36,
fill: 0x000000
});
damageUpgrade.anchor.set(0.5, 0.5);
damageUpgrade.x = 1024;
damageUpgrade.y = 1200;
damageUpgrade.interactive = true;
damageUpgrade.down = function () {
if (playerCoins >= 100) {
playerCoins -= 100;
storage.playerCoins = playerCoins;
closeMarket();
}
};
game.addChild(damageUpgrade);
marketButtons.push(damageUpgrade);
// Close button
var closeButton = new Text2('Close Market', {
size: 36,
fill: 0xFF0000
});
closeButton.anchor.set(0.5, 0.5);
closeButton.x = 1024;
closeButton.y = 1600;
closeButton.interactive = true;
closeButton.down = function () {
closeMarket();
};
game.addChild(closeButton);
marketButtons.push(closeButton);
}
function closeMarket() {
if (!marketOpen) return;
marketOpen = false;
gamePaused = false;
if (marketOverlay) {
marketOverlay.destroy();
marketOverlay = null;
}
for (var i = 0; i < marketButtons.length; i++) {
marketButtons[i].destroy();
}
marketButtons = [];
}
function spawnBoss() {
if (currentBoss) return;
currentBoss = new Boss();
currentBoss.x = 1024;
currentBoss.y = 400;
game.addChild(currentBoss);
// Create boss health bar (green bar only in text area)
bossHealthBar = LK.getAsset('bossHealthBar', {
anchorX: 0,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.8
});
bossHealthBar.x = currentBoss.x - 150;
bossHealthBar.y = currentBoss.y - 170;
game.addChild(bossHealthBar);
// Create boss health text
bossHealthTxt = new Text2(currentBoss.health + '/' + currentBoss.maxHealth, {
size: 84,
fill: 0x000000
});
bossHealthTxt.anchor.set(0.5, 0.5);
bossHealthTxt.x = currentBoss.x;
bossHealthTxt.y = currentBoss.y - 170;
game.addChild(bossHealthTxt);
// Spawn enemies alongside boss
var enemyCount = Math.min(Math.floor(currentLevel / 5) + 2, 6);
for (var e = 0; e < enemyCount; e++) {
var enemy = new Enemy();
enemy.x = Math.random() * 1648 + 200;
enemy.y = Math.random() * 300 + 50;
enemies.push(enemy);
game.addChild(enemy);
}
}
function spawnEnemy() {
if (isBossLevel) return;
var enemy = new Enemy();
enemy.x = Math.random() * 1648 + 200;
enemy.y = -50;
enemies.push(enemy);
game.addChild(enemy);
}
function nextLevel() {
currentLevel++;
storage.currentLevel = currentLevel;
// Check if we've reached the maximum level
if (currentLevel > 100) {
LK.showYouWin();
return;
}
levelTxt.setText('Level: ' + currentLevel);
isBossLevel = isBossLevelCheck(currentLevel);
enemiesKilled = 0;
levelCompleteTimer = 0;
enemiesNeededForLevel = getEnemyCountForLevel(currentLevel);
updateWeaponDisplay();
updatePointsNeededDisplay();
// Award life at checkpoints (every 5 levels)
if (currentLevel % 5 === 0) {
playerLives++;
storage.playerLives = playerLives;
// Save score at checkpoint
storage.checkpointScore = LK.getScore();
storage.checkpointLevel = currentLevel;
LK.effects.flashScreen(0x00ff00, 500); // Green flash for life gained
}
if (isBossLevel) {
spawnBoss();
}
}
// Input handling
var dragActive = false;
var gamePaused = false;
var pauseOverlay = null;
game.down = function (x, y, obj) {
// Right click pause - check for right mouse button (button 2)
if (obj && obj.event && obj.event.button === 2) {
obj.event.preventDefault(); // Prevent context menu
gamePaused = !gamePaused;
if (gamePaused) {
// Create pause overlay
pauseOverlay = new Text2('GAME PAUSED\nRight-click to resume', {
size: 36,
fill: 0xFFFFFF
});
pauseOverlay.anchor.set(0.5, 0.5);
pauseOverlay.x = 1024;
pauseOverlay.y = 1366;
game.addChild(pauseOverlay);
} else {
// Remove pause overlay
if (pauseOverlay) {
pauseOverlay.destroy();
pauseOverlay = null;
}
}
return;
}
if (gamePaused) return;
dragActive = true;
var targetX = Math.max(40, Math.min(2008, x));
var targetY = Math.max(200, Math.min(2600, y));
player.x = targetX;
player.y = targetY;
player.shoot();
};
game.move = function (x, y, obj) {
if (gamePaused) return;
// Direct mouse movement - ship follows mouse cursor immediately
var targetX = Math.max(40, Math.min(2008, x));
var targetY = Math.max(200, Math.min(2600, y));
player.x = targetX;
player.y = targetY;
};
game.up = function (x, y, obj) {
if (gamePaused) return;
dragActive = false;
};
// Initialize level
updateWeaponDisplay();
updatePointsNeededDisplay();
levelTxt.setText('Level: ' + currentLevel);
isBossLevel = isBossLevelCheck(currentLevel);
if (isBossLevel) {
spawnBoss();
}
// Play background music
LK.playMusic('gamemusic');
// Main game loop
game.update = function () {
// Skip updates if game is paused
if (gamePaused) return;
// Update player
player.update();
// Update UI
scoreTxt.setText('Score: ' + LK.getScore());
coinsTxt.setText('Coins: ' + playerCoins);
shipLivesTxt.setText('Lives: ' + playerLives);
shipHPTxt.setText(player.health + '/' + player.maxHealth);
// Update player health bar
var healthPercent = player.health / player.maxHealth;
playerHealthBar.scaleX = healthPercent * 0.4;
// Set color based on health
if (player.health <= 10) {
shipHealthTxt.fill = 0xff0000; // Red for low health
} else if (player.health <= 25) {
shipHealthTxt.fill = 0xffff00; // Yellow for medium health
} else {
shipHealthTxt.fill = 0x00ff00; // Green for full health
}
updatePointsNeededDisplay();
// Check for market opening (every 10 levels)
if (currentLevel % 10 === 0 && !marketOpen && !isBossLevel) {
openMarket();
return;
}
// Auto-shoot continuously
player.shoot();
// Spawn enemies for non-boss levels
if (!isBossLevel) {
enemySpawnTimer++;
var spawnRate = Math.max(20, 60 - Math.floor(currentLevel / 5) * 5);
if (enemySpawnTimer >= spawnRate) {
enemySpawnTimer = 0;
spawnEnemy();
}
} else if (isBossLevel && currentBoss) {
// Spawn additional enemies during boss levels at reduced rate
enemySpawnTimer++;
if (enemySpawnTimer >= 90) {
enemySpawnTimer = 0;
spawnEnemy();
}
}
// Update player bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
if (!bullet || !bullet.update) {
playerBullets.splice(i, 1);
continue;
}
bullet.update(); // Update bullet position
if (bullet.lastY === undefined) bullet.lastY = bullet.y;
// Remove bullets that go off screen
if (bullet.lastY >= -10 && bullet.y < -10) {
bullet.destroy();
playerBullets.splice(i, 1);
continue;
}
// Check collision with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
LK.getSound('enemyHit').play();
LK.setScore(LK.getScore() + 10);
playerCoins += 2;
storage.playerCoins = playerCoins;
bullet.destroy();
playerBullets.splice(i, 1);
enemy.destroy();
enemies.splice(j, 1);
enemiesKilled++;
break;
}
}
// Check collision with boss
if (currentBoss && bullet.intersects(currentBoss)) {
LK.getSound('bossHit').play();
LK.setScore(LK.getScore() + 50);
playerCoins += 5;
storage.playerCoins = playerCoins;
bullet.destroy();
playerBullets.splice(i, 1);
currentBoss.health--;
// Update boss health bar
if (bossHealthBar && bossHealthTxt) {
var healthPercent = currentBoss.health / currentBoss.maxHealth;
bossHealthBar.scaleX = healthPercent * 0.5;
bossHealthTxt.setText(currentBoss.health + '/' + currentBoss.maxHealth);
}
if (currentBoss.health <= 0) {
currentBoss.defeated = true;
currentBoss.destroy();
currentBoss = null;
if (bossHealthBar) {
bossHealthBar.destroy();
bossHealthBar = null;
}
if (bossHealthBarBg) {
bossHealthBarBg.destroy();
bossHealthBarBg = null;
}
if (bossHealthTxt) {
bossHealthTxt.destroy();
bossHealthTxt = null;
}
if (bossHealthTextBg) {
bossHealthTextBg.destroy();
bossHealthTextBg = null;
}
LK.setScore(LK.getScore() + 500);
levelCompleteTimer = 180; // 3 seconds delay before next level
}
break;
}
if (bullet.parent) {
bullet.lastY = bullet.y;
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
if (!bullet || !bullet.update) {
enemyBullets.splice(i, 1);
continue;
}
bullet.update(); // Update bullet position
if (bullet.lastY === undefined) bullet.lastY = bullet.y;
// Remove bullets that go off screen
if (bullet.lastY <= 2742 && bullet.y > 2742) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check collision with player
if (bullet.intersects(player)) {
player.takeDamage();
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
if (bullet.parent) {
bullet.lastY = bullet.y;
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (!enemy || !enemy.update) {
enemies.splice(i, 1);
continue;
}
// Additional safety check before calling update
if (enemy && typeof enemy.update === 'function' && enemy.parent) {
enemy.update(); // Update enemy position and behavior
if (enemy.lastY === undefined) enemy.lastY = enemy.y;
// Remove enemies that go off screen
if (enemy.lastY <= 2742 && enemy.y > 2742) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
// Check collision with player
if (enemy.intersects(player)) {
player.takeDamage();
enemy.destroy();
enemies.splice(i, 1);
continue;
}
if (enemy.parent) {
enemy.lastY = enemy.y;
}
} else {
// Remove invalid enemy objects
enemies.splice(i, 1);
}
}
// Update boss
if (currentBoss && currentBoss.parent) {
currentBoss.update();
// Update boss health bar position to follow boss
if (bossHealthBar) {
bossHealthBar.x = currentBoss.x - 150;
bossHealthBar.y = currentBoss.y - 170;
}
if (bossHealthTxt) {
bossHealthTxt.x = currentBoss.x;
bossHealthTxt.y = currentBoss.y - 170;
}
// Check collision with player
if (currentBoss.intersects(player)) {
player.takeDamage();
}
}
// Level progression
if (!isBossLevel && enemiesKilled >= enemiesNeededForLevel) {
levelCompleteTimer++;
if (levelCompleteTimer >= 180) {
// 3 seconds delay
nextLevel();
}
} else if (isBossLevel && currentBoss && currentBoss.defeated) {
if (levelCompleteTimer > 0) {
levelCompleteTimer--;
if (levelCompleteTimer <= 0) {
nextLevel();
}
}
} else if (isBossLevel && !currentBoss && levelCompleteTimer > 0) {
levelCompleteTimer--;
if (levelCompleteTimer <= 0) {
nextLevel();
}
} else if (isBossLevel && !currentBoss) {
// Boss level but no boss exists, spawn one
spawnBoss();
}
};