Code edit (1 edits merged)
Please save this source code
User prompt
delete the healthBar asset
User prompt
Please fix the bug: 'healthBarImg is not defined' in or related to this line: 'LK.gui.top.addChild(healthBarImg);' Line Number: 331
Code edit (3 edits merged)
Please save this source code
User prompt
Include 5 heart icons in the assets as health indicators. Replacing these images in the asset folder should update them live in the game
User prompt
When I change the health bar and background in the assets, it should also change in the game.
User prompt
Add the health bar and background image to the game assets folder.
User prompt
"Make the health section display 5 heart icons
User prompt
"Add elements to the screen that display the score and high score values.
User prompt
Organize the UI elements (Health, Enemies, Level, Score, High Score) neatly across the top of the screen so they don’t overlap and are easy to read.
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'storage.get is not a function' in or related to this line: 'var highScore = storage.get('highScore') || 0;' Line Number: 295 ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Please fix the bug: 'storage is not defined' in or related to this line: 'var highScore = storage.get('highScore') || 0;' Line Number: 294 ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Display the current score and the player's highest score (high score) at the top-left corner of the screen.
User prompt
The boss should have a unique health bar that is visible to the player when the boss is present.
User prompt
The boss should appear at the top center of the screen and remain stationary." "The boss should be larger in size compared to regular enemies." "The boss must have special skills such as area attacks, bullet waves, or directional lasers." "A boss health bar should be displayed at the top of the screen."
User prompt
Please fix the bug: 'TypeError: Cannot read properties of null (reading 'isAlive')' in or related to this line: 'if (player.isAlive && bossShip.isAlive && circlesIntersect(bossShip, player)) {' Line Number: 572
User prompt
The boss should remain stationary and not move toward the player. The boss should have a health bar displayed at the top of the screen. The boss should have unique skills or abilities.
User prompt
Introduce a boss fight every 5 levels
User prompt
Reduce the fire rate of enemy spaceships to make the game more balanced.
User prompt
If an enemy crosses into the player’s area, the player loses 1 health. If it’s the last enemy of the level, the game should still progress to the next level even if the enemy wasn’t destroyed.
User prompt
enemies spaceship and me spaceship should be bigger
User prompt
The player starts with 5 health points displayed in the top-left corner. Every time they get hit by a bullet, 1 health is lost. When health reaches 0, the player dies.
Code edit (1 edits merged)
Please save this source code
User prompt
The current level should be displayed in the top-left corner of the screen.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Boss ship class
var BossShip = Container.expand(function () {
var self = Container.call(this);
// Attach boss asset (use enemyShip but larger, or a different color if available)
var boss = self.attachAsset('enemyShip', {
width: 400,
height: 400,
color: 0xff0000,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5
});
self.radius = 200;
self.speed = 3;
self.dirX = (Math.random() - 0.5) * 2;
self.waveOffset = Math.random() * Math.PI * 2;
self.isAlive = true;
self.health = 20;
self.maxHealth = 20;
self.lastY = undefined;
// Boss update
self.update = function () {
self.y += self.speed;
self.x += Math.sin(LK.ticks / 30 + self.waveOffset) * 4 + self.dirX;
// Clamp to screen
var minX = 200;
var maxX = 2048 - 200;
if (self.x < minX) {
self.x = minX;
self.dirX = Math.abs(self.dirX);
}
if (self.x > maxX) {
self.x = maxX;
self.dirX = -Math.abs(self.dirX);
}
};
// Flash on hit
self.flash = function () {
tween(boss, {
tint: 0xffffff
}, {
duration: 80,
onFinish: function onFinish() {
tween(boss, {
tint: 0xff0000
}, {
duration: 120
});
}
});
};
return self;
});
// Enemy bullet class
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
// Attach bullet asset (red, 24x48)
var bullet = self.attachAsset('enemyBullet', {
width: 24,
height: 48,
color: 0xff4444,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 14;
self.radius = 12;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Enemy ship class
var EnemyShip = Container.expand(function () {
var self = Container.call(this);
// Attach enemy asset (ellipse, magenta, 220x220)
var enemy = self.attachAsset('enemyShip', {
width: 220,
height: 220,
color: 0xff33cc,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5
});
self.radius = 110;
self.speed = 4 + Math.random() * 2; // Vary speed
self.dirX = (Math.random() - 0.5) * 2; // Slight horizontal drift
// For movement patterns
self.waveOffset = Math.random() * Math.PI * 2;
// For collision
self.isAlive = true;
// Update per frame
self.update = function () {
// Move down, with a sine wave pattern
self.y += self.speed;
self.x += Math.sin(LK.ticks / 30 + self.waveOffset) * 2 + self.dirX;
// Prevent enemy from moving outside the horizontal screen area
var minX = 60;
var maxX = 2048 - 60;
if (self.x < minX) {
self.x = minX;
self.dirX = Math.abs(self.dirX); // bounce right
}
if (self.x > maxX) {
self.x = maxX;
self.dirX = -Math.abs(self.dirX); // bounce left
}
};
// Flash on hit
self.flash = function () {
tween(enemy, {
tint: 0xffffff
}, {
duration: 80,
onFinish: function onFinish() {
tween(enemy, {
tint: 0xff33cc
}, {
duration: 120
});
}
});
};
return self;
});
// Player bullet class
var PlayerBullet = Container.expand(function () {
var self = Container.call(this);
// Attach bullet asset (yellow, 30x60)
var bullet = self.attachAsset('playerBullet', {
width: 30,
height: 60,
color: 0xffe066,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -22;
self.radius = 15;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player spaceship class
var PlayerShip = Container.expand(function () {
var self = Container.call(this);
// Attach ship asset (box, blue, 220x220)
var ship = self.attachAsset('playerShip', {
width: 220,
height: 220,
color: 0x3399ff,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
// Ship properties
self.radius = 110; // for collision
self.isAlive = true;
// Ship flash effect on hit
self.flash = function () {
tween(ship, {
tint: 0xff3333
}, {
duration: 100,
onFinish: function onFinish() {
tween(ship, {
tint: 0x3399ff
}, {
duration: 200
});
}
});
};
return self;
});
// Powerup class
var PowerUp = Container.expand(function () {
var self = Container.call(this);
// Attach powerup asset (green ellipse, 60x60)
var pu = self.attachAsset('powerUp', {
width: 60,
height: 60,
color: 0x44ff88,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5
});
self.radius = 30;
self.speed = 6;
self.update = function () {
self.y += self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000010
});
/****
* Game Code
****/
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var PLAYER_START_X = GAME_WIDTH / 2;
var PLAYER_START_Y = GAME_HEIGHT - 350;
// Level system variables
var currentLevel = 1;
var enemiesPerLevel = 6; // base number of enemies for level 1
var enemiesToSpawn = enemiesPerLevel;
var enemiesKilledThisLevel = 0;
var enemyScoreBase = 10; // base score for level 1
var enemyScoreThisLevel = enemyScoreBase;
// Game state
var player;
var enemies = [];
var playerBullets = [];
var enemyBullets = [];
var powerUps = [];
var dragNode = null;
var lastScore = 0;
var scoreTxt;
var powerUpActive = false;
var powerUpTimer = 0;
var enemySpawnTimer = 0;
var enemyFireTimer = 0;
var powerUpSpawnTimer = 0;
var gameOver = false;
// Boss state
var bossActive = false;
var bossShip = null;
var bossHealthBar = null;
// Score display
scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Health system
var playerHealth = 5;
var healthTxt = new Text2('Health: 5', {
size: 50,
fill: 0xFF4444
});
healthTxt.anchor.set(0, 0);
healthTxt.x = 120;
healthTxt.y = 0;
LK.gui.top.addChild(healthTxt);
// Level display (top left, avoid top left 100x100)
var levelTxt = new Text2('Level 1', {
size: 40,
fill: 0xFFD700
});
levelTxt.anchor.set(0, 0);
levelTxt.x = 120;
levelTxt.y = 100;
LK.gui.top.addChild(levelTxt);
// Enemies left display (top left, avoid top left 100x100)
var enemiesLeftTxt = new Text2('Enemies: 0', {
size: 50,
fill: 0xFF8888
});
enemiesLeftTxt.anchor.set(0, 0);
enemiesLeftTxt.x = 120;
enemiesLeftTxt.y = 60;
LK.gui.top.addChild(enemiesLeftTxt);
// Initialize player
player = new PlayerShip();
player.x = PLAYER_START_X;
player.y = PLAYER_START_Y;
game.addChild(player);
// Touch/move controls
function handleMove(x, y, obj) {
if (dragNode && player.isAlive) {
// Clamp to game area, avoid top 100px (menu)
var px = Math.max(60, Math.min(GAME_WIDTH - 60, x));
var py = Math.max(200, Math.min(GAME_HEIGHT - 60, y));
dragNode.x = px;
dragNode.y = py;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
if (player.isAlive) {
dragNode = player;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Helper: collision check (circle vs circle)
function circlesIntersect(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
var dist = Math.sqrt(dx * dx + dy * dy);
return dist < a.radius + b.radius;
}
// Helper: spawn enemy
function spawnEnemy() {
var enemy = new EnemyShip();
// Spawn in random horizontal position, avoid edges
enemy.x = 120 + Math.random() * (GAME_WIDTH - 240);
enemy.y = -100;
enemies.push(enemy);
game.addChild(enemy);
}
// Helper: spawn enemy bullet
function spawnEnemyBullet(enemy) {
var bullet = new EnemyBullet();
bullet.x = enemy.x;
bullet.y = enemy.y + 60;
enemyBullets.push(bullet);
game.addChild(bullet);
}
// Helper: spawn player bullet
function spawnPlayerBullet() {
var bullet = new PlayerBullet();
bullet.x = player.x;
bullet.y = player.y - 80;
playerBullets.push(bullet);
game.addChild(bullet);
}
// Helper: spawn powerup
function spawnPowerUp() {
var pu = new PowerUp();
pu.x = 120 + Math.random() * (GAME_WIDTH - 240);
pu.y = -60;
powerUps.push(pu);
game.addChild(pu);
}
// Powerup effect: double shot
function activatePowerUp() {
powerUpActive = true;
powerUpTimer = LK.ticks + 360; // 6 seconds at 60fps
// Flash player ship green
tween(player, {
tint: 0x44ff88
}, {
duration: 200,
onFinish: function onFinish() {
tween(player, {
tint: 0x3399ff
}, {
duration: 200
});
}
});
}
// Main game update
game.update = function () {
if (!player.isAlive) {
return;
}
// --- Player auto-fire ---
if (LK.ticks % (powerUpActive ? 7 : 14) === 0) {
if (powerUpActive) {
// Double shot: two bullets
var b1 = new PlayerBullet();
b1.x = player.x - 36;
b1.y = player.y - 80;
playerBullets.push(b1);
game.addChild(b1);
var b2 = new PlayerBullet();
b2.x = player.x + 36;
b2.y = player.y - 80;
playerBullets.push(b2);
game.addChild(b2);
} else {
spawnPlayerBullet();
}
}
// --- Enemy spawn ---
if (!bossActive && enemySpawnTimer <= LK.ticks && enemiesToSpawn > 0) {
spawnEnemy();
enemiesToSpawn--;
// Escalate spawn rate
var minDelay = 24,
maxDelay = 60;
var delay = Math.max(minDelay, maxDelay - Math.floor(LK.getScore() / 10) * 4);
enemySpawnTimer = LK.ticks + delay;
// Update enemies left UI
enemiesLeftTxt.setText('Enemies: ' + (enemiesToSpawn + enemies.length));
}
// --- Enemy fire ---
if (!bossActive && enemyFireTimer <= LK.ticks && enemies.length > 0) {
// Pick random enemy to fire
var idx = Math.floor(Math.random() * enemies.length);
if (enemies[idx]) {
spawnEnemyBullet(enemies[idx]);
}
// Reduce fire rate: increase min and max fire delay for better balance
var minFire = 36,
// was 18
maxFire = 100; // was 50
var fireDelay = Math.max(minFire, maxFire - Math.floor(LK.getScore() / 10) * 3);
enemyFireTimer = LK.ticks + fireDelay;
}
// --- Powerup spawn ---
if (powerUpSpawnTimer <= LK.ticks) {
if (Math.random() < 0.18) {
// ~18% chance
spawnPowerUp();
}
powerUpSpawnTimer = LK.ticks + 300 + Math.floor(Math.random() * 200);
}
// --- Powerup timer ---
if (powerUpActive && LK.ticks > powerUpTimer) {
powerUpActive = false;
}
// --- Update player bullets ---
for (var i = playerBullets.length - 1; i >= 0; i--) {
var b = playerBullets[i];
b.update();
// Remove if off screen
if (b.y < -80) {
b.destroy();
playerBullets.splice(i, 1);
continue;
}
// Check collision with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var e = enemies[j];
if (e.isAlive && circlesIntersect(b, e)) {
e.isAlive = false;
e.flash();
b.destroy();
playerBullets.splice(i, 1);
e.destroy();
enemies.splice(j, 1);
// Score (scaled by level)
LK.setScore(LK.getScore() + enemyScoreThisLevel);
scoreTxt.setText(LK.getScore());
// Track kills for level progression
enemiesKilledThisLevel++;
enemiesLeftTxt.setText('Enemies: ' + (enemiesToSpawn + enemies.length));
// If all enemies for this level are killed, progress to next level
if (enemiesKilledThisLevel >= enemiesPerLevel) {
// Boss fight every 5 levels
if ((currentLevel + 1) % 5 === 0) {
// Spawn boss
bossActive = true;
bossShip = new BossShip();
bossShip.x = GAME_WIDTH / 2;
bossShip.y = -200;
game.addChild(bossShip);
// Add boss health bar
if (bossHealthBar) {
LK.gui.top.removeChild(bossHealthBar);
}
bossHealthBar = new Text2('Boss: ' + bossShip.health + '/' + bossShip.maxHealth, {
size: 60,
fill: 0xFF2222
});
bossHealthBar.anchor.set(0.5, 0);
bossHealthBar.x = GAME_WIDTH / 2;
bossHealthBar.y = 180;
LK.gui.top.addChild(bossHealthBar);
// UI
levelTxt.setText('Boss Level ' + (currentLevel + 1));
enemiesLeftTxt.setText('Boss Fight!');
LK.effects.flashScreen(0xffcc00, 600);
} else {
// Next level!
currentLevel++;
enemiesPerLevel = Math.floor(enemiesPerLevel * 1.25) + 1; // escalate number of enemies
enemiesToSpawn = enemiesPerLevel;
enemiesKilledThisLevel = 0;
enemyScoreThisLevel = enemyScoreBase * Math.pow(2, currentLevel - 1);
// Update UI
levelTxt.setText('Level ' + currentLevel);
enemiesLeftTxt.setText('Enemies: ' + (enemiesToSpawn + enemies.length));
// Optionally: flash screen or give feedback
LK.effects.flashScreen(0x00ffcc, 400);
}
}
break;
}
}
}
// --- Boss fight logic ---
if (bossActive && bossShip && bossShip.isAlive) {
bossShip.update();
// Boss fires more frequently
if (LK.ticks % 18 === 0) {
var bossBullet = new EnemyBullet();
bossBullet.x = bossShip.x;
bossBullet.y = bossShip.y + 180;
enemyBullets.push(bossBullet);
game.addChild(bossBullet);
}
// Update boss health bar
if (bossHealthBar) {
bossHealthBar.setText('Boss: ' + bossShip.health + '/' + bossShip.maxHealth);
}
// Check player bullet collision with boss
for (var i = playerBullets.length - 1; i >= 0; i--) {
var b = playerBullets[i];
if (bossShip.isAlive && circlesIntersect(bossShip, b)) {
bossShip.health--;
bossShip.flash();
b.destroy();
playerBullets.splice(i, 1);
if (bossShip.health <= 0) {
bossShip.isAlive = false;
bossActive = false;
// Remove boss health bar
if (bossHealthBar) {
LK.gui.top.removeChild(bossHealthBar);
bossHealthBar = null;
}
// Score for boss
LK.setScore(LK.getScore() + 100 * currentLevel);
scoreTxt.setText(LK.getScore());
// Next level
currentLevel++;
enemiesPerLevel = Math.floor(enemiesPerLevel * 1.25) + 1;
enemiesToSpawn = enemiesPerLevel;
enemiesKilledThisLevel = 0;
enemyScoreThisLevel = enemyScoreBase * Math.pow(2, currentLevel - 1);
levelTxt.setText('Level ' + currentLevel);
enemiesLeftTxt.setText('Enemies: ' + enemiesPerLevel);
LK.effects.flashScreen(0x00ffcc, 600);
// Remove boss
bossShip.destroy();
bossShip = null;
}
break;
}
}
// Boss collision with player
if (player.isAlive && bossShip.isAlive && circlesIntersect(bossShip, player)) {
bossShip.isAlive = false;
bossActive = false;
if (bossHealthBar) {
LK.gui.top.removeChild(bossHealthBar);
bossHealthBar = null;
}
player.flash();
playerHealth--;
healthTxt.setText('Health: ' + playerHealth);
LK.effects.flashScreen(0xff0000, 400);
if (playerHealth <= 0) {
player.isAlive = false;
tween(player, {
alpha: 0.2
}, {
duration: 400
});
LK.setTimeout(function () {
LK.showGameOver();
}, 900);
}
bossShip.destroy();
bossShip = null;
}
}
// --- Update enemy bullets ---
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var eb = enemyBullets[i];
eb.update();
if (eb.y > GAME_HEIGHT + 80) {
eb.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check collision with player
if (player.isAlive && circlesIntersect(eb, player)) {
eb.destroy();
enemyBullets.splice(i, 1);
player.flash();
playerHealth--;
healthTxt.setText('Health: ' + playerHealth);
LK.effects.flashScreen(0xff0000, 400);
if (playerHealth <= 0) {
// Game over
player.isAlive = false;
tween(player, {
alpha: 0.2
}, {
duration: 400
});
LK.setTimeout(function () {
LK.showGameOver();
}, 900);
break;
}
}
}
// --- Update enemies ---
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
// Track lastY for crossing detection
if (typeof e.lastY === "undefined") e.lastY = e.y;
e.update();
// If enemy crosses into player's area (y > PLAYER_START_Y - 110, i.e. bottom of player ship)
if (e.lastY <= PLAYER_START_Y - 110 && e.y > PLAYER_START_Y - 110) {
// Only trigger if enemy is alive
if (e.isAlive && player.isAlive) {
e.isAlive = false;
tween(e, {}, {
duration: 120,
onFinish: function onFinish() {
e.destroy();
}
});
enemies.splice(i, 1);
playerHealth--;
healthTxt.setText('Health: ' + playerHealth);
LK.effects.flashScreen(0xff0000, 400);
// Level progression if this was the last enemy for the level
enemiesLeftTxt.setText('Enemies: ' + (enemiesToSpawn + enemies.length));
if (enemiesKilledThisLevel + 1 >= enemiesPerLevel && enemiesToSpawn === 0 && enemies.length === 0) {
// Next level!
currentLevel++;
enemiesPerLevel = Math.floor(enemiesPerLevel * 1.25) + 1;
enemiesToSpawn = enemiesPerLevel;
enemiesKilledThisLevel = 0;
enemyScoreThisLevel = enemyScoreBase * Math.pow(2, currentLevel - 1);
// Update UI
levelTxt.setText('Level ' + currentLevel);
enemiesLeftTxt.setText('Enemies: ' + (enemiesToSpawn + enemies.length));
LK.effects.flashScreen(0x00ffcc, 400);
} else {
// Only increment kills if not progressing level
enemiesKilledThisLevel++;
}
if (playerHealth <= 0) {
// Game over
player.isAlive = false;
tween(player, {
alpha: 0.2
}, {
duration: 400
});
LK.setTimeout(function () {
LK.showGameOver();
}, 900);
break;
}
continue;
}
}
if (e.y > GAME_HEIGHT + 100) {
e.destroy();
enemies.splice(i, 1);
continue;
}
// Check collision with player
if (player.isAlive && e.isAlive && circlesIntersect(e, player)) {
e.isAlive = false;
e.flash();
player.flash();
tween(e, {}, {
duration: 120,
onFinish: function onFinish() {
e.destroy();
}
});
enemies.splice(i, 1);
playerHealth--;
healthTxt.setText('Health: ' + playerHealth);
LK.effects.flashScreen(0xff0000, 400);
if (playerHealth <= 0) {
// Game over
player.isAlive = false;
tween(player, {
alpha: 0.2
}, {
duration: 400
});
LK.setTimeout(function () {
LK.showGameOver();
}, 900);
break;
}
}
// Update lastY for next frame
e.lastY = e.y;
}
// --- Update powerups ---
for (var i = powerUps.length - 1; i >= 0; i--) {
var pu = powerUps[i];
pu.update();
if (pu.y > GAME_HEIGHT + 80) {
pu.destroy();
powerUps.splice(i, 1);
continue;
}
// Check collision with player
if (player.isAlive && circlesIntersect(pu, player)) {
pu.destroy();
powerUps.splice(i, 1);
activatePowerUp();
}
}
};
// Set initial score
LK.setScore(0);
scoreTxt.setText('0');
// Reset level system
currentLevel = 1;
enemiesPerLevel = 6;
enemiesToSpawn = enemiesPerLevel;
enemiesKilledThisLevel = 0;
enemyScoreThisLevel = enemyScoreBase;
levelTxt.setText('Level 1');
enemiesLeftTxt.setText('Enemies: ' + enemiesPerLevel);
playerHealth = 5;
healthTxt.setText('Health: ' + playerHealth);
// Reset boss state
bossActive = false;
if (bossShip) {
bossShip.destroy();
bossShip = null;
}
if (bossHealthBar) {
LK.gui.top.removeChild(bossHealthBar);
bossHealthBar = null;
} ===================================================================
--- original.js
+++ change.js
@@ -5,8 +5,61 @@
/****
* Classes
****/
+// Boss ship class
+var BossShip = Container.expand(function () {
+ var self = Container.call(this);
+ // Attach boss asset (use enemyShip but larger, or a different color if available)
+ var boss = self.attachAsset('enemyShip', {
+ width: 400,
+ height: 400,
+ color: 0xff0000,
+ shape: 'ellipse',
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.radius = 200;
+ self.speed = 3;
+ self.dirX = (Math.random() - 0.5) * 2;
+ self.waveOffset = Math.random() * Math.PI * 2;
+ self.isAlive = true;
+ self.health = 20;
+ self.maxHealth = 20;
+ self.lastY = undefined;
+ // Boss update
+ self.update = function () {
+ self.y += self.speed;
+ self.x += Math.sin(LK.ticks / 30 + self.waveOffset) * 4 + self.dirX;
+ // Clamp to screen
+ var minX = 200;
+ var maxX = 2048 - 200;
+ if (self.x < minX) {
+ self.x = minX;
+ self.dirX = Math.abs(self.dirX);
+ }
+ if (self.x > maxX) {
+ self.x = maxX;
+ self.dirX = -Math.abs(self.dirX);
+ }
+ };
+ // Flash on hit
+ self.flash = function () {
+ tween(boss, {
+ tint: 0xffffff
+ }, {
+ duration: 80,
+ onFinish: function onFinish() {
+ tween(boss, {
+ tint: 0xff0000
+ }, {
+ duration: 120
+ });
+ }
+ });
+ };
+ return self;
+});
// Enemy bullet class
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
// Attach bullet asset (red, 24x48)
@@ -185,8 +238,12 @@
var enemySpawnTimer = 0;
var enemyFireTimer = 0;
var powerUpSpawnTimer = 0;
var gameOver = false;
+// Boss state
+var bossActive = false;
+var bossShip = null;
+var bossHealthBar = null;
// Score display
scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
@@ -327,9 +384,9 @@
spawnPlayerBullet();
}
}
// --- Enemy spawn ---
- if (enemySpawnTimer <= LK.ticks && enemiesToSpawn > 0) {
+ if (!bossActive && enemySpawnTimer <= LK.ticks && enemiesToSpawn > 0) {
spawnEnemy();
enemiesToSpawn--;
// Escalate spawn rate
var minDelay = 24,
@@ -339,9 +396,9 @@
// Update enemies left UI
enemiesLeftTxt.setText('Enemies: ' + (enemiesToSpawn + enemies.length));
}
// --- Enemy fire ---
- if (enemyFireTimer <= LK.ticks && enemies.length > 0) {
+ if (!bossActive && enemyFireTimer <= LK.ticks && enemies.length > 0) {
// Pick random enemy to fire
var idx = Math.floor(Math.random() * enemies.length);
if (enemies[idx]) {
spawnEnemyBullet(enemies[idx]);
@@ -392,23 +449,126 @@
enemiesKilledThisLevel++;
enemiesLeftTxt.setText('Enemies: ' + (enemiesToSpawn + enemies.length));
// If all enemies for this level are killed, progress to next level
if (enemiesKilledThisLevel >= enemiesPerLevel) {
- // Next level!
+ // Boss fight every 5 levels
+ if ((currentLevel + 1) % 5 === 0) {
+ // Spawn boss
+ bossActive = true;
+ bossShip = new BossShip();
+ bossShip.x = GAME_WIDTH / 2;
+ bossShip.y = -200;
+ game.addChild(bossShip);
+ // Add boss health bar
+ if (bossHealthBar) {
+ LK.gui.top.removeChild(bossHealthBar);
+ }
+ bossHealthBar = new Text2('Boss: ' + bossShip.health + '/' + bossShip.maxHealth, {
+ size: 60,
+ fill: 0xFF2222
+ });
+ bossHealthBar.anchor.set(0.5, 0);
+ bossHealthBar.x = GAME_WIDTH / 2;
+ bossHealthBar.y = 180;
+ LK.gui.top.addChild(bossHealthBar);
+ // UI
+ levelTxt.setText('Boss Level ' + (currentLevel + 1));
+ enemiesLeftTxt.setText('Boss Fight!');
+ LK.effects.flashScreen(0xffcc00, 600);
+ } else {
+ // Next level!
+ currentLevel++;
+ enemiesPerLevel = Math.floor(enemiesPerLevel * 1.25) + 1; // escalate number of enemies
+ enemiesToSpawn = enemiesPerLevel;
+ enemiesKilledThisLevel = 0;
+ enemyScoreThisLevel = enemyScoreBase * Math.pow(2, currentLevel - 1);
+ // Update UI
+ levelTxt.setText('Level ' + currentLevel);
+ enemiesLeftTxt.setText('Enemies: ' + (enemiesToSpawn + enemies.length));
+ // Optionally: flash screen or give feedback
+ LK.effects.flashScreen(0x00ffcc, 400);
+ }
+ }
+ break;
+ }
+ }
+ }
+ // --- Boss fight logic ---
+ if (bossActive && bossShip && bossShip.isAlive) {
+ bossShip.update();
+ // Boss fires more frequently
+ if (LK.ticks % 18 === 0) {
+ var bossBullet = new EnemyBullet();
+ bossBullet.x = bossShip.x;
+ bossBullet.y = bossShip.y + 180;
+ enemyBullets.push(bossBullet);
+ game.addChild(bossBullet);
+ }
+ // Update boss health bar
+ if (bossHealthBar) {
+ bossHealthBar.setText('Boss: ' + bossShip.health + '/' + bossShip.maxHealth);
+ }
+ // Check player bullet collision with boss
+ for (var i = playerBullets.length - 1; i >= 0; i--) {
+ var b = playerBullets[i];
+ if (bossShip.isAlive && circlesIntersect(bossShip, b)) {
+ bossShip.health--;
+ bossShip.flash();
+ b.destroy();
+ playerBullets.splice(i, 1);
+ if (bossShip.health <= 0) {
+ bossShip.isAlive = false;
+ bossActive = false;
+ // Remove boss health bar
+ if (bossHealthBar) {
+ LK.gui.top.removeChild(bossHealthBar);
+ bossHealthBar = null;
+ }
+ // Score for boss
+ LK.setScore(LK.getScore() + 100 * currentLevel);
+ scoreTxt.setText(LK.getScore());
+ // Next level
currentLevel++;
- enemiesPerLevel = Math.floor(enemiesPerLevel * 1.25) + 1; // escalate number of enemies
+ enemiesPerLevel = Math.floor(enemiesPerLevel * 1.25) + 1;
enemiesToSpawn = enemiesPerLevel;
enemiesKilledThisLevel = 0;
enemyScoreThisLevel = enemyScoreBase * Math.pow(2, currentLevel - 1);
- // Update UI
levelTxt.setText('Level ' + currentLevel);
- enemiesLeftTxt.setText('Enemies: ' + (enemiesToSpawn + enemies.length));
- // Optionally: flash screen or give feedback
- LK.effects.flashScreen(0x00ffcc, 400);
+ enemiesLeftTxt.setText('Enemies: ' + enemiesPerLevel);
+ LK.effects.flashScreen(0x00ffcc, 600);
+ // Remove boss
+ bossShip.destroy();
+ bossShip = null;
}
break;
}
}
+ // Boss collision with player
+ if (player.isAlive && bossShip.isAlive && circlesIntersect(bossShip, player)) {
+ bossShip.isAlive = false;
+ bossActive = false;
+ if (bossHealthBar) {
+ LK.gui.top.removeChild(bossHealthBar);
+ bossHealthBar = null;
+ }
+ player.flash();
+ playerHealth--;
+ healthTxt.setText('Health: ' + playerHealth);
+ LK.effects.flashScreen(0xff0000, 400);
+ if (playerHealth <= 0) {
+ player.isAlive = false;
+ tween(player, {
+ alpha: 0.2
+ }, {
+ duration: 400
+ });
+ LK.setTimeout(function () {
+ LK.showGameOver();
+ }, 900);
+ }
+ bossShip.destroy();
+ bossShip = null;
+ }
}
// --- Update enemy bullets ---
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var eb = enemyBullets[i];
@@ -560,5 +720,15 @@
enemyScoreThisLevel = enemyScoreBase;
levelTxt.setText('Level 1');
enemiesLeftTxt.setText('Enemies: ' + enemiesPerLevel);
playerHealth = 5;
-healthTxt.setText('Health: ' + playerHealth);
\ No newline at end of file
+healthTxt.setText('Health: ' + playerHealth);
+// Reset boss state
+bossActive = false;
+if (bossShip) {
+ bossShip.destroy();
+ bossShip = null;
+}
+if (bossHealthBar) {
+ LK.gui.top.removeChild(bossHealthBar);
+ bossHealthBar = null;
+}
\ No newline at end of file