/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Enemy: Drone
var Drone = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('drone', {
anchorX: 0.5,
anchorY: 0.5
});
self.hp = 1;
self.shootCooldown = 0;
self.update = function () {
self.x -= 8;
};
return self;
});
// Enemy bullet
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGfx = self.attachAsset('bullet_enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -18;
self.update = function () {
self.x += self.speed;
};
return self;
});
// Explosion effect
var Explosion = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 1
});
// Animate fade out
tween(gfx, {
alpha: 0
}, {
duration: 400,
easing: tween.linear,
onFinish: function onFinish() {
self.destroy();
}
});
return self;
});
// Squad member: Chapter2 J
var J = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('j', {
anchorX: 0.5,
anchorY: 0.5
});
self.hp = 4;
self.role = 'j';
self.shootCooldown = 0;
self.update = function () {};
return self;
});
// Laser bullet for J
var LaserBullet = Container.expand(function () {
var self = Container.call(this);
var laserGfx = self.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
width: 40,
height: 12,
color: 0xff00ff,
shape: 'box'
});
self.speed = 32;
self.update = function () {
self.x += self.speed;
};
return self;
});
// Squad member: Medic
var Medic = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('medic', {
anchorX: 0.5,
anchorY: 0.5
});
self.hp = 3;
self.role = 'medic';
self.update = function () {};
return self;
});
// Player bullet
var PlayerBullet = Container.expand(function (assetOverride) {
var self = Container.call(this);
var assetId = assetOverride || 'bullet_player';
var bulletGfx = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 32;
self.update = function () {
self.x += self.speed;
};
return self;
});
// Powerup: Ammo
var PowerupAmmo = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('powerup_ammo', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
self.x -= 6;
};
return self;
});
// Powerup: Health
var PowerupHealth = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('powerup_health', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
self.x -= 6;
};
return self;
});
// Captain Rhea Morgan (player)
var Rhea = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('rhea', {
anchorX: 0.5,
anchorY: 0.5
});
self.hp = 5;
self.shootCooldown = 0;
self.update = function () {};
return self;
});
// Squad member: Chapter2 S
var S = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('s', {
anchorX: 0.5,
anchorY: 0.5
});
self.hp = 4;
self.role = 's';
self.shootCooldown = 0;
self.update = function () {};
return self;
});
// Squad member: Sniper
var Sniper = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('sniper', {
anchorX: 0.5,
anchorY: 0.5
});
self.hp = 2;
self.role = 'sniper';
self.shootCooldown = 0;
self.update = function () {};
return self;
});
// Enemy: Tank (mini-boss)
var Tank = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('tank', {
anchorX: 0.5,
anchorY: 0.5
});
self.hp = 10;
self.shootCooldown = 0;
self.update = function () {
self.x -= 4;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222831
});
/****
* Game Code
****/
// World War I background image
var battlefieldBg = LK.getAsset('ww1_bg', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732,
alpha: 0.35
});
game.addChild(battlefieldBg);
// Track if chapter 2 background has been swapped
var chapter2BgSwapped = false;
// Play music
// Main character: Captain Rhea Morgan
// Squad member: Medic
// Squad member: Sniper
// Enemy: Drone
// Enemy: Tank
// Bullet: Player
// Bullet: Enemy
// Powerup: Health
// Powerup: Ammo
// Explosion effect
// Sound effects
// Music
LK.playMusic('battle_theme');
// Game state variables
var rhea, medic, sniper;
var mh2; // Track mh2 globally for chapter 2
var squad = [];
var playerBullets = [];
var enemyBullets = [];
var enemies = [];
var powerups = [];
var explosions = [];
var score = 0;
var resources = 0;
var gameOver = false;
var youWin = false;
var dragNode = null;
var lastTouchX = 0,
lastTouchY = 0;
// Used to block all spawns and shooting during boss warning
var bossWarningActive = false;
// Used to pause enemy/powerup spawn at start of Chapter 2
var chapter2PauseTicks = 0;
// Track if chapter 2 has started
var chapter2Started = false;
// UI
var scoreTxt = new Text2('0', {
size: 100,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var hpTxt = new Text2('HP: 5', {
size: 70,
fill: 0xFF5252
});
// Move HP text to the upper left, avoiding the top left 100x100 px reserved area
hpTxt.anchor.set(0, 0);
hpTxt.x = 110; // 10px right of reserved area
hpTxt.y = 10;
LK.gui.topLeft.addChild(hpTxt);
var resourceTxt = new Text2('Res: 0', {
size: 70,
fill: 0xFFD600
});
resourceTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(resourceTxt);
// Initialize player and squad
rhea = new Rhea();
rhea.x = 400;
rhea.y = 2732 / 2;
game.addChild(rhea);
medic = new Medic();
medic.x = rhea.x - 120;
medic.y = rhea.y + 120;
game.addChild(medic);
sniper = new Sniper();
sniper.x = rhea.x - 120;
sniper.y = rhea.y - 120;
game.addChild(sniper);
squad = [rhea, medic, sniper];
// Helper: spawn enemy
function spawnEnemy() {
// Only spawn J and S as enemies in Chapter 2 (never ch2)
if (chapter2Started) {
// If bossSpawned, do not spawn any more enemies (only mh2 is allowed)
if (typeof bossSpawned !== "undefined" && bossSpawned) {
return;
}
// Alternate between J and S, but skip J if bossSpawned
var enemy;
if (LK.ticks % 2 === 0) {
enemy = new J();
enemy.x = 2048 + 120;
enemy.y = 300 + Math.floor(Math.random() * (2732 - 600));
enemy.direction = -1;
enemy.shootCooldown = 40;
enemy.lastX = enemy.x;
} else {
enemy = new S();
enemy.x = -120;
enemy.y = 300 + Math.floor(Math.random() * (2732 - 600));
enemy.direction = 1;
enemy.lastX = enemy.x;
}
// Prevent spawning ch2 as an enemy in chapter 2
if (enemy && enemy.children && enemy.children.length > 0 && enemy.children[0].assetId === 'ch2') {
// Do not add ch2 as an enemy
} else {
enemies.push(enemy);
game.addChild(enemy);
}
} else {
var type = LK.ticks % 300 === 0 ? 'tank' : 'drone';
var enemy;
if (type === 'drone') {
enemy = new Drone();
enemy.x = 2048 + 60;
enemy.y = 300 + Math.floor(Math.random() * (2732 - 600));
} else {
enemy = new Tank();
enemy.x = 2048 + 120;
enemy.y = 400 + Math.floor(Math.random() * (2732 - 800));
}
enemies.push(enemy);
game.addChild(enemy);
}
}
// Helper: spawn powerup
function spawnPowerup() {
// In chapter 2, further increase chance of health powerup
if (typeof bossSpawned !== "undefined" && bossSpawned) {
// No powerups after boss appears
return;
}
var healthChance = chapter2Started ? 0.92 : 0.5; // Increased to 92% in chapter 2
var type = Math.random() < healthChance ? 'health' : 'ammo';
var powerup;
if (type === 'health') {
powerup = new PowerupHealth();
} else {
powerup = new PowerupAmmo();
}
powerup.x = 2048 + 60;
powerup.y = 300 + Math.floor(Math.random() * (2732 - 600));
powerups.push(powerup);
game.addChild(powerup);
}
// Helper: fire player bullet
function firePlayerBullet(from) {
// Use ma2 asset for player bullet in Chapter 2, otherwise use default
var bullet;
if (chapter2Started) {
bullet = new PlayerBullet('ma2');
} else {
bullet = new PlayerBullet();
}
bullet.x = from.x + 70;
bullet.y = from.y;
// Stronger sniper: sniper bullets do more damage
if (from.role === 'sniper') {
bullet.damage = 6;
} else if (chapter2Started && from === rhea) {
bullet.damage = 6; // Increase main character's weapon damage in chapter 2
} else {
bullet.damage = 3;
}
playerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot_player').play();
}
// Helper: fire enemy bullet
function fireEnemyBullet(from) {
var bullet = new EnemyBullet();
bullet.x = from.x - 70;
bullet.y = from.y;
enemyBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot_enemy').play();
}
// Helper: show explosion
function showExplosion(x, y) {
var exp = new Explosion();
exp.x = x;
exp.y = y;
explosions.push(exp);
game.addChild(exp);
LK.getSound('explosion').play();
}
// Helper: update UI
function updateUI() {
scoreTxt.setText(score);
hpTxt.setText('HP: ' + rhea.hp);
resourceTxt.setText('Res: ' + resources);
}
// Handle dragging Rhea (player)
function handleMove(x, y, obj) {
if (dragNode) {
// Clamp to game area
var minY = 120,
maxY = 2732 - 120;
dragNode.y = Math.max(minY, Math.min(maxY, y));
lastTouchX = x;
lastTouchY = y;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only allow dragging Rhea
if (!gameOver && !youWin) {
if (Math.abs(x - rhea.x) < 120 && Math.abs(y - rhea.y) < 120) {
dragNode = rhea;
handleMove(x, y, obj);
}
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Main game update loop
game.update = function () {
if (gameOver || youWin) return;
// Player auto-fire
if (
// Only allow auto-fire if not in chapter 2 pause, or if chapter 2 hasn't started yet, or if not in chapter 2
(!chapter2Started || typeof chapter2PauseTicks !== "undefined" && chapter2PauseTicks === 0
// Block auto-fire during boss warning
) && !bossWarningActive) {
if (rhea.shootCooldown > 0) rhea.shootCooldown--;
if (rhea.shootCooldown === 0) {
firePlayerBullet(rhea);
rhea.shootCooldown = 10;
}
// Sniper auto-fire (slower)
if (sniper.shootCooldown > 0) sniper.shootCooldown--;
if (sniper.shootCooldown === 0) {
firePlayerBullet(sniper);
sniper.shootCooldown = 30;
}
}
// Medic follows Rhea
medic.x += (rhea.x - 120 - medic.x) * 0.15;
medic.y += (rhea.y + 120 - medic.y) * 0.15;
// Sniper follows Rhea
sniper.x += (rhea.x - 120 - sniper.x) * 0.15;
sniper.y += (rhea.y - 120 - sniper.y) * 0.15;
// Spawn enemies
if (typeof chapter2PauseTicks !== "undefined" && chapter2PauseTicks > 0) {
chapter2PauseTicks--;
} else if (!bossWarningActive) {
// Block all spawns during boss warning
// Only spawn if not in chapter2 pause or boss warning
if (LK.ticks % 120 === 0) {
spawnEnemy();
}
// Spawn powerups
if (chapter2Started) {
// In chapter 2, spawn powerups more often (every 90 ticks)
if (LK.ticks % 90 === 0) {
spawnPowerup();
}
} else {
if (LK.ticks % 180 === 0) {
spawnPowerup();
}
}
}
// Update player bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
var b = playerBullets[i];
b.update();
// Remove if off screen
if (b.x > 2048 + 60) {
b.destroy();
playerBullets.splice(i, 1);
continue;
}
// Hit enemy
for (var j = enemies.length - 1; j >= 0; j--) {
var e = enemies[j];
if (b.intersects(e)) {
var dmg = typeof b.damage === "number" ? b.damage : 3;
e.hp -= dmg;
showExplosion(b.x, b.y);
b.destroy();
playerBullets.splice(i, 1);
if (e.hp <= 0) {
showExplosion(e.x, e.y);
score += e instanceof Tank ? 10 : 2;
resources += e instanceof Tank ? 5 : 1;
e.destroy();
enemies.splice(j, 1);
}
updateUI();
break;
}
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var b = enemyBullets[i];
b.update();
if (b.x < -60) {
b.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Hit squad
for (var j = 0; j < squad.length; j++) {
var s = squad[j];
if (b.intersects(s)) {
// If mh2 fired this bullet, do more damage (enemy bullet collision)
if (mh2 && b.parent === mh2) {
s.hp -= mh2.damage || 6;
} else {
s.hp--;
}
showExplosion(b.x, b.y);
b.destroy();
enemyBullets.splice(i, 1);
if (s.hp <= 0) {
showExplosion(s.x, s.y);
if (s === rhea) {
// Game over
gameOver = true;
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
} else {
s.destroy();
squad.splice(j, 1);
}
}
updateUI();
break;
}
}
}
// Laser bullets from J
for (var i = playerBullets.length - 1; i >= 0; i--) {
var b = playerBullets[i];
if (b instanceof LaserBullet) {
b.update();
// Remove if off screen
if (b.x > 2048 + 60) {
b.destroy();
playerBullets.splice(i, 1);
continue;
}
// Hit squad (including Rhea)
for (var j = 0; j < squad.length; j++) {
var s = squad[j];
if (b.intersects(s)) {
s.hp -= 2; // Laser does 2 damage
showExplosion(b.x, b.y);
b.destroy();
playerBullets.splice(i, 1);
if (s.hp <= 0) {
showExplosion(s.x, s.y);
if (s === rhea) {
// Game over
gameOver = true;
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
} else {
s.destroy();
squad.splice(j, 1);
}
}
updateUI();
break;
}
}
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
// Special movement for J and S in chapter 2
if (e.role === 'j' && chapter2Started) {
// J moves left until near player, then stops
if (typeof e.lastX === "undefined") e.lastX = e.x;
if (e.x > rhea.x + 300) {
e.x -= 12;
}
// J fires laser continuously at player from the beginning of chapter 2
if (e.shootCooldown > 0) e.shootCooldown--;
if (e.shootCooldown === 0) {
// If mh2 is present, fire at mh2, otherwise fire at Rhea
var target = rhea;
if (typeof mh2 !== "undefined" && mh2 && mh2.parent) {
target = mh2;
}
var laser = new LaserBullet();
laser.x = e.x - 60;
laser.y = e.y;
// Calculate direction vector from J to target
var dx = target.x - e.x;
var dy = target.y - e.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist === 0) dist = 1;
laser.dirX = dx / dist;
laser.dirY = dy / dist;
laser.speed = 32;
// Override update to move toward target
laser.update = function () {
this.x += this.dirX * this.speed;
this.y += this.dirY * this.speed;
};
playerBullets.push(laser);
game.addChild(laser);
e.shootCooldown = 18; // J fires every 18 ticks (slower fire rate)
}
e.lastX = e.x;
} else if (e.role === 's' && chapter2Started) {
// S moves right until near player, then stops
if (typeof e.lastX === "undefined") e.lastX = e.x;
if (e.x < rhea.x - 300) {
e.x += 12;
}
e.lastX = e.x;
} else {
e.update();
}
// Remove if off screen
if (e.role !== 'j' && e.role !== 's' && (e.x < -200 || e.x > 2048 + 200)) {
e.destroy();
enemies.splice(i, 1);
continue;
}
// Enemy fire (for non-J/S enemies)
if ((!e.role || e.role !== 'j' && e.role !== 's') && e.shootCooldown !== undefined) {
if (e.shootCooldown > 0) e.shootCooldown--;
if (e.shootCooldown === 0) {
// Only fire if on screen
if (e.x < 2048 && e.x > 0) {
fireEnemyBullet(e);
e.shootCooldown = e instanceof Tank ? 30 : 60;
}
}
}
// Collide with player
if (e.intersects(rhea)) {
// If mh2, do more damage
if (mh2 && e === mh2) {
rhea.hp -= mh2.damage || 6;
} else {
rhea.hp -= 2;
}
showExplosion(e.x, e.y);
e.destroy();
enemies.splice(i, 1);
if (rhea.hp <= 0) {
gameOver = true;
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
updateUI();
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
var p = powerups[i];
p.update();
if (p.x < -60) {
p.destroy();
powerups.splice(i, 1);
continue;
}
// Pickup by player
if (p.intersects(rhea)) {
if (p instanceof PowerupHealth) {
rhea.hp = Math.min(rhea.hp + 2, 7);
} else if (p instanceof PowerupAmmo) {
// For MVP, just increase resources
resources += 2;
}
LK.getSound('pickup').play();
p.destroy();
powerups.splice(i, 1);
updateUI();
}
}
// Chapter 2 trigger and Win condition
if (typeof chapter2Started === "undefined") {
chapter2Started = false;
}
// --- BOSS WARNING & MH2 SPAWN LOGIC FOR CHAPTER 2 ---
// Track boss warning and boss spawn state
if (typeof bossWarningShown === "undefined") bossWarningShown = false;
if (typeof bossSpawned === "undefined") bossSpawned = false;
if (typeof bossWarningOverlay === "undefined") bossWarningOverlay = null;
if (typeof bossWarningTimer === "undefined") bossWarningTimer = 0;
// Show boss warning at 40 points in chapter 2, only once
if (chapter2Started && score >= 40 && !bossWarningShown) {
// Show overlay message
bossWarningOverlay = new Container();
var bossWarnBg = LK.getAsset('centerCircle', {
width: 2048,
height: 2732,
color: 0x000000,
shape: 'box',
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
alpha: 1
});
bossWarningOverlay.addChild(bossWarnBg);
var bossWarnTxt = new Text2('THE BOSS IS COMING', {
size: 180,
fill: 0xFFD700,
font: "Impact, 'Arial Black', Tahoma"
});
bossWarnTxt.anchor.set(0.5, 0.5);
bossWarnTxt.x = 2048 / 2;
bossWarnTxt.y = 2732 / 2;
bossWarningOverlay.addChild(bossWarnTxt);
game.addChild(bossWarningOverlay);
bossWarningShown = true;
bossWarningTimer = 120; // Show for 2 seconds (120 ticks)
// Block all spawns and shooting until boss appears
bossWarningActive = true;
}
// Remove boss warning overlay after timer, and spawn mh2 if not already spawned
if (bossWarningShown && bossWarningOverlay && bossWarningTimer > 0) {
bossWarningTimer--;
if (bossWarningTimer === 0) {
if (bossWarningOverlay && bossWarningOverlay.parent) {
bossWarningOverlay.parent.removeChild(bossWarningOverlay);
bossWarningOverlay.destroy();
}
bossWarningOverlay = null;
// Unblock spawns and shooting
bossWarningActive = false;
// Spawn mh2 as boss if not already spawned
if (!bossSpawned) {
// Remove all J, S, and any other enemies except mh2 from enemies array
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] && (enemies[i].role === 'j' || enemies[i].role === 's' || enemies[i] !== mh2)) {
enemies[i].destroy();
enemies.splice(i, 1);
}
}
bossSpawned = true;
allowJSpawning = false;
// Spawn mh2 at boss position (center right)
mh2 = LK.getAsset('mh2', {
anchorX: 0.5,
anchorY: 0.5
});
mh2.x = 2048 - 200;
mh2.y = 2732 / 2;
mh2.hp = 30;
mh2.damage = 12; // Very high damage
mh2.lastX = mh2.x;
mh2.lastY = mh2.y;
mh2.shootCooldown = 30; // Boss fires every 30 ticks
mh2.dir = 1;
mh2.update = function () {
if (typeof this.lastY === "undefined") this.lastY = this.y;
this.y += this.dir * 8;
if (this.y > 2732 - 200) this.dir = -1;
if (this.y < 200) this.dir = 1;
this.lastY = this.y;
// Boss shooting logic
if (typeof this.shootCooldown === "undefined") this.shootCooldown = 30;
if (this.shootCooldown > 0) this.shootCooldown--;
if (this.shootCooldown === 0) {
// Fire a burst of 3 bullets in a spread
for (var i = -1; i <= 1; i++) {
var bullet = new EnemyBullet();
bullet.x = this.x - 70;
bullet.y = this.y + i * 40;
bullet.speed = -24;
bullet.update = function (angle) {
return function () {
this.x += this.speed;
this.y += angle * 8;
};
}(i);
enemyBullets.push(bullet);
game.addChild(bullet);
}
LK.getSound('shoot_enemy').play();
this.shootCooldown = 30;
}
};
// Add boss to enemies for collision/damage logic
enemies.push(mh2);
game.addChild(mh2);
}
}
}
// --- END BOSS LOGIC ---
if (!chapter2Started && score >= 30) {
chapter2Started = true;
// Play S2 as background music in chapter 2
LK.playMusic('s2');
// Swap background to bf2 for chapter 2
if (!chapter2BgSwapped) {
var bf2Bg = LK.getAsset('bf2', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732,
alpha: 0.5
});
// Remove old background
if (battlefieldBg && battlefieldBg.parent) {
battlefieldBg.parent.removeChild(battlefieldBg);
}
game.addChildAt(bf2Bg, 0); // Add at bottom
chapter2BgSwapped = true;
}
// Change Rhea's asset to ch2 for chapter 2 (main character is now ch2)
if (rhea && rhea.children && rhea.children.length > 0) {
// Remove old asset
var oldGfx = rhea.children[0];
rhea.removeChild(oldGfx);
// Add new ch2 asset
var newGfx = rhea.attachAsset('ch2', {
anchorX: 0.5,
anchorY: 0.5
});
// Optionally update rhea's role/assetId for clarity
rhea.role = 'ch2';
rhea.assetId = 'ch2';
}
chapter2PauseTicks = 120; // 2 seconds at 60fps
// Remove medic and sniper from squad and game
if (medic && medic.parent) {
medic.parent.removeChild(medic);
medic.destroy();
}
if (sniper && sniper.parent) {
sniper.parent.removeChild(sniper);
sniper.destroy();
}
// Remove medic and sniper from squad array
for (var i = squad.length - 1; i >= 0; i--) {
if (squad[i] === medic || squad[i] === sniper) {
squad.splice(i, 1);
}
}
// Remove medic, sniper, and any ch2 from enemies array if present
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === medic || enemies[i] === sniper || enemies[i].children && enemies[i].children.length > 0 && enemies[i].children[0].assetId === 'ch2') {
enemies[i].destroy();
enemies.splice(i, 1);
}
}
// In chapter 2, set attackers to be J and S only (handled in spawnEnemy)
// Create a black overlay
var chapter2Overlay = new Container();
var blackBg = LK.getAsset('centerCircle', {
width: 2048,
height: 2732,
color: 0x000000,
shape: 'box',
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
alpha: 1
});
chapter2Overlay.addChild(blackBg);
// Show chapter 2 message
var chapter2Txt = new Text2('Chapter 2: Firestorm Rising', {
size: 120,
fill: 0x00BFFF,
font: "Impact, 'Arial Black', Tahoma"
});
chapter2Txt.anchor.set(0.5, 0.5);
chapter2Txt.x = 2048 / 2;
chapter2Txt.y = 2732 / 2;
chapter2Overlay.addChild(chapter2Txt);
game.addChild(chapter2Overlay);
// Fade out the overlay and message after 2 seconds
tween(chapter2Overlay, {
alpha: 0
}, {
duration: 2000,
easing: tween.linear,
onFinish: function onFinish() {
chapter2Overlay.destroy();
}
});
// Remove all non-J/S/ch2 enemies from the field at the start of Chapter 2
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
// Remove any enemy that is not 'j' or 's' or is a ch2 (since player is ch2 now)
if (!(e.role === 'j' || e.role === 's') || e !== rhea && e.children && e.children.length > 0 && e.children[0].assetId === 'ch2') {
e.destroy();
enemies.splice(i, 1);
}
}
// Remove J and S spawn logic after boss warning, only allow mh2 to appear after warning
// (No J or S spawn, only mh2 will appear after boss warning)
if (bossSpawned) {
// Remove all squad members except Rhea (main character)
for (var i = squad.length - 1; i >= 0; i--) {
if (squad[i] !== rhea) {
if (squad[i].parent) squad[i].parent.removeChild(squad[i]);
squad[i].destroy();
squad.splice(i, 1);
}
}
// Remove all enemies except mh2
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] !== mh2) {
enemies[i].destroy();
enemies.splice(i, 1);
}
}
// Block all spawns except mh2 (no-op, as mh2 is already spawned)
}
}
// Game over when mh2 (boss) is defeated in chapter 2
if (bossSpawned && mh2 && typeof mh2.hp === "number" && mh2.hp <= 0) {
gameOver = true;
LK.effects.flashScreen(0x000000, 1000);
var bossDefeatedTxt = new Text2('New Boss of the Game', {
size: 180,
fill: 0xFFD700,
font: "Impact, 'Arial Black', Tahoma"
});
bossDefeatedTxt.anchor.set(0.5, 0.5);
bossDefeatedTxt.x = 2048 / 2;
bossDefeatedTxt.y = 2732 / 2;
game.addChild(bossDefeatedTxt);
// End game after a short delay to show the message
LK.setTimeout(function () {
LK.showGameOver();
}, 1600);
return;
}
// Win condition: score threshold
if (score >= 50) {
youWin = true;
LK.effects.flashScreen(0x00ff00, 1000);
// Show 'You are a Real Player' as a gesture
var playerTxt = new Text2('You are a Real Player', {
size: 140,
fill: 0xFFD700,
font: "Impact, 'Arial Black', Tahoma"
});
playerTxt.anchor.set(0.5, 0.5);
playerTxt.x = 2048 / 2;
playerTxt.y = 2732 / 2;
game.addChild(playerTxt);
LK.showYouWin();
return;
}
};
// Initial UI update
updateUI(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Enemy: Drone
var Drone = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('drone', {
anchorX: 0.5,
anchorY: 0.5
});
self.hp = 1;
self.shootCooldown = 0;
self.update = function () {
self.x -= 8;
};
return self;
});
// Enemy bullet
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGfx = self.attachAsset('bullet_enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -18;
self.update = function () {
self.x += self.speed;
};
return self;
});
// Explosion effect
var Explosion = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 1
});
// Animate fade out
tween(gfx, {
alpha: 0
}, {
duration: 400,
easing: tween.linear,
onFinish: function onFinish() {
self.destroy();
}
});
return self;
});
// Squad member: Chapter2 J
var J = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('j', {
anchorX: 0.5,
anchorY: 0.5
});
self.hp = 4;
self.role = 'j';
self.shootCooldown = 0;
self.update = function () {};
return self;
});
// Laser bullet for J
var LaserBullet = Container.expand(function () {
var self = Container.call(this);
var laserGfx = self.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
width: 40,
height: 12,
color: 0xff00ff,
shape: 'box'
});
self.speed = 32;
self.update = function () {
self.x += self.speed;
};
return self;
});
// Squad member: Medic
var Medic = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('medic', {
anchorX: 0.5,
anchorY: 0.5
});
self.hp = 3;
self.role = 'medic';
self.update = function () {};
return self;
});
// Player bullet
var PlayerBullet = Container.expand(function (assetOverride) {
var self = Container.call(this);
var assetId = assetOverride || 'bullet_player';
var bulletGfx = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 32;
self.update = function () {
self.x += self.speed;
};
return self;
});
// Powerup: Ammo
var PowerupAmmo = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('powerup_ammo', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
self.x -= 6;
};
return self;
});
// Powerup: Health
var PowerupHealth = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('powerup_health', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
self.x -= 6;
};
return self;
});
// Captain Rhea Morgan (player)
var Rhea = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('rhea', {
anchorX: 0.5,
anchorY: 0.5
});
self.hp = 5;
self.shootCooldown = 0;
self.update = function () {};
return self;
});
// Squad member: Chapter2 S
var S = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('s', {
anchorX: 0.5,
anchorY: 0.5
});
self.hp = 4;
self.role = 's';
self.shootCooldown = 0;
self.update = function () {};
return self;
});
// Squad member: Sniper
var Sniper = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('sniper', {
anchorX: 0.5,
anchorY: 0.5
});
self.hp = 2;
self.role = 'sniper';
self.shootCooldown = 0;
self.update = function () {};
return self;
});
// Enemy: Tank (mini-boss)
var Tank = Container.expand(function () {
var self = Container.call(this);
var gfx = self.attachAsset('tank', {
anchorX: 0.5,
anchorY: 0.5
});
self.hp = 10;
self.shootCooldown = 0;
self.update = function () {
self.x -= 4;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222831
});
/****
* Game Code
****/
// World War I background image
var battlefieldBg = LK.getAsset('ww1_bg', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732,
alpha: 0.35
});
game.addChild(battlefieldBg);
// Track if chapter 2 background has been swapped
var chapter2BgSwapped = false;
// Play music
// Main character: Captain Rhea Morgan
// Squad member: Medic
// Squad member: Sniper
// Enemy: Drone
// Enemy: Tank
// Bullet: Player
// Bullet: Enemy
// Powerup: Health
// Powerup: Ammo
// Explosion effect
// Sound effects
// Music
LK.playMusic('battle_theme');
// Game state variables
var rhea, medic, sniper;
var mh2; // Track mh2 globally for chapter 2
var squad = [];
var playerBullets = [];
var enemyBullets = [];
var enemies = [];
var powerups = [];
var explosions = [];
var score = 0;
var resources = 0;
var gameOver = false;
var youWin = false;
var dragNode = null;
var lastTouchX = 0,
lastTouchY = 0;
// Used to block all spawns and shooting during boss warning
var bossWarningActive = false;
// Used to pause enemy/powerup spawn at start of Chapter 2
var chapter2PauseTicks = 0;
// Track if chapter 2 has started
var chapter2Started = false;
// UI
var scoreTxt = new Text2('0', {
size: 100,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var hpTxt = new Text2('HP: 5', {
size: 70,
fill: 0xFF5252
});
// Move HP text to the upper left, avoiding the top left 100x100 px reserved area
hpTxt.anchor.set(0, 0);
hpTxt.x = 110; // 10px right of reserved area
hpTxt.y = 10;
LK.gui.topLeft.addChild(hpTxt);
var resourceTxt = new Text2('Res: 0', {
size: 70,
fill: 0xFFD600
});
resourceTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(resourceTxt);
// Initialize player and squad
rhea = new Rhea();
rhea.x = 400;
rhea.y = 2732 / 2;
game.addChild(rhea);
medic = new Medic();
medic.x = rhea.x - 120;
medic.y = rhea.y + 120;
game.addChild(medic);
sniper = new Sniper();
sniper.x = rhea.x - 120;
sniper.y = rhea.y - 120;
game.addChild(sniper);
squad = [rhea, medic, sniper];
// Helper: spawn enemy
function spawnEnemy() {
// Only spawn J and S as enemies in Chapter 2 (never ch2)
if (chapter2Started) {
// If bossSpawned, do not spawn any more enemies (only mh2 is allowed)
if (typeof bossSpawned !== "undefined" && bossSpawned) {
return;
}
// Alternate between J and S, but skip J if bossSpawned
var enemy;
if (LK.ticks % 2 === 0) {
enemy = new J();
enemy.x = 2048 + 120;
enemy.y = 300 + Math.floor(Math.random() * (2732 - 600));
enemy.direction = -1;
enemy.shootCooldown = 40;
enemy.lastX = enemy.x;
} else {
enemy = new S();
enemy.x = -120;
enemy.y = 300 + Math.floor(Math.random() * (2732 - 600));
enemy.direction = 1;
enemy.lastX = enemy.x;
}
// Prevent spawning ch2 as an enemy in chapter 2
if (enemy && enemy.children && enemy.children.length > 0 && enemy.children[0].assetId === 'ch2') {
// Do not add ch2 as an enemy
} else {
enemies.push(enemy);
game.addChild(enemy);
}
} else {
var type = LK.ticks % 300 === 0 ? 'tank' : 'drone';
var enemy;
if (type === 'drone') {
enemy = new Drone();
enemy.x = 2048 + 60;
enemy.y = 300 + Math.floor(Math.random() * (2732 - 600));
} else {
enemy = new Tank();
enemy.x = 2048 + 120;
enemy.y = 400 + Math.floor(Math.random() * (2732 - 800));
}
enemies.push(enemy);
game.addChild(enemy);
}
}
// Helper: spawn powerup
function spawnPowerup() {
// In chapter 2, further increase chance of health powerup
if (typeof bossSpawned !== "undefined" && bossSpawned) {
// No powerups after boss appears
return;
}
var healthChance = chapter2Started ? 0.92 : 0.5; // Increased to 92% in chapter 2
var type = Math.random() < healthChance ? 'health' : 'ammo';
var powerup;
if (type === 'health') {
powerup = new PowerupHealth();
} else {
powerup = new PowerupAmmo();
}
powerup.x = 2048 + 60;
powerup.y = 300 + Math.floor(Math.random() * (2732 - 600));
powerups.push(powerup);
game.addChild(powerup);
}
// Helper: fire player bullet
function firePlayerBullet(from) {
// Use ma2 asset for player bullet in Chapter 2, otherwise use default
var bullet;
if (chapter2Started) {
bullet = new PlayerBullet('ma2');
} else {
bullet = new PlayerBullet();
}
bullet.x = from.x + 70;
bullet.y = from.y;
// Stronger sniper: sniper bullets do more damage
if (from.role === 'sniper') {
bullet.damage = 6;
} else if (chapter2Started && from === rhea) {
bullet.damage = 6; // Increase main character's weapon damage in chapter 2
} else {
bullet.damage = 3;
}
playerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot_player').play();
}
// Helper: fire enemy bullet
function fireEnemyBullet(from) {
var bullet = new EnemyBullet();
bullet.x = from.x - 70;
bullet.y = from.y;
enemyBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot_enemy').play();
}
// Helper: show explosion
function showExplosion(x, y) {
var exp = new Explosion();
exp.x = x;
exp.y = y;
explosions.push(exp);
game.addChild(exp);
LK.getSound('explosion').play();
}
// Helper: update UI
function updateUI() {
scoreTxt.setText(score);
hpTxt.setText('HP: ' + rhea.hp);
resourceTxt.setText('Res: ' + resources);
}
// Handle dragging Rhea (player)
function handleMove(x, y, obj) {
if (dragNode) {
// Clamp to game area
var minY = 120,
maxY = 2732 - 120;
dragNode.y = Math.max(minY, Math.min(maxY, y));
lastTouchX = x;
lastTouchY = y;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only allow dragging Rhea
if (!gameOver && !youWin) {
if (Math.abs(x - rhea.x) < 120 && Math.abs(y - rhea.y) < 120) {
dragNode = rhea;
handleMove(x, y, obj);
}
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Main game update loop
game.update = function () {
if (gameOver || youWin) return;
// Player auto-fire
if (
// Only allow auto-fire if not in chapter 2 pause, or if chapter 2 hasn't started yet, or if not in chapter 2
(!chapter2Started || typeof chapter2PauseTicks !== "undefined" && chapter2PauseTicks === 0
// Block auto-fire during boss warning
) && !bossWarningActive) {
if (rhea.shootCooldown > 0) rhea.shootCooldown--;
if (rhea.shootCooldown === 0) {
firePlayerBullet(rhea);
rhea.shootCooldown = 10;
}
// Sniper auto-fire (slower)
if (sniper.shootCooldown > 0) sniper.shootCooldown--;
if (sniper.shootCooldown === 0) {
firePlayerBullet(sniper);
sniper.shootCooldown = 30;
}
}
// Medic follows Rhea
medic.x += (rhea.x - 120 - medic.x) * 0.15;
medic.y += (rhea.y + 120 - medic.y) * 0.15;
// Sniper follows Rhea
sniper.x += (rhea.x - 120 - sniper.x) * 0.15;
sniper.y += (rhea.y - 120 - sniper.y) * 0.15;
// Spawn enemies
if (typeof chapter2PauseTicks !== "undefined" && chapter2PauseTicks > 0) {
chapter2PauseTicks--;
} else if (!bossWarningActive) {
// Block all spawns during boss warning
// Only spawn if not in chapter2 pause or boss warning
if (LK.ticks % 120 === 0) {
spawnEnemy();
}
// Spawn powerups
if (chapter2Started) {
// In chapter 2, spawn powerups more often (every 90 ticks)
if (LK.ticks % 90 === 0) {
spawnPowerup();
}
} else {
if (LK.ticks % 180 === 0) {
spawnPowerup();
}
}
}
// Update player bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
var b = playerBullets[i];
b.update();
// Remove if off screen
if (b.x > 2048 + 60) {
b.destroy();
playerBullets.splice(i, 1);
continue;
}
// Hit enemy
for (var j = enemies.length - 1; j >= 0; j--) {
var e = enemies[j];
if (b.intersects(e)) {
var dmg = typeof b.damage === "number" ? b.damage : 3;
e.hp -= dmg;
showExplosion(b.x, b.y);
b.destroy();
playerBullets.splice(i, 1);
if (e.hp <= 0) {
showExplosion(e.x, e.y);
score += e instanceof Tank ? 10 : 2;
resources += e instanceof Tank ? 5 : 1;
e.destroy();
enemies.splice(j, 1);
}
updateUI();
break;
}
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var b = enemyBullets[i];
b.update();
if (b.x < -60) {
b.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Hit squad
for (var j = 0; j < squad.length; j++) {
var s = squad[j];
if (b.intersects(s)) {
// If mh2 fired this bullet, do more damage (enemy bullet collision)
if (mh2 && b.parent === mh2) {
s.hp -= mh2.damage || 6;
} else {
s.hp--;
}
showExplosion(b.x, b.y);
b.destroy();
enemyBullets.splice(i, 1);
if (s.hp <= 0) {
showExplosion(s.x, s.y);
if (s === rhea) {
// Game over
gameOver = true;
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
} else {
s.destroy();
squad.splice(j, 1);
}
}
updateUI();
break;
}
}
}
// Laser bullets from J
for (var i = playerBullets.length - 1; i >= 0; i--) {
var b = playerBullets[i];
if (b instanceof LaserBullet) {
b.update();
// Remove if off screen
if (b.x > 2048 + 60) {
b.destroy();
playerBullets.splice(i, 1);
continue;
}
// Hit squad (including Rhea)
for (var j = 0; j < squad.length; j++) {
var s = squad[j];
if (b.intersects(s)) {
s.hp -= 2; // Laser does 2 damage
showExplosion(b.x, b.y);
b.destroy();
playerBullets.splice(i, 1);
if (s.hp <= 0) {
showExplosion(s.x, s.y);
if (s === rhea) {
// Game over
gameOver = true;
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
} else {
s.destroy();
squad.splice(j, 1);
}
}
updateUI();
break;
}
}
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
// Special movement for J and S in chapter 2
if (e.role === 'j' && chapter2Started) {
// J moves left until near player, then stops
if (typeof e.lastX === "undefined") e.lastX = e.x;
if (e.x > rhea.x + 300) {
e.x -= 12;
}
// J fires laser continuously at player from the beginning of chapter 2
if (e.shootCooldown > 0) e.shootCooldown--;
if (e.shootCooldown === 0) {
// If mh2 is present, fire at mh2, otherwise fire at Rhea
var target = rhea;
if (typeof mh2 !== "undefined" && mh2 && mh2.parent) {
target = mh2;
}
var laser = new LaserBullet();
laser.x = e.x - 60;
laser.y = e.y;
// Calculate direction vector from J to target
var dx = target.x - e.x;
var dy = target.y - e.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist === 0) dist = 1;
laser.dirX = dx / dist;
laser.dirY = dy / dist;
laser.speed = 32;
// Override update to move toward target
laser.update = function () {
this.x += this.dirX * this.speed;
this.y += this.dirY * this.speed;
};
playerBullets.push(laser);
game.addChild(laser);
e.shootCooldown = 18; // J fires every 18 ticks (slower fire rate)
}
e.lastX = e.x;
} else if (e.role === 's' && chapter2Started) {
// S moves right until near player, then stops
if (typeof e.lastX === "undefined") e.lastX = e.x;
if (e.x < rhea.x - 300) {
e.x += 12;
}
e.lastX = e.x;
} else {
e.update();
}
// Remove if off screen
if (e.role !== 'j' && e.role !== 's' && (e.x < -200 || e.x > 2048 + 200)) {
e.destroy();
enemies.splice(i, 1);
continue;
}
// Enemy fire (for non-J/S enemies)
if ((!e.role || e.role !== 'j' && e.role !== 's') && e.shootCooldown !== undefined) {
if (e.shootCooldown > 0) e.shootCooldown--;
if (e.shootCooldown === 0) {
// Only fire if on screen
if (e.x < 2048 && e.x > 0) {
fireEnemyBullet(e);
e.shootCooldown = e instanceof Tank ? 30 : 60;
}
}
}
// Collide with player
if (e.intersects(rhea)) {
// If mh2, do more damage
if (mh2 && e === mh2) {
rhea.hp -= mh2.damage || 6;
} else {
rhea.hp -= 2;
}
showExplosion(e.x, e.y);
e.destroy();
enemies.splice(i, 1);
if (rhea.hp <= 0) {
gameOver = true;
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
updateUI();
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
var p = powerups[i];
p.update();
if (p.x < -60) {
p.destroy();
powerups.splice(i, 1);
continue;
}
// Pickup by player
if (p.intersects(rhea)) {
if (p instanceof PowerupHealth) {
rhea.hp = Math.min(rhea.hp + 2, 7);
} else if (p instanceof PowerupAmmo) {
// For MVP, just increase resources
resources += 2;
}
LK.getSound('pickup').play();
p.destroy();
powerups.splice(i, 1);
updateUI();
}
}
// Chapter 2 trigger and Win condition
if (typeof chapter2Started === "undefined") {
chapter2Started = false;
}
// --- BOSS WARNING & MH2 SPAWN LOGIC FOR CHAPTER 2 ---
// Track boss warning and boss spawn state
if (typeof bossWarningShown === "undefined") bossWarningShown = false;
if (typeof bossSpawned === "undefined") bossSpawned = false;
if (typeof bossWarningOverlay === "undefined") bossWarningOverlay = null;
if (typeof bossWarningTimer === "undefined") bossWarningTimer = 0;
// Show boss warning at 40 points in chapter 2, only once
if (chapter2Started && score >= 40 && !bossWarningShown) {
// Show overlay message
bossWarningOverlay = new Container();
var bossWarnBg = LK.getAsset('centerCircle', {
width: 2048,
height: 2732,
color: 0x000000,
shape: 'box',
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
alpha: 1
});
bossWarningOverlay.addChild(bossWarnBg);
var bossWarnTxt = new Text2('THE BOSS IS COMING', {
size: 180,
fill: 0xFFD700,
font: "Impact, 'Arial Black', Tahoma"
});
bossWarnTxt.anchor.set(0.5, 0.5);
bossWarnTxt.x = 2048 / 2;
bossWarnTxt.y = 2732 / 2;
bossWarningOverlay.addChild(bossWarnTxt);
game.addChild(bossWarningOverlay);
bossWarningShown = true;
bossWarningTimer = 120; // Show for 2 seconds (120 ticks)
// Block all spawns and shooting until boss appears
bossWarningActive = true;
}
// Remove boss warning overlay after timer, and spawn mh2 if not already spawned
if (bossWarningShown && bossWarningOverlay && bossWarningTimer > 0) {
bossWarningTimer--;
if (bossWarningTimer === 0) {
if (bossWarningOverlay && bossWarningOverlay.parent) {
bossWarningOverlay.parent.removeChild(bossWarningOverlay);
bossWarningOverlay.destroy();
}
bossWarningOverlay = null;
// Unblock spawns and shooting
bossWarningActive = false;
// Spawn mh2 as boss if not already spawned
if (!bossSpawned) {
// Remove all J, S, and any other enemies except mh2 from enemies array
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] && (enemies[i].role === 'j' || enemies[i].role === 's' || enemies[i] !== mh2)) {
enemies[i].destroy();
enemies.splice(i, 1);
}
}
bossSpawned = true;
allowJSpawning = false;
// Spawn mh2 at boss position (center right)
mh2 = LK.getAsset('mh2', {
anchorX: 0.5,
anchorY: 0.5
});
mh2.x = 2048 - 200;
mh2.y = 2732 / 2;
mh2.hp = 30;
mh2.damage = 12; // Very high damage
mh2.lastX = mh2.x;
mh2.lastY = mh2.y;
mh2.shootCooldown = 30; // Boss fires every 30 ticks
mh2.dir = 1;
mh2.update = function () {
if (typeof this.lastY === "undefined") this.lastY = this.y;
this.y += this.dir * 8;
if (this.y > 2732 - 200) this.dir = -1;
if (this.y < 200) this.dir = 1;
this.lastY = this.y;
// Boss shooting logic
if (typeof this.shootCooldown === "undefined") this.shootCooldown = 30;
if (this.shootCooldown > 0) this.shootCooldown--;
if (this.shootCooldown === 0) {
// Fire a burst of 3 bullets in a spread
for (var i = -1; i <= 1; i++) {
var bullet = new EnemyBullet();
bullet.x = this.x - 70;
bullet.y = this.y + i * 40;
bullet.speed = -24;
bullet.update = function (angle) {
return function () {
this.x += this.speed;
this.y += angle * 8;
};
}(i);
enemyBullets.push(bullet);
game.addChild(bullet);
}
LK.getSound('shoot_enemy').play();
this.shootCooldown = 30;
}
};
// Add boss to enemies for collision/damage logic
enemies.push(mh2);
game.addChild(mh2);
}
}
}
// --- END BOSS LOGIC ---
if (!chapter2Started && score >= 30) {
chapter2Started = true;
// Play S2 as background music in chapter 2
LK.playMusic('s2');
// Swap background to bf2 for chapter 2
if (!chapter2BgSwapped) {
var bf2Bg = LK.getAsset('bf2', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732,
alpha: 0.5
});
// Remove old background
if (battlefieldBg && battlefieldBg.parent) {
battlefieldBg.parent.removeChild(battlefieldBg);
}
game.addChildAt(bf2Bg, 0); // Add at bottom
chapter2BgSwapped = true;
}
// Change Rhea's asset to ch2 for chapter 2 (main character is now ch2)
if (rhea && rhea.children && rhea.children.length > 0) {
// Remove old asset
var oldGfx = rhea.children[0];
rhea.removeChild(oldGfx);
// Add new ch2 asset
var newGfx = rhea.attachAsset('ch2', {
anchorX: 0.5,
anchorY: 0.5
});
// Optionally update rhea's role/assetId for clarity
rhea.role = 'ch2';
rhea.assetId = 'ch2';
}
chapter2PauseTicks = 120; // 2 seconds at 60fps
// Remove medic and sniper from squad and game
if (medic && medic.parent) {
medic.parent.removeChild(medic);
medic.destroy();
}
if (sniper && sniper.parent) {
sniper.parent.removeChild(sniper);
sniper.destroy();
}
// Remove medic and sniper from squad array
for (var i = squad.length - 1; i >= 0; i--) {
if (squad[i] === medic || squad[i] === sniper) {
squad.splice(i, 1);
}
}
// Remove medic, sniper, and any ch2 from enemies array if present
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === medic || enemies[i] === sniper || enemies[i].children && enemies[i].children.length > 0 && enemies[i].children[0].assetId === 'ch2') {
enemies[i].destroy();
enemies.splice(i, 1);
}
}
// In chapter 2, set attackers to be J and S only (handled in spawnEnemy)
// Create a black overlay
var chapter2Overlay = new Container();
var blackBg = LK.getAsset('centerCircle', {
width: 2048,
height: 2732,
color: 0x000000,
shape: 'box',
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
alpha: 1
});
chapter2Overlay.addChild(blackBg);
// Show chapter 2 message
var chapter2Txt = new Text2('Chapter 2: Firestorm Rising', {
size: 120,
fill: 0x00BFFF,
font: "Impact, 'Arial Black', Tahoma"
});
chapter2Txt.anchor.set(0.5, 0.5);
chapter2Txt.x = 2048 / 2;
chapter2Txt.y = 2732 / 2;
chapter2Overlay.addChild(chapter2Txt);
game.addChild(chapter2Overlay);
// Fade out the overlay and message after 2 seconds
tween(chapter2Overlay, {
alpha: 0
}, {
duration: 2000,
easing: tween.linear,
onFinish: function onFinish() {
chapter2Overlay.destroy();
}
});
// Remove all non-J/S/ch2 enemies from the field at the start of Chapter 2
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
// Remove any enemy that is not 'j' or 's' or is a ch2 (since player is ch2 now)
if (!(e.role === 'j' || e.role === 's') || e !== rhea && e.children && e.children.length > 0 && e.children[0].assetId === 'ch2') {
e.destroy();
enemies.splice(i, 1);
}
}
// Remove J and S spawn logic after boss warning, only allow mh2 to appear after warning
// (No J or S spawn, only mh2 will appear after boss warning)
if (bossSpawned) {
// Remove all squad members except Rhea (main character)
for (var i = squad.length - 1; i >= 0; i--) {
if (squad[i] !== rhea) {
if (squad[i].parent) squad[i].parent.removeChild(squad[i]);
squad[i].destroy();
squad.splice(i, 1);
}
}
// Remove all enemies except mh2
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] !== mh2) {
enemies[i].destroy();
enemies.splice(i, 1);
}
}
// Block all spawns except mh2 (no-op, as mh2 is already spawned)
}
}
// Game over when mh2 (boss) is defeated in chapter 2
if (bossSpawned && mh2 && typeof mh2.hp === "number" && mh2.hp <= 0) {
gameOver = true;
LK.effects.flashScreen(0x000000, 1000);
var bossDefeatedTxt = new Text2('New Boss of the Game', {
size: 180,
fill: 0xFFD700,
font: "Impact, 'Arial Black', Tahoma"
});
bossDefeatedTxt.anchor.set(0.5, 0.5);
bossDefeatedTxt.x = 2048 / 2;
bossDefeatedTxt.y = 2732 / 2;
game.addChild(bossDefeatedTxt);
// End game after a short delay to show the message
LK.setTimeout(function () {
LK.showGameOver();
}, 1600);
return;
}
// Win condition: score threshold
if (score >= 50) {
youWin = true;
LK.effects.flashScreen(0x00ff00, 1000);
// Show 'You are a Real Player' as a gesture
var playerTxt = new Text2('You are a Real Player', {
size: 140,
fill: 0xFFD700,
font: "Impact, 'Arial Black', Tahoma"
});
playerTxt.anchor.set(0.5, 0.5);
playerTxt.x = 2048 / 2;
playerTxt.y = 2732 / 2;
game.addChild(playerTxt);
LK.showYouWin();
return;
}
};
// Initial UI update
updateUI();
soldier character. In-Game asset. 2d. High contrast. No shadows
Enemy robot. In-Game asset. 2d. High contrast. No shadows
Let the tank face left. In-Game asset. 2d. High contrast. No shadows
Sniper soldier. In-Game asset. 2d. High contrast. No shadows
Medic Soldier. In-Game asset. 2d. High contrast. No shadows
Health. In-Game asset. 2d. High contrast. No shadows
Ammo. In-Game asset. 2d. High contrast. No shadows
bullet enemy. In-Game asset. 2d. High contrast. No shadows
smile bullet to the right. In-Game asset. 2d. High contrast. No shadows
explosion effect. In-Game asset. 2d. High contrast. No shadows
dron enemy. In-Game asset. 2d. High contrast. No shadows
technological soldier. In-Game asset. 2d. High contrast. No shadows
just laser. In-Game asset. 2d. High contrast. No shadows
technological enemy. In-Game asset. 2d. High contrast. No shadows