54
1
1y
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('enemyJet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.5
});
self.maxHealth = 10;
self.health = 10;
self.speed = 1;
self.fireRate = 90;
self.fireTimer = 0;
self.missileRate = 180;
self.missileTimer = 0;
self.rocketRate = 150;
self.rocketTimer = 0;
self.horizontalSpeed = 1;
self.horizontalDirection = 1;
self.weaponSystems = {
bullets: 1,
missiles: 0,
rockets: 0
};
self.bossType = 1; // 1 for wave 10, 2 for wave 20
self.update = function () {
self.y += self.speed;
// Horizontal movement
self.x += self.horizontalSpeed * self.horizontalDirection;
if (self.x < 200 || self.x > 1848) {
self.horizontalDirection *= -1;
}
// Update timers
if (self.fireTimer > 0) {
self.fireTimer--;
}
if (self.missileTimer > 0) {
self.missileTimer--;
}
if (self.rocketTimer > 0) {
self.rocketTimer--;
}
// Fire bullets
if (self.fireTimer <= 0 && self.y > 0 && self.y < 2400) {
for (var i = 0; i < self.weaponSystems.bullets; i++) {
self.fireBullet(i);
}
self.fireTimer = self.fireRate;
}
// Fire missiles
if (self.weaponSystems.missiles > 0 && self.missileTimer <= 0 && self.y > 0 && self.y < 2400) {
for (var i = 0; i < self.weaponSystems.missiles; i++) {
self.fireMissile(i);
}
self.missileTimer = self.missileRate;
}
// Fire rockets
if (self.weaponSystems.rockets > 0 && self.rocketTimer <= 0 && self.y > 0 && self.y < 2400) {
for (var i = 0; i < self.weaponSystems.rockets; i++) {
self.fireRocket(i);
}
self.rocketTimer = self.rocketRate;
}
};
self.fireBullet = function (index) {
var bullet = new EnemyBullet();
var offset = (index - (self.weaponSystems.bullets - 1) / 2) * 40;
bullet.x = self.x + offset;
bullet.y = self.y + 75;
enemyBullets.push(bullet);
game.addChild(bullet);
};
self.fireMissile = function (index) {
var missile = new Missile();
var offset = (index - (self.weaponSystems.missiles - 1) / 2) * 60;
missile.x = self.x + offset;
missile.y = self.y + 75;
// Mark as boss missile for enhanced tracking
missile.isBossMissile = true;
missile.trackingSpeed = 1.2; // Faster tracking for boss missiles
missile.damage = 3; // More damage from boss missiles
// Add visual effect to indicate guided missile
tween(missile, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200
});
missiles.push(missile);
game.addChild(missile);
LK.getSound('missile').play();
};
self.fireRocket = function (index) {
var missile = new Missile();
var offset = (index - (self.weaponSystems.rockets - 1) / 2) * 80;
missile.x = self.x + offset;
missile.y = self.y + 75;
// Mark wave 20 boss rockets as guided missiles
missile.isBossMissile = true;
missile.trackingSpeed = 1.5; // Even faster tracking for wave 20 boss
missile.damage = 4; // High damage from wave 20 boss
// Visual enhancement for wave 20 boss missiles
tween(missile, {
scaleX: 1.5,
scaleY: 1.5,
tint: 0xFF0000
}, {
duration: 300
});
missiles.push(missile);
game.addChild(missile);
LK.getSound('missile').play();
};
self.takeDamage = function (damage) {
self.health -= damage;
// Flash effect when taking damage
LK.effects.flashObject(self, 0xFF0000, 200);
if (self.health <= 0) {
// Create multiple explosions for boss death - reduced for performance
for (var i = 0; i < 3; i++) {
var explosion = LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x + (Math.random() - 0.5) * 150,
y: self.y + (Math.random() - 0.5) * 150
});
game.addChild(explosion);
tween(explosion, {
alpha: 0
}, {
duration: 600,
delay: i * 100,
onFinish: function onFinish() {
explosion.destroy();
}
});
}
self.destroy();
LK.getSound('explosion').play();
return true;
}
return false;
};
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 = 6;
self.damage = 1;
self.update = function () {
self.y += self.speed;
};
return self;
});
var EnemyJet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('enemyJet', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 1;
self.speed = 2;
self.fireRate = 120;
self.fireTimer = Math.random() * 60;
self.canFireMissiles = false;
self.missileRate = 300;
self.missileTimer = Math.random() * 150;
self.horizontalSpeed = (Math.random() - 0.5) * 3;
self.horizontalDirection = Math.random() > 0.5 ? 1 : -1;
self.update = function () {
self.y += self.speed;
// Add horizontal movement
self.x += self.horizontalSpeed * self.horizontalDirection;
// Bounce off screen edges
if (self.x < 100 || self.x > 1948) {
self.horizontalDirection *= -1;
}
if (self.fireTimer > 0) {
self.fireTimer--;
}
if (self.missileTimer > 0) {
self.missileTimer--;
}
// Fire bullets
if (self.fireTimer <= 0 && self.y > 0 && self.y < 2400) {
self.fireBullet();
self.fireTimer = self.fireRate + Math.random() * 60;
}
// Fire missiles (if capable)
if (self.canFireMissiles && self.missileTimer <= 0 && self.y > 0 && self.y < 2400) {
self.fireMissile();
self.missileTimer = self.missileRate + Math.random() * 120;
}
};
self.fireBullet = function () {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + 50;
enemyBullets.push(bullet);
game.addChild(bullet);
};
self.fireMissile = function () {
var missile = new Missile();
missile.x = self.x;
missile.y = self.y + 50;
missiles.push(missile);
game.addChild(missile);
LK.getSound('missile').play();
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
// Create explosion effect
var explosion = LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
});
game.addChild(explosion);
// Enhanced explosion animation
explosion.scaleX = 0.1;
explosion.scaleY = 0.1;
tween(explosion, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
explosion.destroy();
}
});
// Create sparkle particles - limit to prevent performance issues
if (particles.length < 50) {
for (var p = 0; p < 4; p++) {
var sparkle = new Particle();
sparkle.attachAsset('sparkle', {
anchorX: 0.5,
anchorY: 0.5
});
sparkle.x = self.x + (Math.random() - 0.5) * 100;
sparkle.y = self.y + (Math.random() - 0.5) * 100;
sparkle.velocityX = (Math.random() - 0.5) * 8;
sparkle.velocityY = (Math.random() - 0.5) * 8;
sparkle.life = 20;
sparkle.maxLife = 20;
particles.push(sparkle);
game.addChild(sparkle);
}
}
self.destroy();
LK.getSound('explosion').play();
return true;
}
return false;
};
return self;
});
var GuidedMissile = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('guidedMissile', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = 3;
self.trackingSpeed = 2.0;
self.upgradeLevel = 1;
self.baseSpeed = 8;
self.rotation = 0;
self.update = function () {
// Enhanced guided missile behavior
if (enemies.length > 0) {
// Find nearest enemy
var nearestEnemy = null;
var nearestDistance = Infinity;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestEnemy = enemy;
}
}
// Track nearest enemy
if (nearestEnemy) {
var dx = nearestEnemy.x - self.x;
var dy = nearestEnemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
// Calculate angle to target
var angle = Math.atan2(dy, dx);
// Apply guided movement with upgrade-based speed
var currentTrackingSpeed = self.trackingSpeed * self.upgradeLevel;
self.x += Math.cos(angle) * currentTrackingSpeed;
self.y += Math.sin(angle) * currentTrackingSpeed;
// Rotate missile to face direction of movement
self.rotation = angle + Math.PI / 2;
}
}
}
// Base forward movement with upgrade-based speed
var forwardSpeed = self.speed + (self.upgradeLevel - 1) * 2;
self.y -= forwardSpeed;
};
return self;
});
var KamikazeJet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('enemyJet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
graphics.tint = 0xFF4444; // Red tint to distinguish kamikaze planes
self.health = 1;
self.speed = 4;
self.trackingSpeed = 1.5;
self.isKamikaze = true;
self.update = function () {
// Kamikaze behavior - chase the player
if (player) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
// Calculate normalized direction to player
var dirX = dx / distance;
var dirY = dy / distance;
// Move towards player
self.x += dirX * self.trackingSpeed;
self.y += dirY * self.trackingSpeed;
// Rotate to face direction of movement
var angle = Math.atan2(dy, dx);
self.rotation = angle + Math.PI / 2;
}
}
// Also move forward
self.y += self.speed;
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
// Create explosion effect
var explosion = LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
});
game.addChild(explosion);
// Fade out explosion with tween effect
tween(explosion, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
explosion.destroy();
}
});
self.destroy();
LK.getSound('explosion').play();
return true;
}
return false;
};
return self;
});
var Missile = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('missile', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4;
self.damage = 2;
self.trackingSpeed = 0.5;
self.isBossMissile = false;
self.baseSpeed = 4;
self.rotation = 0;
self.update = function () {
// Enhanced homing behavior
if (player) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
// Stronger and faster tracking for boss missiles
var currentTrackingSpeed = self.isBossMissile ? self.trackingSpeed * 3 : self.trackingSpeed;
// Calculate angle to player
var angle = Math.atan2(dy, dx);
// Apply guided movement
self.x += Math.cos(angle) * currentTrackingSpeed;
self.y += Math.sin(angle) * currentTrackingSpeed;
// Rotate missile to face direction of movement for visual feedback
self.rotation = angle + Math.PI / 2;
}
}
// Base forward movement (faster for boss missiles)
var forwardSpeed = self.isBossMissile ? self.speed * 1.5 : self.speed;
self.y += forwardSpeed;
};
return self;
});
var Particle = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.life = 60;
self.maxLife = 60;
self.fadeOut = true;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.life--;
if (self.fadeOut) {
var lifeRatio = self.life / self.maxLife;
graphics.alpha = lifeRatio;
// Smooth scaling effect
graphics.scaleX = lifeRatio;
graphics.scaleY = lifeRatio;
}
if (self.life <= 0) {
self.destroy();
}
};
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.damage = 1;
self.update = function () {
self.y += self.speed;
};
return self;
});
var PlayerJet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('playerJet', {
anchorX: 0.5,
anchorY: 0.5
});
self.maxHealth = 3;
self.health = 3;
self.speed = 8;
self.fireRate = 15;
self.fireTimer = 0;
self.invulnerable = false;
self.invulnerableTimer = 0;
self.guidedMissileLevel = 0;
self.guidedMissileRate = 90;
self.guidedMissileTimer = 0;
self.update = function () {
if (self.fireTimer > 0) {
self.fireTimer--;
}
if (self.guidedMissileTimer > 0) {
self.guidedMissileTimer--;
}
if (self.invulnerable) {
self.invulnerableTimer--;
if (self.invulnerableTimer <= 0) {
self.invulnerable = false;
graphics.alpha = 1;
} else {
graphics.alpha = 0.5;
}
}
// Auto-fire
if (self.fireTimer <= 0) {
self.fire();
self.fireTimer = self.fireRate;
}
// Auto-fire guided missiles if upgraded
if (self.guidedMissileLevel > 0 && self.guidedMissileTimer <= 0) {
self.fireGuidedMissile();
self.guidedMissileTimer = self.guidedMissileRate;
}
};
self.fire = function () {
var bullet = new PlayerBullet();
bullet.x = self.x;
bullet.y = self.y - 50;
playerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
};
self.fireGuidedMissile = function () {
var guidedMissile = new GuidedMissile();
guidedMissile.x = self.x;
guidedMissile.y = self.y - 50;
guidedMissile.upgradeLevel = self.guidedMissileLevel;
// Visual effect based on upgrade level
var tintColor = 0x00FF00 + self.guidedMissileLevel * 0x001100;
tween(guidedMissile, {
tint: tintColor,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100
});
guidedMissiles.push(guidedMissile);
game.addChild(guidedMissile);
LK.getSound('missile').play();
};
self.takeDamage = function (damage) {
if (self.invulnerable) return;
self.health -= damage;
self.invulnerable = true;
self.invulnerableTimer = 60;
LK.effects.flashObject(self, 0xFF0000, 500);
if (self.health <= 0) {
gameOver();
}
updateHealthDisplay();
};
return self;
});
var Rocket = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('missile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
graphics.tint = 0xFF4444;
self.speed = 5;
self.damage = 3;
self.update = function () {
self.y += self.speed;
};
return self;
});
var StarField = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = Math.random() * 2 + 1;
self.twinklePhase = Math.random() * Math.PI * 2;
self.baseAlpha = Math.random() * 0.8 + 0.2;
self.update = function () {
self.y += self.speed;
// Twinkling effect
self.twinklePhase += 0.1;
graphics.alpha = self.baseAlpha + Math.sin(self.twinklePhase) * 0.3;
};
return self;
});
var Trail = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('trail', {
anchorX: 0.5,
anchorY: 0.5
});
self.life = 30;
self.maxLife = 30;
self.update = function () {
self.life--;
graphics.alpha = self.life / self.maxLife;
graphics.scaleY = self.life / self.maxLife;
if (self.life <= 0) {
self.destroy();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x001122,
fullscreen: true
});
/****
* Game Code
****/
// Background setup
var background1 = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
var background2 = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: -2732
});
game.addChild(background1);
game.addChild(background2);
// Game state variables
var currentWave = 1;
var maxWaves = 20;
var waveEnemies = [];
var enemiesSpawned = 0;
var enemiesToSpawn = 5;
var spawnTimer = 0;
var spawnRate = 60;
var waveComplete = false;
var gameState = 'playing'; // 'playing', 'upgrading', 'gameover'
// Game objects
var player = null;
var playerBullets = [];
var enemyBullets = [];
var missiles = [];
var rockets = [];
var guidedMissiles = [];
var enemies = [];
var currentBoss = null;
var particles = [];
var trails = [];
var maxTrails = 20;
var stars = [];
// UI elements
var waveText = null;
var healthText = null;
var upgradeContainer = null;
// Initialize player
player = new PlayerJet();
player.x = 1024;
player.y = 2800;
game.addChild(player);
// Smooth entrance animation
tween(player, {
y: 2300
}, {
duration: 1000,
easing: tween.easeOut
});
// Initialize UI
waveText = new Text2('Wave: 1', {
size: 60,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
LK.gui.top.addChild(waveText);
waveText.y = 50;
healthText = new Text2('Health: 3', {
size: 80,
fill: 0x00FF00
});
healthText.anchor.set(1, 0);
LK.gui.topRight.addChild(healthText);
healthText.x = -20;
healthText.y = 50;
function updateHealthDisplay() {
healthText.setText('Health: ' + player.health);
}
function updateWaveDisplay() {
waveText.setText('Wave: ' + currentWave);
}
function spawnEnemy() {
// Spawn boss on waves 10 and 20
if ((currentWave === 10 || currentWave === 20) && enemiesSpawned === 0) {
var boss = new Boss();
boss.x = 1024;
boss.y = -150;
if (currentWave === 10) {
// Wave 10 boss: 2 missiles, 1 bullet
boss.maxHealth = 30;
boss.health = 30;
boss.weaponSystems.bullets = 1;
boss.weaponSystems.missiles = 2;
boss.weaponSystems.rockets = 0;
boss.bossType = 1;
} else if (currentWave === 20) {
// Wave 20 boss: 2 rockets, 2 bullets, more health
boss.maxHealth = 50;
boss.health = 50;
boss.weaponSystems.bullets = 2;
boss.weaponSystems.missiles = 0;
boss.weaponSystems.rockets = 2;
boss.bossType = 2;
}
enemies.push(boss);
game.addChild(boss);
currentBoss = boss;
// Boss entrance animation
boss.alpha = 0;
boss.scaleX = 0.5;
boss.scaleY = 0.5;
tween(boss, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 1500,
easing: tween.easeOut
});
enemiesSpawned++;
return;
}
var enemy;
// Spawn kamikaze planes starting from wave 15
if (currentWave >= 15 && Math.random() < 0.3) {
enemy = new KamikazeJet();
enemy.x = Math.random() * 1700 + 174;
enemy.y = -90;
// Scale kamikaze difficulty
enemy.health = Math.floor(currentWave / 8) + 1;
enemy.speed = 3 + currentWave * 0.3;
enemy.trackingSpeed = 1.5 + currentWave * 0.1;
} else {
enemy = new EnemyJet();
enemy.x = Math.random() * 1700 + 174;
enemy.y = -90;
// Scale difficulty based on wave
enemy.health = Math.floor(currentWave / 5) + 1;
enemy.speed = 2 + currentWave * 0.2;
enemy.fireRate = Math.max(60 - currentWave * 2, 30);
// Enable missiles for later waves
if (currentWave >= 5) {
enemy.canFireMissiles = true;
}
}
enemies.push(enemy);
game.addChild(enemy);
// Enemy entrance animation
enemy.alpha = 0;
enemy.y -= 100;
tween(enemy, {
alpha: 1,
y: enemy.y + 100
}, {
duration: 800,
easing: tween.easeOut
});
enemiesSpawned++;
}
function startWave() {
currentWave++;
if (currentWave > maxWaves) {
showYouWinScreen();
return;
}
// Boss waves spawn only 1 enemy (the boss)
if (currentWave === 10 || currentWave === 20) {
enemiesToSpawn = 1;
} else {
// Standard enemy count progression
enemiesToSpawn = 5 + Math.floor(currentWave * 1.5);
}
enemiesSpawned = 0;
spawnTimer = 0;
waveComplete = false;
gameState = 'playing';
updateWaveDisplay();
// Wave transition animation
waveText.alpha = 0;
waveText.scaleX = 2;
waveText.scaleY = 2;
tween(waveText, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 1000,
easing: tween.bounceOut
});
// Hide upgrade menu if visible
if (upgradeContainer) {
upgradeContainer.destroy();
upgradeContainer = null;
}
}
function completeWave() {
if (currentWave >= maxWaves) {
showYouWinScreen();
return;
}
waveComplete = true;
gameState = 'upgrading';
// Fade out all bullets and missiles smoothly
for (var i = 0; i < playerBullets.length; i++) {
var bullet = playerBullets[i];
tween(bullet, {
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
if (this.target && this.target.destroy) {
this.target.destroy();
}
}
});
}
playerBullets = [];
for (var i = 0; i < enemyBullets.length; i++) {
var bullet = enemyBullets[i];
tween(bullet, {
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
if (this.target && this.target.destroy) {
this.target.destroy();
}
}
});
}
enemyBullets = [];
for (var i = 0; i < missiles.length; i++) {
var missile = missiles[i];
tween(missile, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 1000,
onFinish: function onFinish() {
if (this.target && this.target.destroy) {
this.target.destroy();
}
}
});
}
missiles = [];
for (var i = 0; i < rockets.length; i++) {
var rocket = rockets[i];
tween(rocket, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 1000,
onFinish: function onFinish() {
if (this.target && this.target.destroy) {
this.target.destroy();
}
}
});
}
rockets = [];
for (var i = 0; i < guidedMissiles.length; i++) {
var guidedMissile = guidedMissiles[i];
tween(guidedMissile, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 1000,
onFinish: function onFinish() {
if (this.target && this.target.destroy) {
this.target.destroy();
}
}
});
}
guidedMissiles = [];
showUpgradeMenu();
}
function showUpgradeMenu() {
upgradeContainer = new Container();
game.addChild(upgradeContainer);
var titleText = new Text2('Choose Upgrade', {
size: 80,
fill: 0xFFFF00
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
upgradeContainer.addChild(titleText);
var upgrades = [{
text: 'Faster Firing',
action: function action() {
player.fireRate = Math.max(player.fireRate - 3, 5);
}
}, {
text: 'More Health',
action: function action() {
player.maxHealth++;
player.health = player.maxHealth;
}
}, {
text: 'Faster Movement',
action: function action() {
player.speed += 2;
}
}];
// Add guided missiles upgrade only from wave 10 onwards
if (currentWave >= 10) {
upgrades.push({
text: 'Guided Missiles',
action: function action() {
player.guidedMissileLevel++;
// Faster firing rate with each upgrade (but starting from slower base rate)
player.guidedMissileRate = Math.max(90 - player.guidedMissileLevel * 10, 30);
}
});
}
for (var i = 0; i < Math.min(upgrades.length, 4); i++) {
var upgrade = upgrades[i];
var button = new Container();
var buttonBg = LK.getAsset('upgradeButton', {
anchorX: 0.5,
anchorY: 0.5
});
button.addChild(buttonBg);
var buttonText = new Text2(upgrade.text, {
size: 40,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
button.addChild(buttonText);
button.x = 1024;
button.y = 1200 + i * 200;
button.upgrade = upgrade;
button.down = function (x, y, obj) {
this.upgrade.action();
updateHealthDisplay();
startWave();
};
upgradeContainer.addChild(button);
}
}
function gameOver() {
gameState = 'gameover';
showGameOverScreen();
}
function showGameOverScreen() {
var gameOverContainer = new Container();
game.addChild(gameOverContainer);
gameOverContainer.zIndex = 1000;
// Semi-transparent background
var overlay = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
overlay.alpha = 0.7;
overlay.tint = 0x000000;
gameOverContainer.addChild(overlay);
// Game Over title
var gameOverText = new Text2('GAME OVER', {
size: 120,
fill: 0xFF0000
});
gameOverText.anchor.set(0.5, 0.5);
gameOverText.x = 1024;
gameOverText.y = 1200;
gameOverContainer.addChild(gameOverText);
// Final score/wave text
var finalScoreText = new Text2('Final Wave: ' + currentWave, {
size: 60,
fill: 0xFFFFFF
});
finalScoreText.anchor.set(0.5, 0.5);
finalScoreText.x = 1024;
finalScoreText.y = 1350;
gameOverContainer.addChild(finalScoreText);
// Restart button
var restartButton = new Container();
var buttonBg = LK.getAsset('upgradeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.5
});
buttonBg.tint = 0x00AA00;
restartButton.addChild(buttonBg);
var buttonText = new Text2('PLAY AGAIN', {
size: 50,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
restartButton.addChild(buttonText);
restartButton.x = 1024;
restartButton.y = 1600;
restartButton.down = function (x, y, obj) {
restartGame();
};
gameOverContainer.addChild(restartButton);
}
function showYouWinScreen() {
var youWinContainer = new Container();
game.addChild(youWinContainer);
youWinContainer.zIndex = 1000;
// Semi-transparent background
var overlay = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
overlay.alpha = 0.7;
overlay.tint = 0x000000;
youWinContainer.addChild(overlay);
// You Win title
var youWinText = new Text2('YOU WIN!', {
size: 120,
fill: 0x00FF00
});
youWinText.anchor.set(0.5, 0.5);
youWinText.x = 1024;
youWinText.y = 1200;
youWinContainer.addChild(youWinText);
// Congratulations text
var congratsText = new Text2('You defeated all waves!', {
size: 60,
fill: 0xFFFFFF
});
congratsText.anchor.set(0.5, 0.5);
congratsText.x = 1024;
congratsText.y = 1350;
youWinContainer.addChild(congratsText);
// Play again button
var playAgainButton = new Container();
var buttonBg = LK.getAsset('upgradeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.5
});
buttonBg.tint = 0x00AA00;
playAgainButton.addChild(buttonBg);
var buttonText = new Text2('PLAY AGAIN', {
size: 50,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
playAgainButton.addChild(buttonText);
playAgainButton.x = 1024;
playAgainButton.y = 1600;
playAgainButton.down = function (x, y, obj) {
restartGame();
};
youWinContainer.addChild(playAgainButton);
}
function restartGame() {
// Reset game state
gameState = 'playing';
currentWave = 1;
waveComplete = false;
enemiesSpawned = 0;
enemiesToSpawn = 5;
spawnTimer = 0;
// Clear all game objects
for (var i = playerBullets.length - 1; i >= 0; i--) {
playerBullets[i].destroy();
}
playerBullets = [];
for (var i = enemyBullets.length - 1; i >= 0; i--) {
enemyBullets[i].destroy();
}
enemyBullets = [];
for (var i = missiles.length - 1; i >= 0; i--) {
missiles[i].destroy();
}
missiles = [];
for (var i = rockets.length - 1; i >= 0; i--) {
rockets[i].destroy();
}
rockets = [];
for (var i = guidedMissiles.length - 1; i >= 0; i--) {
guidedMissiles[i].destroy();
}
guidedMissiles = [];
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
}
enemies = [];
currentBoss = null;
// Clear particles and trails
for (var i = particles.length - 1; i >= 0; i--) {
particles[i].destroy();
}
particles = [];
for (var i = trails.length - 1; i >= 0; i--) {
trails[i].destroy();
}
trails = [];
for (var i = stars.length - 1; i >= 0; i--) {
stars[i].destroy();
}
stars = [];
// Reset player
if (player) {
player.destroy();
}
player = new PlayerJet();
player.x = 1024;
player.y = 2300;
player.health = player.maxHealth;
player.fireRate = 15;
player.speed = 8;
player.guidedMissileLevel = 0;
player.guidedMissileTimer = 0;
game.addChild(player);
// Clear any existing containers
if (upgradeContainer) {
upgradeContainer.destroy();
upgradeContainer = null;
}
// Find and remove all game over or win screen containers
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
if (child instanceof Container && child.zIndex === 1000) {
child.destroy();
}
}
// Update UI
updateWaveDisplay();
updateHealthDisplay();
}
// Input handling
var dragActive = false;
game.down = function (x, y, obj) {
if (gameState === 'playing') {
dragActive = true;
player.x = x;
player.y = y;
}
};
game.move = function (x, y, obj) {
if (gameState === 'playing' && dragActive) {
var targetX = Math.max(70, Math.min(1978, x));
var targetY = Math.max(150, Math.min(2582, y));
// Direct position update for responsive movement
player.x = targetX;
player.y = targetY;
}
};
game.up = function (x, y, obj) {
dragActive = false;
};
// Main game loop
game.update = function () {
if (gameState !== 'playing') return;
// Background is now static - no movement or scaling
// Spawn enemies
if (enemiesSpawned < enemiesToSpawn) {
spawnTimer++;
if (spawnTimer >= spawnRate) {
spawnEnemy();
spawnTimer = 0;
}
}
// Update player bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
if (bullet.y < -50) {
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)) {
if (enemy.takeDamage(bullet.damage)) {
enemies.splice(j, 1);
// Reset boss reference if boss was destroyed
if (enemy === currentBoss) {
currentBoss = null;
}
}
bullet.destroy();
playerBullets.splice(i, 1);
break;
}
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
if (bullet.y > 2782) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check collision with player
if (bullet.intersects(player)) {
player.takeDamage(bullet.damage);
bullet.destroy();
enemyBullets.splice(i, 1);
}
}
// Update missiles
for (var i = missiles.length - 1; i >= 0; i--) {
var missile = missiles[i];
if (missile.y > 2782 || missile.y < -50 || missile.x < -50 || missile.x > 2098) {
missile.destroy();
missiles.splice(i, 1);
continue;
}
// Check collision with player
if (missile.intersects(player)) {
player.takeDamage(missile.damage);
missile.destroy();
missiles.splice(i, 1);
}
}
// Update rockets
for (var i = rockets.length - 1; i >= 0; i--) {
var rocket = rockets[i];
if (rocket.y > 2782 || rocket.y < -50 || rocket.x < -50 || rocket.x > 2098) {
rocket.destroy();
rockets.splice(i, 1);
continue;
}
// Check collision with player
if (rocket.intersects(player)) {
player.takeDamage(rocket.damage);
rocket.destroy();
rockets.splice(i, 1);
}
}
// Update guided missiles
for (var i = guidedMissiles.length - 1; i >= 0; i--) {
var guidedMissile = guidedMissiles[i];
if (guidedMissile.y < -50 || guidedMissile.x < -50 || guidedMissile.x > 2098) {
guidedMissile.destroy();
guidedMissiles.splice(i, 1);
continue;
}
// Check collision with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (guidedMissile.intersects(enemy)) {
if (enemy.takeDamage(guidedMissile.damage)) {
enemies.splice(j, 1);
// Reset boss reference if boss was destroyed
if (enemy === currentBoss) {
currentBoss = null;
}
}
guidedMissile.destroy();
guidedMissiles.splice(i, 1);
break;
}
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.y > 2782) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
// Check kamikaze collision with player
if (enemy.isKamikaze && enemy.intersects(player)) {
// Kamikaze plane crashes into player
player.takeDamage(2); // Kamikaze does more damage
enemy.destroy();
enemies.splice(i, 1);
// Create explosion at crash site
var explosion = LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
x: enemy.x,
y: enemy.y
});
game.addChild(explosion);
tween(explosion, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
explosion.destroy();
}
});
LK.getSound('explosion').play();
}
}
// Update particles
for (var i = particles.length - 1; i >= 0; i--) {
var particle = particles[i];
if (particle.life <= 0) {
particle.destroy();
particles.splice(i, 1);
}
}
// Update trails
for (var i = trails.length - 1; i >= 0; i--) {
var trail = trails[i];
if (trail.life <= 0 || trail.y > 2832) {
trail.destroy();
trails.splice(i, 1);
}
}
// Create engine trail for player - reduced frequency
if (LK.ticks % 5 === 0 && trails.length < maxTrails) {
var trail = new Trail();
trail.x = player.x;
trail.y = player.y + 60;
trails.push(trail);
game.addChild(trail);
}
// Update starfield
for (var i = stars.length - 1; i >= 0; i--) {
var star = stars[i];
if (star.y > 2782) {
star.destroy();
stars.splice(i, 1);
}
}
// Generate new stars - limit to prevent performance issues
if (LK.ticks % 30 === 0 && stars.length < 30) {
var star = new StarField();
star.x = Math.random() * 2048;
star.y = -10;
stars.push(star);
game.addChild(star);
}
// Check wave completion
if (enemiesSpawned >= enemiesToSpawn && enemies.length === 0 && !waveComplete) {
completeWave();
}
};
// Start first wave
updateWaveDisplay();
updateHealthDisplay();
LK.playMusic('bgmusic'); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('enemyJet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.5
});
self.maxHealth = 10;
self.health = 10;
self.speed = 1;
self.fireRate = 90;
self.fireTimer = 0;
self.missileRate = 180;
self.missileTimer = 0;
self.rocketRate = 150;
self.rocketTimer = 0;
self.horizontalSpeed = 1;
self.horizontalDirection = 1;
self.weaponSystems = {
bullets: 1,
missiles: 0,
rockets: 0
};
self.bossType = 1; // 1 for wave 10, 2 for wave 20
self.update = function () {
self.y += self.speed;
// Horizontal movement
self.x += self.horizontalSpeed * self.horizontalDirection;
if (self.x < 200 || self.x > 1848) {
self.horizontalDirection *= -1;
}
// Update timers
if (self.fireTimer > 0) {
self.fireTimer--;
}
if (self.missileTimer > 0) {
self.missileTimer--;
}
if (self.rocketTimer > 0) {
self.rocketTimer--;
}
// Fire bullets
if (self.fireTimer <= 0 && self.y > 0 && self.y < 2400) {
for (var i = 0; i < self.weaponSystems.bullets; i++) {
self.fireBullet(i);
}
self.fireTimer = self.fireRate;
}
// Fire missiles
if (self.weaponSystems.missiles > 0 && self.missileTimer <= 0 && self.y > 0 && self.y < 2400) {
for (var i = 0; i < self.weaponSystems.missiles; i++) {
self.fireMissile(i);
}
self.missileTimer = self.missileRate;
}
// Fire rockets
if (self.weaponSystems.rockets > 0 && self.rocketTimer <= 0 && self.y > 0 && self.y < 2400) {
for (var i = 0; i < self.weaponSystems.rockets; i++) {
self.fireRocket(i);
}
self.rocketTimer = self.rocketRate;
}
};
self.fireBullet = function (index) {
var bullet = new EnemyBullet();
var offset = (index - (self.weaponSystems.bullets - 1) / 2) * 40;
bullet.x = self.x + offset;
bullet.y = self.y + 75;
enemyBullets.push(bullet);
game.addChild(bullet);
};
self.fireMissile = function (index) {
var missile = new Missile();
var offset = (index - (self.weaponSystems.missiles - 1) / 2) * 60;
missile.x = self.x + offset;
missile.y = self.y + 75;
// Mark as boss missile for enhanced tracking
missile.isBossMissile = true;
missile.trackingSpeed = 1.2; // Faster tracking for boss missiles
missile.damage = 3; // More damage from boss missiles
// Add visual effect to indicate guided missile
tween(missile, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200
});
missiles.push(missile);
game.addChild(missile);
LK.getSound('missile').play();
};
self.fireRocket = function (index) {
var missile = new Missile();
var offset = (index - (self.weaponSystems.rockets - 1) / 2) * 80;
missile.x = self.x + offset;
missile.y = self.y + 75;
// Mark wave 20 boss rockets as guided missiles
missile.isBossMissile = true;
missile.trackingSpeed = 1.5; // Even faster tracking for wave 20 boss
missile.damage = 4; // High damage from wave 20 boss
// Visual enhancement for wave 20 boss missiles
tween(missile, {
scaleX: 1.5,
scaleY: 1.5,
tint: 0xFF0000
}, {
duration: 300
});
missiles.push(missile);
game.addChild(missile);
LK.getSound('missile').play();
};
self.takeDamage = function (damage) {
self.health -= damage;
// Flash effect when taking damage
LK.effects.flashObject(self, 0xFF0000, 200);
if (self.health <= 0) {
// Create multiple explosions for boss death - reduced for performance
for (var i = 0; i < 3; i++) {
var explosion = LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x + (Math.random() - 0.5) * 150,
y: self.y + (Math.random() - 0.5) * 150
});
game.addChild(explosion);
tween(explosion, {
alpha: 0
}, {
duration: 600,
delay: i * 100,
onFinish: function onFinish() {
explosion.destroy();
}
});
}
self.destroy();
LK.getSound('explosion').play();
return true;
}
return false;
};
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 = 6;
self.damage = 1;
self.update = function () {
self.y += self.speed;
};
return self;
});
var EnemyJet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('enemyJet', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 1;
self.speed = 2;
self.fireRate = 120;
self.fireTimer = Math.random() * 60;
self.canFireMissiles = false;
self.missileRate = 300;
self.missileTimer = Math.random() * 150;
self.horizontalSpeed = (Math.random() - 0.5) * 3;
self.horizontalDirection = Math.random() > 0.5 ? 1 : -1;
self.update = function () {
self.y += self.speed;
// Add horizontal movement
self.x += self.horizontalSpeed * self.horizontalDirection;
// Bounce off screen edges
if (self.x < 100 || self.x > 1948) {
self.horizontalDirection *= -1;
}
if (self.fireTimer > 0) {
self.fireTimer--;
}
if (self.missileTimer > 0) {
self.missileTimer--;
}
// Fire bullets
if (self.fireTimer <= 0 && self.y > 0 && self.y < 2400) {
self.fireBullet();
self.fireTimer = self.fireRate + Math.random() * 60;
}
// Fire missiles (if capable)
if (self.canFireMissiles && self.missileTimer <= 0 && self.y > 0 && self.y < 2400) {
self.fireMissile();
self.missileTimer = self.missileRate + Math.random() * 120;
}
};
self.fireBullet = function () {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + 50;
enemyBullets.push(bullet);
game.addChild(bullet);
};
self.fireMissile = function () {
var missile = new Missile();
missile.x = self.x;
missile.y = self.y + 50;
missiles.push(missile);
game.addChild(missile);
LK.getSound('missile').play();
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
// Create explosion effect
var explosion = LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
});
game.addChild(explosion);
// Enhanced explosion animation
explosion.scaleX = 0.1;
explosion.scaleY = 0.1;
tween(explosion, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
explosion.destroy();
}
});
// Create sparkle particles - limit to prevent performance issues
if (particles.length < 50) {
for (var p = 0; p < 4; p++) {
var sparkle = new Particle();
sparkle.attachAsset('sparkle', {
anchorX: 0.5,
anchorY: 0.5
});
sparkle.x = self.x + (Math.random() - 0.5) * 100;
sparkle.y = self.y + (Math.random() - 0.5) * 100;
sparkle.velocityX = (Math.random() - 0.5) * 8;
sparkle.velocityY = (Math.random() - 0.5) * 8;
sparkle.life = 20;
sparkle.maxLife = 20;
particles.push(sparkle);
game.addChild(sparkle);
}
}
self.destroy();
LK.getSound('explosion').play();
return true;
}
return false;
};
return self;
});
var GuidedMissile = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('guidedMissile', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = 3;
self.trackingSpeed = 2.0;
self.upgradeLevel = 1;
self.baseSpeed = 8;
self.rotation = 0;
self.update = function () {
// Enhanced guided missile behavior
if (enemies.length > 0) {
// Find nearest enemy
var nearestEnemy = null;
var nearestDistance = Infinity;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestEnemy = enemy;
}
}
// Track nearest enemy
if (nearestEnemy) {
var dx = nearestEnemy.x - self.x;
var dy = nearestEnemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
// Calculate angle to target
var angle = Math.atan2(dy, dx);
// Apply guided movement with upgrade-based speed
var currentTrackingSpeed = self.trackingSpeed * self.upgradeLevel;
self.x += Math.cos(angle) * currentTrackingSpeed;
self.y += Math.sin(angle) * currentTrackingSpeed;
// Rotate missile to face direction of movement
self.rotation = angle + Math.PI / 2;
}
}
}
// Base forward movement with upgrade-based speed
var forwardSpeed = self.speed + (self.upgradeLevel - 1) * 2;
self.y -= forwardSpeed;
};
return self;
});
var KamikazeJet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('enemyJet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
graphics.tint = 0xFF4444; // Red tint to distinguish kamikaze planes
self.health = 1;
self.speed = 4;
self.trackingSpeed = 1.5;
self.isKamikaze = true;
self.update = function () {
// Kamikaze behavior - chase the player
if (player) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
// Calculate normalized direction to player
var dirX = dx / distance;
var dirY = dy / distance;
// Move towards player
self.x += dirX * self.trackingSpeed;
self.y += dirY * self.trackingSpeed;
// Rotate to face direction of movement
var angle = Math.atan2(dy, dx);
self.rotation = angle + Math.PI / 2;
}
}
// Also move forward
self.y += self.speed;
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
// Create explosion effect
var explosion = LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
});
game.addChild(explosion);
// Fade out explosion with tween effect
tween(explosion, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
explosion.destroy();
}
});
self.destroy();
LK.getSound('explosion').play();
return true;
}
return false;
};
return self;
});
var Missile = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('missile', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4;
self.damage = 2;
self.trackingSpeed = 0.5;
self.isBossMissile = false;
self.baseSpeed = 4;
self.rotation = 0;
self.update = function () {
// Enhanced homing behavior
if (player) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
// Stronger and faster tracking for boss missiles
var currentTrackingSpeed = self.isBossMissile ? self.trackingSpeed * 3 : self.trackingSpeed;
// Calculate angle to player
var angle = Math.atan2(dy, dx);
// Apply guided movement
self.x += Math.cos(angle) * currentTrackingSpeed;
self.y += Math.sin(angle) * currentTrackingSpeed;
// Rotate missile to face direction of movement for visual feedback
self.rotation = angle + Math.PI / 2;
}
}
// Base forward movement (faster for boss missiles)
var forwardSpeed = self.isBossMissile ? self.speed * 1.5 : self.speed;
self.y += forwardSpeed;
};
return self;
});
var Particle = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.life = 60;
self.maxLife = 60;
self.fadeOut = true;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.life--;
if (self.fadeOut) {
var lifeRatio = self.life / self.maxLife;
graphics.alpha = lifeRatio;
// Smooth scaling effect
graphics.scaleX = lifeRatio;
graphics.scaleY = lifeRatio;
}
if (self.life <= 0) {
self.destroy();
}
};
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.damage = 1;
self.update = function () {
self.y += self.speed;
};
return self;
});
var PlayerJet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('playerJet', {
anchorX: 0.5,
anchorY: 0.5
});
self.maxHealth = 3;
self.health = 3;
self.speed = 8;
self.fireRate = 15;
self.fireTimer = 0;
self.invulnerable = false;
self.invulnerableTimer = 0;
self.guidedMissileLevel = 0;
self.guidedMissileRate = 90;
self.guidedMissileTimer = 0;
self.update = function () {
if (self.fireTimer > 0) {
self.fireTimer--;
}
if (self.guidedMissileTimer > 0) {
self.guidedMissileTimer--;
}
if (self.invulnerable) {
self.invulnerableTimer--;
if (self.invulnerableTimer <= 0) {
self.invulnerable = false;
graphics.alpha = 1;
} else {
graphics.alpha = 0.5;
}
}
// Auto-fire
if (self.fireTimer <= 0) {
self.fire();
self.fireTimer = self.fireRate;
}
// Auto-fire guided missiles if upgraded
if (self.guidedMissileLevel > 0 && self.guidedMissileTimer <= 0) {
self.fireGuidedMissile();
self.guidedMissileTimer = self.guidedMissileRate;
}
};
self.fire = function () {
var bullet = new PlayerBullet();
bullet.x = self.x;
bullet.y = self.y - 50;
playerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
};
self.fireGuidedMissile = function () {
var guidedMissile = new GuidedMissile();
guidedMissile.x = self.x;
guidedMissile.y = self.y - 50;
guidedMissile.upgradeLevel = self.guidedMissileLevel;
// Visual effect based on upgrade level
var tintColor = 0x00FF00 + self.guidedMissileLevel * 0x001100;
tween(guidedMissile, {
tint: tintColor,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100
});
guidedMissiles.push(guidedMissile);
game.addChild(guidedMissile);
LK.getSound('missile').play();
};
self.takeDamage = function (damage) {
if (self.invulnerable) return;
self.health -= damage;
self.invulnerable = true;
self.invulnerableTimer = 60;
LK.effects.flashObject(self, 0xFF0000, 500);
if (self.health <= 0) {
gameOver();
}
updateHealthDisplay();
};
return self;
});
var Rocket = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('missile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
graphics.tint = 0xFF4444;
self.speed = 5;
self.damage = 3;
self.update = function () {
self.y += self.speed;
};
return self;
});
var StarField = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = Math.random() * 2 + 1;
self.twinklePhase = Math.random() * Math.PI * 2;
self.baseAlpha = Math.random() * 0.8 + 0.2;
self.update = function () {
self.y += self.speed;
// Twinkling effect
self.twinklePhase += 0.1;
graphics.alpha = self.baseAlpha + Math.sin(self.twinklePhase) * 0.3;
};
return self;
});
var Trail = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('trail', {
anchorX: 0.5,
anchorY: 0.5
});
self.life = 30;
self.maxLife = 30;
self.update = function () {
self.life--;
graphics.alpha = self.life / self.maxLife;
graphics.scaleY = self.life / self.maxLife;
if (self.life <= 0) {
self.destroy();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x001122,
fullscreen: true
});
/****
* Game Code
****/
// Background setup
var background1 = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
var background2 = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: -2732
});
game.addChild(background1);
game.addChild(background2);
// Game state variables
var currentWave = 1;
var maxWaves = 20;
var waveEnemies = [];
var enemiesSpawned = 0;
var enemiesToSpawn = 5;
var spawnTimer = 0;
var spawnRate = 60;
var waveComplete = false;
var gameState = 'playing'; // 'playing', 'upgrading', 'gameover'
// Game objects
var player = null;
var playerBullets = [];
var enemyBullets = [];
var missiles = [];
var rockets = [];
var guidedMissiles = [];
var enemies = [];
var currentBoss = null;
var particles = [];
var trails = [];
var maxTrails = 20;
var stars = [];
// UI elements
var waveText = null;
var healthText = null;
var upgradeContainer = null;
// Initialize player
player = new PlayerJet();
player.x = 1024;
player.y = 2800;
game.addChild(player);
// Smooth entrance animation
tween(player, {
y: 2300
}, {
duration: 1000,
easing: tween.easeOut
});
// Initialize UI
waveText = new Text2('Wave: 1', {
size: 60,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
LK.gui.top.addChild(waveText);
waveText.y = 50;
healthText = new Text2('Health: 3', {
size: 80,
fill: 0x00FF00
});
healthText.anchor.set(1, 0);
LK.gui.topRight.addChild(healthText);
healthText.x = -20;
healthText.y = 50;
function updateHealthDisplay() {
healthText.setText('Health: ' + player.health);
}
function updateWaveDisplay() {
waveText.setText('Wave: ' + currentWave);
}
function spawnEnemy() {
// Spawn boss on waves 10 and 20
if ((currentWave === 10 || currentWave === 20) && enemiesSpawned === 0) {
var boss = new Boss();
boss.x = 1024;
boss.y = -150;
if (currentWave === 10) {
// Wave 10 boss: 2 missiles, 1 bullet
boss.maxHealth = 30;
boss.health = 30;
boss.weaponSystems.bullets = 1;
boss.weaponSystems.missiles = 2;
boss.weaponSystems.rockets = 0;
boss.bossType = 1;
} else if (currentWave === 20) {
// Wave 20 boss: 2 rockets, 2 bullets, more health
boss.maxHealth = 50;
boss.health = 50;
boss.weaponSystems.bullets = 2;
boss.weaponSystems.missiles = 0;
boss.weaponSystems.rockets = 2;
boss.bossType = 2;
}
enemies.push(boss);
game.addChild(boss);
currentBoss = boss;
// Boss entrance animation
boss.alpha = 0;
boss.scaleX = 0.5;
boss.scaleY = 0.5;
tween(boss, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 1500,
easing: tween.easeOut
});
enemiesSpawned++;
return;
}
var enemy;
// Spawn kamikaze planes starting from wave 15
if (currentWave >= 15 && Math.random() < 0.3) {
enemy = new KamikazeJet();
enemy.x = Math.random() * 1700 + 174;
enemy.y = -90;
// Scale kamikaze difficulty
enemy.health = Math.floor(currentWave / 8) + 1;
enemy.speed = 3 + currentWave * 0.3;
enemy.trackingSpeed = 1.5 + currentWave * 0.1;
} else {
enemy = new EnemyJet();
enemy.x = Math.random() * 1700 + 174;
enemy.y = -90;
// Scale difficulty based on wave
enemy.health = Math.floor(currentWave / 5) + 1;
enemy.speed = 2 + currentWave * 0.2;
enemy.fireRate = Math.max(60 - currentWave * 2, 30);
// Enable missiles for later waves
if (currentWave >= 5) {
enemy.canFireMissiles = true;
}
}
enemies.push(enemy);
game.addChild(enemy);
// Enemy entrance animation
enemy.alpha = 0;
enemy.y -= 100;
tween(enemy, {
alpha: 1,
y: enemy.y + 100
}, {
duration: 800,
easing: tween.easeOut
});
enemiesSpawned++;
}
function startWave() {
currentWave++;
if (currentWave > maxWaves) {
showYouWinScreen();
return;
}
// Boss waves spawn only 1 enemy (the boss)
if (currentWave === 10 || currentWave === 20) {
enemiesToSpawn = 1;
} else {
// Standard enemy count progression
enemiesToSpawn = 5 + Math.floor(currentWave * 1.5);
}
enemiesSpawned = 0;
spawnTimer = 0;
waveComplete = false;
gameState = 'playing';
updateWaveDisplay();
// Wave transition animation
waveText.alpha = 0;
waveText.scaleX = 2;
waveText.scaleY = 2;
tween(waveText, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 1000,
easing: tween.bounceOut
});
// Hide upgrade menu if visible
if (upgradeContainer) {
upgradeContainer.destroy();
upgradeContainer = null;
}
}
function completeWave() {
if (currentWave >= maxWaves) {
showYouWinScreen();
return;
}
waveComplete = true;
gameState = 'upgrading';
// Fade out all bullets and missiles smoothly
for (var i = 0; i < playerBullets.length; i++) {
var bullet = playerBullets[i];
tween(bullet, {
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
if (this.target && this.target.destroy) {
this.target.destroy();
}
}
});
}
playerBullets = [];
for (var i = 0; i < enemyBullets.length; i++) {
var bullet = enemyBullets[i];
tween(bullet, {
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
if (this.target && this.target.destroy) {
this.target.destroy();
}
}
});
}
enemyBullets = [];
for (var i = 0; i < missiles.length; i++) {
var missile = missiles[i];
tween(missile, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 1000,
onFinish: function onFinish() {
if (this.target && this.target.destroy) {
this.target.destroy();
}
}
});
}
missiles = [];
for (var i = 0; i < rockets.length; i++) {
var rocket = rockets[i];
tween(rocket, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 1000,
onFinish: function onFinish() {
if (this.target && this.target.destroy) {
this.target.destroy();
}
}
});
}
rockets = [];
for (var i = 0; i < guidedMissiles.length; i++) {
var guidedMissile = guidedMissiles[i];
tween(guidedMissile, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 1000,
onFinish: function onFinish() {
if (this.target && this.target.destroy) {
this.target.destroy();
}
}
});
}
guidedMissiles = [];
showUpgradeMenu();
}
function showUpgradeMenu() {
upgradeContainer = new Container();
game.addChild(upgradeContainer);
var titleText = new Text2('Choose Upgrade', {
size: 80,
fill: 0xFFFF00
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
upgradeContainer.addChild(titleText);
var upgrades = [{
text: 'Faster Firing',
action: function action() {
player.fireRate = Math.max(player.fireRate - 3, 5);
}
}, {
text: 'More Health',
action: function action() {
player.maxHealth++;
player.health = player.maxHealth;
}
}, {
text: 'Faster Movement',
action: function action() {
player.speed += 2;
}
}];
// Add guided missiles upgrade only from wave 10 onwards
if (currentWave >= 10) {
upgrades.push({
text: 'Guided Missiles',
action: function action() {
player.guidedMissileLevel++;
// Faster firing rate with each upgrade (but starting from slower base rate)
player.guidedMissileRate = Math.max(90 - player.guidedMissileLevel * 10, 30);
}
});
}
for (var i = 0; i < Math.min(upgrades.length, 4); i++) {
var upgrade = upgrades[i];
var button = new Container();
var buttonBg = LK.getAsset('upgradeButton', {
anchorX: 0.5,
anchorY: 0.5
});
button.addChild(buttonBg);
var buttonText = new Text2(upgrade.text, {
size: 40,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
button.addChild(buttonText);
button.x = 1024;
button.y = 1200 + i * 200;
button.upgrade = upgrade;
button.down = function (x, y, obj) {
this.upgrade.action();
updateHealthDisplay();
startWave();
};
upgradeContainer.addChild(button);
}
}
function gameOver() {
gameState = 'gameover';
showGameOverScreen();
}
function showGameOverScreen() {
var gameOverContainer = new Container();
game.addChild(gameOverContainer);
gameOverContainer.zIndex = 1000;
// Semi-transparent background
var overlay = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
overlay.alpha = 0.7;
overlay.tint = 0x000000;
gameOverContainer.addChild(overlay);
// Game Over title
var gameOverText = new Text2('GAME OVER', {
size: 120,
fill: 0xFF0000
});
gameOverText.anchor.set(0.5, 0.5);
gameOverText.x = 1024;
gameOverText.y = 1200;
gameOverContainer.addChild(gameOverText);
// Final score/wave text
var finalScoreText = new Text2('Final Wave: ' + currentWave, {
size: 60,
fill: 0xFFFFFF
});
finalScoreText.anchor.set(0.5, 0.5);
finalScoreText.x = 1024;
finalScoreText.y = 1350;
gameOverContainer.addChild(finalScoreText);
// Restart button
var restartButton = new Container();
var buttonBg = LK.getAsset('upgradeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.5
});
buttonBg.tint = 0x00AA00;
restartButton.addChild(buttonBg);
var buttonText = new Text2('PLAY AGAIN', {
size: 50,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
restartButton.addChild(buttonText);
restartButton.x = 1024;
restartButton.y = 1600;
restartButton.down = function (x, y, obj) {
restartGame();
};
gameOverContainer.addChild(restartButton);
}
function showYouWinScreen() {
var youWinContainer = new Container();
game.addChild(youWinContainer);
youWinContainer.zIndex = 1000;
// Semi-transparent background
var overlay = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
overlay.alpha = 0.7;
overlay.tint = 0x000000;
youWinContainer.addChild(overlay);
// You Win title
var youWinText = new Text2('YOU WIN!', {
size: 120,
fill: 0x00FF00
});
youWinText.anchor.set(0.5, 0.5);
youWinText.x = 1024;
youWinText.y = 1200;
youWinContainer.addChild(youWinText);
// Congratulations text
var congratsText = new Text2('You defeated all waves!', {
size: 60,
fill: 0xFFFFFF
});
congratsText.anchor.set(0.5, 0.5);
congratsText.x = 1024;
congratsText.y = 1350;
youWinContainer.addChild(congratsText);
// Play again button
var playAgainButton = new Container();
var buttonBg = LK.getAsset('upgradeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.5
});
buttonBg.tint = 0x00AA00;
playAgainButton.addChild(buttonBg);
var buttonText = new Text2('PLAY AGAIN', {
size: 50,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
playAgainButton.addChild(buttonText);
playAgainButton.x = 1024;
playAgainButton.y = 1600;
playAgainButton.down = function (x, y, obj) {
restartGame();
};
youWinContainer.addChild(playAgainButton);
}
function restartGame() {
// Reset game state
gameState = 'playing';
currentWave = 1;
waveComplete = false;
enemiesSpawned = 0;
enemiesToSpawn = 5;
spawnTimer = 0;
// Clear all game objects
for (var i = playerBullets.length - 1; i >= 0; i--) {
playerBullets[i].destroy();
}
playerBullets = [];
for (var i = enemyBullets.length - 1; i >= 0; i--) {
enemyBullets[i].destroy();
}
enemyBullets = [];
for (var i = missiles.length - 1; i >= 0; i--) {
missiles[i].destroy();
}
missiles = [];
for (var i = rockets.length - 1; i >= 0; i--) {
rockets[i].destroy();
}
rockets = [];
for (var i = guidedMissiles.length - 1; i >= 0; i--) {
guidedMissiles[i].destroy();
}
guidedMissiles = [];
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
}
enemies = [];
currentBoss = null;
// Clear particles and trails
for (var i = particles.length - 1; i >= 0; i--) {
particles[i].destroy();
}
particles = [];
for (var i = trails.length - 1; i >= 0; i--) {
trails[i].destroy();
}
trails = [];
for (var i = stars.length - 1; i >= 0; i--) {
stars[i].destroy();
}
stars = [];
// Reset player
if (player) {
player.destroy();
}
player = new PlayerJet();
player.x = 1024;
player.y = 2300;
player.health = player.maxHealth;
player.fireRate = 15;
player.speed = 8;
player.guidedMissileLevel = 0;
player.guidedMissileTimer = 0;
game.addChild(player);
// Clear any existing containers
if (upgradeContainer) {
upgradeContainer.destroy();
upgradeContainer = null;
}
// Find and remove all game over or win screen containers
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
if (child instanceof Container && child.zIndex === 1000) {
child.destroy();
}
}
// Update UI
updateWaveDisplay();
updateHealthDisplay();
}
// Input handling
var dragActive = false;
game.down = function (x, y, obj) {
if (gameState === 'playing') {
dragActive = true;
player.x = x;
player.y = y;
}
};
game.move = function (x, y, obj) {
if (gameState === 'playing' && dragActive) {
var targetX = Math.max(70, Math.min(1978, x));
var targetY = Math.max(150, Math.min(2582, y));
// Direct position update for responsive movement
player.x = targetX;
player.y = targetY;
}
};
game.up = function (x, y, obj) {
dragActive = false;
};
// Main game loop
game.update = function () {
if (gameState !== 'playing') return;
// Background is now static - no movement or scaling
// Spawn enemies
if (enemiesSpawned < enemiesToSpawn) {
spawnTimer++;
if (spawnTimer >= spawnRate) {
spawnEnemy();
spawnTimer = 0;
}
}
// Update player bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
if (bullet.y < -50) {
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)) {
if (enemy.takeDamage(bullet.damage)) {
enemies.splice(j, 1);
// Reset boss reference if boss was destroyed
if (enemy === currentBoss) {
currentBoss = null;
}
}
bullet.destroy();
playerBullets.splice(i, 1);
break;
}
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
if (bullet.y > 2782) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check collision with player
if (bullet.intersects(player)) {
player.takeDamage(bullet.damage);
bullet.destroy();
enemyBullets.splice(i, 1);
}
}
// Update missiles
for (var i = missiles.length - 1; i >= 0; i--) {
var missile = missiles[i];
if (missile.y > 2782 || missile.y < -50 || missile.x < -50 || missile.x > 2098) {
missile.destroy();
missiles.splice(i, 1);
continue;
}
// Check collision with player
if (missile.intersects(player)) {
player.takeDamage(missile.damage);
missile.destroy();
missiles.splice(i, 1);
}
}
// Update rockets
for (var i = rockets.length - 1; i >= 0; i--) {
var rocket = rockets[i];
if (rocket.y > 2782 || rocket.y < -50 || rocket.x < -50 || rocket.x > 2098) {
rocket.destroy();
rockets.splice(i, 1);
continue;
}
// Check collision with player
if (rocket.intersects(player)) {
player.takeDamage(rocket.damage);
rocket.destroy();
rockets.splice(i, 1);
}
}
// Update guided missiles
for (var i = guidedMissiles.length - 1; i >= 0; i--) {
var guidedMissile = guidedMissiles[i];
if (guidedMissile.y < -50 || guidedMissile.x < -50 || guidedMissile.x > 2098) {
guidedMissile.destroy();
guidedMissiles.splice(i, 1);
continue;
}
// Check collision with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (guidedMissile.intersects(enemy)) {
if (enemy.takeDamage(guidedMissile.damage)) {
enemies.splice(j, 1);
// Reset boss reference if boss was destroyed
if (enemy === currentBoss) {
currentBoss = null;
}
}
guidedMissile.destroy();
guidedMissiles.splice(i, 1);
break;
}
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.y > 2782) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
// Check kamikaze collision with player
if (enemy.isKamikaze && enemy.intersects(player)) {
// Kamikaze plane crashes into player
player.takeDamage(2); // Kamikaze does more damage
enemy.destroy();
enemies.splice(i, 1);
// Create explosion at crash site
var explosion = LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
x: enemy.x,
y: enemy.y
});
game.addChild(explosion);
tween(explosion, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
explosion.destroy();
}
});
LK.getSound('explosion').play();
}
}
// Update particles
for (var i = particles.length - 1; i >= 0; i--) {
var particle = particles[i];
if (particle.life <= 0) {
particle.destroy();
particles.splice(i, 1);
}
}
// Update trails
for (var i = trails.length - 1; i >= 0; i--) {
var trail = trails[i];
if (trail.life <= 0 || trail.y > 2832) {
trail.destroy();
trails.splice(i, 1);
}
}
// Create engine trail for player - reduced frequency
if (LK.ticks % 5 === 0 && trails.length < maxTrails) {
var trail = new Trail();
trail.x = player.x;
trail.y = player.y + 60;
trails.push(trail);
game.addChild(trail);
}
// Update starfield
for (var i = stars.length - 1; i >= 0; i--) {
var star = stars[i];
if (star.y > 2782) {
star.destroy();
stars.splice(i, 1);
}
}
// Generate new stars - limit to prevent performance issues
if (LK.ticks % 30 === 0 && stars.length < 30) {
var star = new StarField();
star.x = Math.random() * 2048;
star.y = -10;
stars.push(star);
game.addChild(star);
}
// Check wave completion
if (enemiesSpawned >= enemiesToSpawn && enemies.length === 0 && !waveComplete) {
completeWave();
}
};
// Start first wave
updateWaveDisplay();
updateHealthDisplay();
LK.playMusic('bgmusic');
Un avión militar enemigo visto desde arriba sin fondo y realista. 2d. High contrast. No shadows
Explosión realista vista desde arriba. 2d. High contrast. No shadows
Un avión militar realista visto desde arriba
Realistic guided missile seen from above
Fondo aéreo con nubes y montañas realista visto desde arriba
Bala realista
2 estelas realistas de avión vista desde arriba sin avión. In-Game asset