/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BackgroundStar = Container.expand(function () {
var self = Container.call(this);
var starGraphics = self.attachAsset('backgroundStar', {
anchorX: 0.5,
anchorY: 0.5
});
// Random movement properties
self.speedX = (Math.random() - 0.5) * 2; // -1 to 1
self.speedY = Math.random() * 2 + 1; // 1 to 3
self.originalColor = 0x4a5d4a; // Dark green for day
self.nightColor = 0xffffff; // White for night
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
// Wrap around screen edges
if (self.x < 0) self.x = 2048;
if (self.x > 2048) self.x = 0;
if (self.y > 2732) {
self.y = -10;
// Randomize position when wrapping
self.x = Math.random() * 2048;
}
};
return self;
});
var Bomb = Container.expand(function () {
var self = Container.call(this);
var bombGraphics = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.targetX = 0;
self.targetY = 0;
self.update = function () {
// Basic fallback movement if no tween is active
if (!self.targetX && !self.targetY) {
self.y += self.speed;
}
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -12;
self.lastX = 0;
self.lastY = 0;
self.stuckTimer = 0;
self.update = function () {
self.y += self.speed;
// Track last position to detect if bullet is stuck
var moveThreshold = 2; // Minimum movement required
var dx = Math.abs(self.x - self.lastX);
var dy = Math.abs(self.y - self.lastY);
var totalMovement = dx + dy;
// If bullet hasn't moved enough, increment stuck timer
if (totalMovement < moveThreshold) {
self.stuckTimer++;
} else {
self.stuckTimer = 0; // Reset timer if bullet is moving
}
// If stuck for more than 3 seconds (180 frames at 60fps), mark for destruction
if (self.stuckTimer >= 180) {
self.destroy();
return;
}
// Store current position for next frame comparison
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
var DestructionMark = Container.expand(function () {
var self = Container.call(this);
var markGraphics = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5
});
markGraphics.tint = 0x000000; // Black color
markGraphics.alpha = 0.8;
self.update = function () {
self.y += scrollSpeed; // Move with ground scroll
};
return self;
});
var Explosion = Container.expand(function () {
var self = Container.call(this);
var explosionGraphics = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifespan = 30;
self.update = function () {
self.lifespan--;
self.alpha = self.lifespan / 30;
if (self.lifespan <= 0) {
self.destroy();
}
};
return self;
});
var FighterJet = Container.expand(function () {
var self = Container.call(this);
var jetGraphics = self.attachAsset('fighterJet', {
anchorX: 0.5,
anchorY: 0.5
});
var bombTargetCircle = self.attachAsset('bombTarget', {
anchorX: 0.5,
anchorY: 0.5
});
bombTargetCircle.y = -400; // Position much further in front of the jet
bombTargetCircle.alpha = 0.6;
bombTargetCircle.visible = false;
self.bombTargetCircle = bombTargetCircle;
self.lives = 3;
self.flareCount = 3;
self.maxFlares = 3;
self.flareRechargeTimer = 0;
self.aaSystemsDestroyed = 0;
self.update = function () {
// Recharge flares over time
if (self.flareCount < self.maxFlares) {
self.flareRechargeTimer++;
if (self.flareRechargeTimer >= 150) {
// 2.5 seconds at 60fps
self.flareCount++;
self.flareRechargeTimer = 0;
updateFlareDisplay();
}
}
};
return self;
});
var Flare = Container.expand(function () {
var self = Container.call(this);
var flareGraphics = self.attachAsset('flare', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifespan = 240; // 4 seconds at 60fps
self.update = function () {
self.lifespan--;
// Track nearest missile
var nearestMissile = null;
var nearestDistance = Infinity;
for (var i = 0; i < missiles.length; i++) {
var missile = missiles[i];
var dx = missile.x - self.x;
var dy = missile.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestMissile = missile;
}
}
// Move towards nearest missile if within tracking range
if (nearestMissile && nearestDistance < 300) {
var dx = nearestMissile.x - self.x;
var dy = nearestMissile.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * 8;
self.y += dy / distance * 8;
}
}
// Destroy flare after 4 seconds no matter what
if (self.lifespan <= 0) {
self.destroy();
}
};
return self;
});
var GroundTarget = Container.expand(function (targetType) {
var self = Container.call(this);
var targetGraphics = self.attachAsset(targetType, {
anchorX: 0.5,
anchorY: 0.5
});
self.targetType = targetType;
self.points = getPointsForTarget(targetType);
self.canShootMissiles = targetType === 's300System';
self.missileTimer = 0;
// Set random movement direction for vehicles (only once at creation)
if (targetType === 'car' || targetType === 'armyVan') {
self.moveDirection = Math.random() < 0.5 ? -4 : 4; // Either -4 (left) or 4 (right)
} else {
self.moveDirection = 0;
}
self.update = function () {
self.y += scrollSpeed;
// Movement for cars and army vans in one consistent direction
if (self.targetType === 'car' || self.targetType === 'armyVan') {
// Don't move if moveDirection is 0 (stationary trucks at nuclear facility)
if (self.moveDirection !== 0) {
// Move consistently in the chosen direction
self.x += self.moveDirection;
// Mirror vehicle to face movement direction
if (self.moveDirection > 0) {
// Moving right - no flip needed (default orientation)
targetGraphics.scale.x = Math.abs(targetGraphics.scale.x);
} else {
// Moving left - flip horizontally
targetGraphics.scale.x = -Math.abs(targetGraphics.scale.x);
}
// Keep vehicles within screen bounds and reverse direction if hitting edges
if (self.x < 50) {
self.x = 50;
self.moveDirection = Math.abs(self.moveDirection); // Force positive direction
}
if (self.x > 1998) {
self.x = 1998;
self.moveDirection = -Math.abs(self.moveDirection); // Force negative direction
}
}
}
// S-300 systems shoot missiles at the player (modified for nuclear facility)
if (self.canShootMissiles && self.y > 200 && self.y < 2000) {
// Check if this is a nuclear facility S-300 with shooting restrictions
if (self.canShootOnce !== undefined) {
// Nuclear facility S-300 - can only shoot once until missile hits or is destroyed
if (self.canShootOnce && !self.hasShot) {
self.missileTimer++;
if (self.missileTimer >= 180) {
// Every 3 seconds
self.fireMissile();
self.hasShot = true;
self.canShootOnce = false;
self.missileTimer = 0;
}
}
} else {
// Regular S-300 - shoots normally
self.missileTimer++;
if (self.missileTimer >= 180) {
// Every 3 seconds
self.fireMissile();
self.missileTimer = 0;
}
}
}
};
self.fireMissile = function () {
var missile = new Missile();
missile.x = self.x;
missile.y = self.y;
missile.targetX = fighterJet.x;
missile.targetY = fighterJet.y;
missiles.push(missile);
game.addChild(missile);
LK.getSound('missile').play();
};
return self;
});
var Missile = Container.expand(function () {
var self = Container.call(this);
var missileGraphics = self.attachAsset('missile', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.targetX = 0;
self.targetY = 0;
self.lastX = 0;
self.lastY = 0;
self.stuckTimer = 0;
self.bottomTimer = 0; // Track time spent in bottom area
self.lifeTimer = 540; // 9 seconds at 60fps
self.update = function () {
// Decrease lifespan timer
self.lifeTimer--;
if (self.lifeTimer <= 0) {
self.destroy();
return;
}
// Track last position to detect if missile is stuck
var moveThreshold = 2; // Minimum movement required
var dx = Math.abs(self.x - self.lastX);
var dy = Math.abs(self.y - self.lastY);
var totalMovement = dx + dy;
// If missile hasn't moved enough, increment stuck timer
if (totalMovement < moveThreshold) {
self.stuckTimer++;
} else {
self.stuckTimer = 0; // Reset timer if missile is moving
}
// If stuck for more than 3 seconds (180 frames at 60fps), mark for destruction
if (self.stuckTimer >= 180) {
self.destroy();
return;
}
// Check if missile is in bottom 20% of screen (2732 * 0.8 = 2185.6)
if (self.y > 2185) {
self.bottomTimer++;
} else {
self.bottomTimer = 0; // Reset timer if missile leaves bottom area
}
// If missile has been in bottom area for more than 3 seconds (180 frames at 60fps), mark for destruction
if (self.bottomTimer >= 180) {
self.destroy();
return;
}
// Store current position for next frame comparison
self.lastX = self.x;
self.lastY = self.y;
// Move towards target (fighter jet)
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
var gameStarted = false;
var startScreen = null;
var startButton = null;
var fighterJet = null;
var scrollSpeed = 3;
var missiles = [];
// Game statistics tracking
var gameStats = {
buildingsDestroyed: 0,
targetBuildingsDestroyed: 0,
missilesIntercepted: 0,
batteriesDestroyed: 0,
missileStorageDestroyed: 0,
vehiclesDestroyed: 0
};
// Nuclear facility tracking
var nightsPassed = 0;
var nuclearFacilityReached = false;
var nuclearFacility = null;
var nuclearFacilityHits = 0;
var facilityS300Systems = [];
var facilityTrucks = [];
// Create start screen
startScreen = new Container();
game.addChild(startScreen);
// Add title text
var titleText = new Text2('B-2 Mission', {
size: 120,
fill: '#ffffff'
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
startScreen.addChild(titleText);
// Create start button
startButton = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 1.2
});
startButton.x = 1024;
startButton.y = 700;
startButton.tint = 0x27ae60;
startScreen.addChild(startButton);
// Add button text
var buttonText = new Text2('START', {
size: 120,
fill: '#ffffff'
});
buttonText.anchor.set(0.5, 0.5);
buttonText.x = 1024;
buttonText.y = 700;
startScreen.addChild(buttonText);
// Add mouse illustration
var mouseIllustration = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.15,
scaleY: 0.4
});
mouseIllustration.x = 1024;
mouseIllustration.y = 2000;
mouseIllustration.tint = 0xcccccc;
startScreen.addChild(mouseIllustration);
// Add legend container
var legendContainer = new Container();
startScreen.addChild(legendContainer);
// Legend title
var legendTitle = new Text2('TARGET LEGEND', {
size: 90,
fill: '#ffffff'
});
legendTitle.anchor.set(0.5, 0.5);
legendTitle.x = 1024;
legendTitle.y = 850;
startScreen.addChild(legendTitle);
// Create legend entries with visual representations
var legendY = 950;
var legendSpacing = 140;
// Buildings (Civilian) - Bombs only
var civilianBuildingGraphic = LK.getAsset('building', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
civilianBuildingGraphic.x = 300;
civilianBuildingGraphic.y = legendY;
startScreen.addChild(civilianBuildingGraphic);
var civilianBuildingText = new Text2('Civilian Building: -10 pts (Bombs Only)', {
size: 60,
fill: '#ff6666'
});
civilianBuildingText.anchor.set(0, 0.5);
civilianBuildingText.x = 450;
civilianBuildingText.y = legendY;
startScreen.addChild(civilianBuildingText);
// Target Buildings (Military) - Bombs only
legendY += legendSpacing;
var targetBuildingGraphic = LK.getAsset('targetBuilding', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
targetBuildingGraphic.x = 300;
targetBuildingGraphic.y = legendY;
startScreen.addChild(targetBuildingGraphic);
var targetBuildingText = new Text2('Military Building: +10 pts (Bombs Only)', {
size: 60,
fill: '#66ff66'
});
targetBuildingText.anchor.set(0, 0.5);
targetBuildingText.x = 450;
targetBuildingText.y = legendY;
startScreen.addChild(targetBuildingText);
// S-300 Systems - Bombs only
legendY += legendSpacing;
var s300Graphic = LK.getAsset('s300System', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
s300Graphic.x = 300;
s300Graphic.y = legendY;
startScreen.addChild(s300Graphic);
var s300Text = new Text2('S-300 System: +15 pts (Bombs Only)', {
size: 60,
fill: '#66ff66'
});
s300Text.anchor.set(0, 0.5);
s300Text.x = 450;
s300Text.y = legendY;
startScreen.addChild(s300Text);
// Missile Storage - Bombs only
legendY += legendSpacing;
var missileStorageGraphic = LK.getAsset('missileStorage', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
missileStorageGraphic.x = 300;
missileStorageGraphic.y = legendY;
startScreen.addChild(missileStorageGraphic);
var missileStorageText = new Text2('Missile Storage: +10 pts (Bombs Only)', {
size: 60,
fill: '#66ff66'
});
missileStorageText.anchor.set(0, 0.5);
missileStorageText.x = 450;
missileStorageText.y = legendY;
startScreen.addChild(missileStorageText);
// Civilian Cars - Cannon only
legendY += legendSpacing;
var carGraphic = LK.getAsset('car', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
carGraphic.x = 300;
carGraphic.y = legendY;
startScreen.addChild(carGraphic);
var carText = new Text2('Civilian Car: -5 pts (Cannon & Bombs)', {
size: 60,
fill: '#ff6666'
});
carText.anchor.set(0, 0.5);
carText.x = 450;
carText.y = legendY;
startScreen.addChild(carText);
// Army Vans - Cannon only
legendY += legendSpacing;
var armyVanGraphic = LK.getAsset('armyVan', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
armyVanGraphic.x = 300;
armyVanGraphic.y = legendY;
startScreen.addChild(armyVanGraphic);
var armyVanText = new Text2('Army Van: +5 pts (Cannon & Bombs)', {
size: 60,
fill: '#66ff66'
});
armyVanText.anchor.set(0, 0.5);
armyVanText.x = 450;
armyVanText.y = legendY;
startScreen.addChild(armyVanText);
// Nuclear Facility - Bombs only, requires 3 hits
legendY += legendSpacing;
var nuclearFacilityGraphic = LK.getAsset('nuclearFacility', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
nuclearFacilityGraphic.x = 300;
nuclearFacilityGraphic.y = legendY;
startScreen.addChild(nuclearFacilityGraphic);
var nuclearFacilityText = new Text2('Nuclear Facility: +50 pts (3 Bombs Required)', {
size: 60,
fill: '#ffff00'
});
nuclearFacilityText.anchor.set(0, 0.5);
nuclearFacilityText.x = 450;
nuclearFacilityText.y = legendY;
startScreen.addChild(nuclearFacilityText);
// Add weapon demonstrations
var weaponY = 2300;
var bombGraphic = LK.getAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.5
});
bombGraphic.x = 350;
bombGraphic.y = weaponY;
startScreen.addChild(bombGraphic);
var bombWeaponText = new Text2('BOMB - Move mouse forward - Destroys Buildings & S-300', {
size: 65,
fill: '#ffff00'
});
bombWeaponText.anchor.set(0, 0.5);
bombWeaponText.x = 450;
bombWeaponText.y = weaponY;
startScreen.addChild(bombWeaponText);
weaponY += 120;
var bulletGraphic = LK.getAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3
});
bulletGraphic.x = 350;
bulletGraphic.y = weaponY;
startScreen.addChild(bulletGraphic);
var bulletWeaponText = new Text2('CANNON - Click mouse - Destroys Vehicles Only', {
size: 65,
fill: '#ffff00'
});
bulletWeaponText.anchor.set(0, 0.5);
bulletWeaponText.x = 450;
bulletWeaponText.y = weaponY;
startScreen.addChild(bulletWeaponText);
weaponY += 120;
var flareGraphic = LK.getAsset('flare', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3
});
flareGraphic.x = 350;
flareGraphic.y = weaponY;
startScreen.addChild(flareGraphic);
var flareWeaponText = new Text2('FLARES - Shake the mouse left and right to use', {
size: 65,
fill: '#ffff00'
});
flareWeaponText.anchor.set(0, 0.5);
flareWeaponText.x = 450;
flareWeaponText.y = weaponY;
startScreen.addChild(flareWeaponText);
// Handle start button click
startButton.down = function (x, y, obj) {
if (gameStarted) return;
gameStarted = true;
startScreen.destroy();
// Initialize game after start button is clicked
initializeGame();
};
function getPointsForTarget(targetType) {
switch (targetType) {
case 'building':
return -10;
// Civilian building penalty
case 'targetBuilding':
return 10;
// Military building points
case 'car':
return -5;
// Regular truck penalty
case 'armyVan':
return 5;
// Military truck points
case 's300System':
return 15;
// S-300 system points
case 'missileStorage':
return 10;
// Missile storage points
default:
return 10;
}
}
function updateFlareDisplay() {
if (fighterJet && fighterJet.flareCount !== undefined) {
var flareText = LK.gui.top.children.find(function (child) {
return child.text && child.text.includes('Flares:');
});
if (flareText) {
flareText.setText('Flares: ' + fighterJet.flareCount);
}
}
}
function initializeGame() {
// Set initial desert background color
game.setBackgroundColor(0xC2B280); // Desert sandy color
// Start background color cycling after game begins
var backgroundTimer = 0;
var isNightMode = false;
var glowingElements = []; // Track all elements that should glow
var backgroundStars = []; // Track background stars
// Create initial background stars
for (var i = 0; i < 150; i++) {
var star = new BackgroundStar();
star.x = Math.random() * 2048;
star.y = Math.random() * 2732;
// Initialize stars to single pixel size at game start
star.children[0].scale.x = 0.1;
star.children[0].scale.y = 0.1;
backgroundStars.push(star);
game.addChild(star);
}
function updateStarColors() {
for (var i = 0; i < backgroundStars.length; i++) {
var star = backgroundStars[i];
if (isNightMode) {
star.children[0].tint = star.nightColor;
// Variable sizes during night - can be up to 2x normal size
var nightScale = 1 + Math.random() * 1.0; // 1 to 2 times normal size
star.children[0].scale.x = nightScale;
star.children[0].scale.y = nightScale;
} else {
star.children[0].tint = star.originalColor;
// Single pixel size during day (1/10th of original 10px size)
star.children[0].scale.x = 0.1;
star.children[0].scale.y = 0.1;
}
}
}
function addGlowEffect(element) {
if (element && !element.isGlowing) {
element.isGlowing = true;
element.originalTint = element.tint || 0xffffff;
glowingElements.push(element);
// Enhanced glow effect with multiple colors
var glowColors = [0x00ffff, 0xffff00, 0xff00ff, 0x00ff00];
var randomColor = glowColors[Math.floor(Math.random() * glowColors.length)];
// Create pulsing glow effect with random color
tween(element, {
tint: randomColor
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (element.isGlowing) {
tween(element, {
tint: 0xffffff
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (element.isGlowing) {
addGlowEffect(element); // Continue glow cycle
}
}
});
}
}
});
}
}
function removeGlowEffect(element) {
if (element && element.isGlowing) {
element.isGlowing = false;
tween.stop(element, {
tint: true
});
element.tint = element.originalTint || 0xffffff;
}
}
var nightGlowTimer = 0;
function enhanceGlowEffect(element) {
if (element && element.isGlowing) {
// Stop any existing tween
tween.stop(element, {
tint: true
});
// Create intense glow burst effect
var intenseBrightColors = [0x00ffff, 0xffff00, 0xff00ff, 0x00ff00, 0xff8800, 0x8800ff];
var randomColor = intenseBrightColors[Math.floor(Math.random() * intenseBrightColors.length)];
// Enhanced bright glow effect
tween(element, {
tint: randomColor
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (element.isGlowing) {
tween(element, {
tint: 0xffffff
}, {
duration: 300,
easing: tween.easeIn
});
}
}
});
}
}
function updateNightGlow() {
if (isNightMode) {
// Increment night glow timer
nightGlowTimer++;
// Every 1.5 seconds (90 frames at 60fps), enhance the glow
var shouldEnhanceGlow = nightGlowTimer % 90 === 0;
// Add glow to all missiles
for (var i = 0; i < missiles.length; i++) {
addGlowEffect(missiles[i]);
if (shouldEnhanceGlow) {
enhanceGlowEffect(missiles[i]);
}
}
// Add glow to all bullets
for (var i = 0; i < bullets.length; i++) {
addGlowEffect(bullets[i]);
if (shouldEnhanceGlow) {
enhanceGlowEffect(bullets[i]);
}
}
// Add glow to all bombs
for (var i = 0; i < bombs.length; i++) {
addGlowEffect(bombs[i]);
if (shouldEnhanceGlow) {
enhanceGlowEffect(bombs[i]);
}
}
// Add glow to all flares
for (var i = 0; i < flares.length; i++) {
addGlowEffect(flares[i]);
if (shouldEnhanceGlow) {
enhanceGlowEffect(flares[i]);
}
}
// Add glow to all explosions
for (var i = 0; i < explosions.length; i++) {
addGlowEffect(explosions[i]);
if (shouldEnhanceGlow) {
enhanceGlowEffect(explosions[i]);
}
}
// Add glow to all ground targets
for (var i = 0; i < groundTargets.length; i++) {
addGlowEffect(groundTargets[i]);
if (shouldEnhanceGlow) {
enhanceGlowEffect(groundTargets[i]);
}
}
// Add glow to all destruction marks
for (var i = 0; i < destructionMarks.length; i++) {
addGlowEffect(destructionMarks[i]);
if (shouldEnhanceGlow) {
enhanceGlowEffect(destructionMarks[i]);
}
}
// Add glow to fighter jet
addGlowEffect(fighterJet);
if (shouldEnhanceGlow) {
enhanceGlowEffect(fighterJet);
}
// Add glow to fighter jet bomb target circle
if (fighterJet && fighterJet.bombTargetCircle) {
addGlowEffect(fighterJet.bombTargetCircle);
if (shouldEnhanceGlow) {
enhanceGlowEffect(fighterJet.bombTargetCircle);
}
}
} else {
// Reset night glow timer when not in night mode
nightGlowTimer = 0;
// Remove glow from all elements
for (var i = glowingElements.length - 1; i >= 0; i--) {
var element = glowingElements[i];
if (element && !element.destroyed) {
removeGlowEffect(element);
}
glowingElements.splice(i, 1);
}
}
}
function cycleBackground() {
if (!isNightMode) {
// Switch to night (dark desert)
tween(game, {}, {
duration: 1000,
onFinish: function onFinish() {
game.setBackgroundColor(0x2C1810); // Very dark desert color
isNightMode = true;
updateStarColors(); // Update star colors for night
updateNightGlow(); // Apply glow effects
LK.setTimeout(function () {
cycleBackground();
}, 10000); // 10 seconds night
}
});
} else {
// Switch back to day (desert)
tween(game, {}, {
duration: 1000,
onFinish: function onFinish() {
game.setBackgroundColor(0xC2B280); // Desert sandy color
isNightMode = false;
nightsPassed++; // Increment nights passed when day starts
updateStarColors(); // Update star colors for day
updateNightGlow(); // Remove glow effects
// Check if we should trigger nuclear facility after second night
if (nightsPassed >= 2 && !nuclearFacilityReached) {
nuclearFacilityReached = true;
scrollSpeed = 0; // Stop scrolling
// Clear everything from the board before creating nuclear facility
// Clear all missiles
for (var i = missiles.length - 1; i >= 0; i--) {
if (missiles[i]) {
missiles[i].destroy();
missiles.splice(i, 1);
}
}
// Clear all bullets
for (var i = bullets.length - 1; i >= 0; i--) {
if (bullets[i]) {
bullets[i].destroy();
bullets.splice(i, 1);
}
}
// Clear all bombs
for (var i = bombs.length - 1; i >= 0; i--) {
if (bombs[i]) {
bombs[i].destroy();
bombs.splice(i, 1);
}
}
// Clear all flares
for (var i = flares.length - 1; i >= 0; i--) {
if (flares[i]) {
flares[i].destroy();
flares.splice(i, 1);
}
}
// Clear all ground targets
for (var i = groundTargets.length - 1; i >= 0; i--) {
if (groundTargets[i]) {
groundTargets[i].destroy();
groundTargets.splice(i, 1);
}
}
// Clear all explosions
for (var i = explosions.length - 1; i >= 0; i--) {
if (explosions[i]) {
explosions[i].destroy();
explosions.splice(i, 1);
}
}
// Clear all destruction marks
for (var i = destructionMarks.length - 1; i >= 0; i--) {
if (destructionMarks[i]) {
destructionMarks[i].destroy();
destructionMarks.splice(i, 1);
}
}
// Clear all road graphics
for (var i = roadSystem.roadGraphics.length - 1; i >= 0; i--) {
if (roadSystem.roadGraphics[i]) {
roadSystem.roadGraphics[i].destroy();
roadSystem.roadGraphics.splice(i, 1);
}
}
// Clear road system data
roadSystem.roads = [];
createNuclearFacility(); // Create the nuclear facility
} else {
LK.setTimeout(function () {
cycleBackground();
}, 20000); // 20 seconds day
}
}
});
}
}
// One-time verification that stars are single pixel size after 1 second
LK.setTimeout(function () {
for (var i = 0; i < backgroundStars.length; i++) {
var star = backgroundStars[i];
if (!isNightMode) {
// Ensure stars are single pixel size during day
star.children[0].scale.x = 0.1;
star.children[0].scale.y = 0.1;
}
}
}, 1000);
// Start the first cycle after 20 seconds
LK.setTimeout(function () {
cycleBackground();
}, 20000);
// Create fighter jet instance
fighterJet = new FighterJet();
game.addChild(fighterJet);
fighterJet.x = 1024;
fighterJet.y = 2200;
var bullets = [];
var bombs = [];
var flares = [];
var groundTargets = [];
var explosions = [];
var destructionMarks = [];
var spawnTimer = 0;
var gameSpeed = 1;
var roadSystem = {
roads: [],
// Array to track active roads
roadGraphics: [],
// Array to track road graphics for movement
nextRoadTime: 0,
// Time for next road generation
roadHeight: 80,
// Height of road area
generateNewRoad: true // Flag to generate road immediately at start
};
var bombKeyPressed = false;
var flareKeyPressed = false;
var keyStates = {};
var fKeyPressed = false;
var bKeyPressed = false;
var lastMouseX = 0;
var lastMouseY = 0;
// Store bomb target position when bomb is released
var bombTargetX = 0;
var bombTargetY = 0;
var bombCooldown = 0;
var bombingActive = true;
var bombTargetMarked = false;
// UI Elements
var scoreText = new Text2('Score: 0', {
size: 60,
fill: '#ffffff'
});
scoreText.anchor.set(0, 0);
scoreText.x = 150;
scoreText.y = 50;
LK.gui.topLeft.addChild(scoreText);
var livesText = new Text2('Lives: 3', {
size: 60,
fill: '#ffffff'
});
livesText.anchor.set(1, 0);
LK.gui.topRight.addChild(livesText);
var flareText = new Text2('Flares: 3', {
size: 60,
fill: '#ffffff'
});
flareText.anchor.set(0.5, 0);
LK.gui.top.addChild(flareText);
var bombingText = new Text2('Bombing: Active', {
size: 60,
fill: '#ffffff'
});
bombingText.anchor.set(0.5, 0);
bombingText.y = 70;
LK.gui.top.addChild(bombingText);
function updateScore() {
scoreText.setText('Score: ' + LK.getScore());
}
function updateLivesDisplay() {
livesText.setText('Lives: ' + fighterJet.lives);
}
function updateFlareDisplay() {
flareText.setText('Flares: ' + fighterJet.flareCount);
}
function updateBombingDisplay() {
bombingText.setText('Bombing: ' + (bombingActive ? 'Active' : 'Disabled'));
}
function showGameOverWithStats() {
// Stop all game movement by setting scrollSpeed to 0
scrollSpeed = 0;
// Stop background cycling timers - clear specific timers manually
// Note: Since we don't have references to specific timers, we'll rely on the
// game state changes to prevent further timer actions
// Stop all missile movement
for (var i = 0; i < missiles.length; i++) {
if (missiles[i] && missiles[i].update) {
missiles[i].update = function () {}; // Override update to do nothing
}
}
// Stop all bullet movement
for (var i = 0; i < bullets.length; i++) {
if (bullets[i] && bullets[i].update) {
bullets[i].update = function () {}; // Override update to do nothing
}
}
// Stop all bomb movement
for (var i = 0; i < bombs.length; i++) {
if (bombs[i] && bombs[i].update) {
bombs[i].update = function () {}; // Override update to do nothing
}
}
// Stop all ground target movement
for (var i = 0; i < groundTargets.length; i++) {
if (groundTargets[i] && groundTargets[i].update) {
groundTargets[i].update = function () {}; // Override update to do nothing
}
}
// Stop all flare movement
for (var i = 0; i < flares.length; i++) {
if (flares[i] && flares[i].update) {
flares[i].update = function () {}; // Override update to do nothing
}
}
// Stop background star movement
for (var i = 0; i < backgroundStars.length; i++) {
if (backgroundStars[i] && backgroundStars[i].update) {
backgroundStars[i].update = function () {}; // Override update to do nothing
}
}
// Reset to day mode if currently in night mode
if (isNightMode) {
game.setBackgroundColor(0xC2B280); // Desert sandy color
isNightMode = false;
// Update star colors for day mode
for (var i = 0; i < backgroundStars.length; i++) {
var star = backgroundStars[i];
if (star && star.children && star.children[0]) {
star.children[0].tint = star.originalColor;
star.children[0].scale.x = 0.1;
star.children[0].scale.y = 0.1;
}
}
// Remove all glow effects
for (var i = glowingElements.length - 1; i >= 0; i--) {
var element = glowingElements[i];
if (element && !element.destroyed) {
element.isGlowing = false;
tween.stop(element, {
tint: true
});
element.tint = element.originalTint || 0xffffff;
}
glowingElements.splice(i, 1);
}
}
// Create game over statistics screen
var gameOverContainer = new Container();
game.addChild(gameOverContainer);
// Background - Main dark background
var bgRect = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 2.5
});
bgRect.x = 1024;
bgRect.y = 1366;
bgRect.tint = 0x000000;
bgRect.alpha = 0.9;
gameOverContainer.addChild(bgRect);
// Statistics background - Dark background for statistics section
var statsBgRect = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 6.6,
scaleY: 32.4
});
statsBgRect.x = 1024;
statsBgRect.y = 1200;
statsBgRect.tint = 0x000000;
statsBgRect.alpha = 0.85;
gameOverContainer.addChild(statsBgRect);
// Game Over title
var gameOverTitle = new Text2('GAME OVER', {
size: 100,
fill: '#ffffff'
});
gameOverTitle.anchor.set(0.5, 0.5);
gameOverTitle.x = 1024;
gameOverTitle.y = 500;
gameOverContainer.addChild(gameOverTitle);
// Final Score
var finalScoreText = new Text2('Final Score: ' + LK.getScore(), {
size: 80,
fill: '#ffff00'
});
finalScoreText.anchor.set(0.5, 0.5);
finalScoreText.x = 1024;
finalScoreText.y = 600;
gameOverContainer.addChild(finalScoreText);
// Mission briefing data
var missionBriefingTitle = new Text2('MISSION DATA', {
size: 60,
fill: '#ffff00'
});
missionBriefingTitle.anchor.set(0.5, 0.5);
missionBriefingTitle.x = 1024;
missionBriefingTitle.y = 680;
gameOverContainer.addChild(missionBriefingTitle);
var missionDurationText = new Text2('Duration: ' + Math.floor(LK.ticks / 60) + ' seconds', {
size: 50,
fill: '#cccccc'
});
missionDurationText.anchor.set(0.5, 0.5);
missionDurationText.x = 1024;
missionDurationText.y = 720;
gameOverContainer.addChild(missionDurationText);
// Statistics title
var statsTitle = new Text2('MISSION STATISTICS', {
size: 70,
fill: '#ffffff'
});
statsTitle.anchor.set(0.5, 0.5);
statsTitle.x = 1024;
statsTitle.y = 780;
gameOverContainer.addChild(statsTitle);
var statsY = 880;
var statsSpacing = 120;
// Civilian Buildings Destroyed
var civilianBuildingIcon = LK.getAsset('building', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
civilianBuildingIcon.x = 400;
civilianBuildingIcon.y = statsY;
gameOverContainer.addChild(civilianBuildingIcon);
var civilianBuildingStatText = new Text2('Civilian Buildings: ' + gameStats.buildingsDestroyed, {
size: 60,
fill: '#ff6666'
});
civilianBuildingStatText.anchor.set(0, 0.5);
civilianBuildingStatText.x = 500;
civilianBuildingStatText.y = statsY;
gameOverContainer.addChild(civilianBuildingStatText);
// Military Buildings Destroyed
statsY += statsSpacing;
var militaryBuildingIcon = LK.getAsset('targetBuilding', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
militaryBuildingIcon.x = 400;
militaryBuildingIcon.y = statsY;
gameOverContainer.addChild(militaryBuildingIcon);
var militaryBuildingStatText = new Text2('Military Buildings: ' + gameStats.targetBuildingsDestroyed, {
size: 60,
fill: '#66ff66'
});
militaryBuildingStatText.anchor.set(0, 0.5);
militaryBuildingStatText.x = 500;
militaryBuildingStatText.y = statsY;
gameOverContainer.addChild(militaryBuildingStatText);
// S-300 Batteries Destroyed
statsY += statsSpacing;
var batteryIcon = LK.getAsset('s300System', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
batteryIcon.x = 400;
batteryIcon.y = statsY;
gameOverContainer.addChild(batteryIcon);
var batteryStatText = new Text2('S-300 Batteries: ' + gameStats.batteriesDestroyed, {
size: 60,
fill: '#66ff66'
});
batteryStatText.anchor.set(0, 0.5);
batteryStatText.x = 500;
batteryStatText.y = statsY;
gameOverContainer.addChild(batteryStatText);
// Missile Storage Destroyed
statsY += statsSpacing;
var missileStorageIcon = LK.getAsset('missileStorage', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
missileStorageIcon.x = 400;
missileStorageIcon.y = statsY;
gameOverContainer.addChild(missileStorageIcon);
var missileStorageStatText = new Text2('Missile Storage: ' + gameStats.missileStorageDestroyed, {
size: 60,
fill: '#66ff66'
});
missileStorageStatText.anchor.set(0, 0.5);
missileStorageStatText.x = 500;
missileStorageStatText.y = statsY;
gameOverContainer.addChild(missileStorageStatText);
// Vehicles Destroyed
statsY += statsSpacing;
var vehicleIcon = LK.getAsset('car', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
vehicleIcon.x = 400;
vehicleIcon.y = statsY;
gameOverContainer.addChild(vehicleIcon);
var vehicleStatText = new Text2('Vehicles Destroyed: ' + gameStats.vehiclesDestroyed, {
size: 60,
fill: '#ffff66'
});
vehicleStatText.anchor.set(0, 0.5);
vehicleStatText.x = 500;
vehicleStatText.y = statsY;
gameOverContainer.addChild(vehicleStatText);
// Nuclear Facility Hits
statsY += statsSpacing;
var nuclearIcon = LK.getAsset('nuclearFacility', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
nuclearIcon.x = 400;
nuclearIcon.y = statsY;
gameOverContainer.addChild(nuclearIcon);
var nuclearStatText = new Text2('Nuclear Facility Hits: ' + nuclearFacilityHits + '/3', {
size: 60,
fill: '#ffff00'
});
nuclearStatText.anchor.set(0, 0.5);
nuclearStatText.x = 500;
nuclearStatText.y = statsY;
gameOverContainer.addChild(nuclearStatText);
// Missiles Intercepted
statsY += statsSpacing;
var missileIcon = LK.getAsset('missile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
missileIcon.x = 400;
missileIcon.y = statsY;
gameOverContainer.addChild(missileIcon);
var missileStatText = new Text2('Missiles Intercepted: ' + gameStats.missilesIntercepted, {
size: 60,
fill: '#66ffff'
});
missileStatText.anchor.set(0, 0.5);
missileStatText.x = 500;
missileStatText.y = statsY;
gameOverContainer.addChild(missileStatText);
// Continue button
var continueButton = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 1
});
continueButton.x = 1024;
continueButton.y = 2000;
continueButton.tint = 0x27ae60;
gameOverContainer.addChild(continueButton);
var continueButtonText = new Text2('CONTINUE', {
size: 80,
fill: '#ffffff'
});
continueButtonText.anchor.set(0.5, 0.5);
continueButtonText.x = 1024;
continueButtonText.y = 2000;
gameOverContainer.addChild(continueButtonText);
// Handle continue button click
continueButton.down = function () {
gameOverContainer.destroy();
LK.showGameOver();
};
// Auto continue after 10 seconds
LK.setTimeout(function () {
if (!gameOverContainer.destroyed) {
gameOverContainer.destroy();
LK.showGameOver();
}
}, 10000);
}
function generateRoadStructure() {
// Don't generate roads if nuclear facility is reached
if (nuclearFacilityReached) {
return;
}
// Check if we need to create a new road based on time
if (roadSystem.generateNewRoad || LK.ticks >= roadSystem.nextRoadTime) {
// Create new road at top of screen (y = 0)
var roadY = 0;
var road = {
y: roadY,
hasVehicles: true,
// Always have vehicles (minimum 1)
hasBuildings: true,
// Always have buildings (minimum 1)
vehicleCount: Math.floor(Math.random() * 4) + 1,
// 1-4 vehicles
buildingCount: Math.floor(Math.random() * 4) + 2 // 2-5 buildings
};
roadSystem.roads.push(road);
// Draw the actual road
var roadGraphics = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5
});
roadGraphics.x = 1024; // Center of screen
roadGraphics.y = roadY;
roadSystem.roadGraphics.push(roadGraphics);
game.addChild(roadGraphics);
// Spawn vehicles on road (always at least 1)
for (var v = 0; v < road.vehicleCount; v++) {
var vehicleType = Math.random() < 0.6 ? 'car' : 'armyVan';
var vehicle = new GroundTarget(vehicleType);
vehicle.x = Math.random() * 1700 + 174; // Increased travel distance
vehicle.y = roadY;
groundTargets.push(vehicle);
game.addChild(vehicle);
}
// Spawn buildings adjacent to road (above it) - always at least 1
var placedBuildings = []; // Track placed building positions
for (var b = 0; b < road.buildingCount; b++) {
var buildingType;
var rand = Math.random();
if (rand < 0.3) {
buildingType = 'building';
} else if (rand < 0.6) {
buildingType = 'targetBuilding';
} else if (rand < 0.8) {
buildingType = 's300System';
} else {
buildingType = 's300System';
}
// Find valid position with minimum spacing
var attempts = 0;
var validPosition = false;
var buildingX, buildingY;
while (!validPosition && attempts < 20) {
buildingX = Math.random() * 1400 + 324; // Slightly more constrained
buildingY = roadY - 150; // Position above road
validPosition = true;
// Check distance from other buildings
for (var pb = 0; pb < placedBuildings.length; pb++) {
var placedBuilding = placedBuildings[pb];
var dx = buildingX - placedBuilding.x;
var dy = buildingY - placedBuilding.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Minimum distance of 250 pixels between buildings
if (distance < 250) {
validPosition = false;
break;
}
}
attempts++;
}
// Only place building if valid position found
if (validPosition) {
var building = new GroundTarget(buildingType);
building.x = buildingX;
// Raise regular buildings by 25 pixels (original 5 + additional 20)
if (buildingType === 'building') {
building.y = buildingY - 25;
} else if (buildingType === 's300System') {
building.y = buildingY + 5; // Lower military buildings by 5 pixels
} else {
building.y = buildingY;
}
placedBuildings.push({
x: buildingX,
y: buildingY
});
groundTargets.push(building);
game.addChild(building);
}
}
// Calculate next road time: 5.5 seconds average ± 1.5 seconds (330 ± 90 ticks at 60fps)
var baseTime = 330; // 5.5 seconds at 60fps
var variance = 180; // ±3 seconds total range (1.5 second variance)
var nextRoadDelay = baseTime + (Math.random() * variance - variance / 2);
roadSystem.nextRoadTime = LK.ticks + nextRoadDelay;
roadSystem.generateNewRoad = false;
}
}
function spawnAntiAircraftSystems() {
// Don't spawn additional systems if nuclear facility is reached
if (nuclearFacilityReached) {
return;
}
// Count current S-300 systems in the active area (between roads)
var currentS300Count = 0;
for (var i = 0; i < groundTargets.length; i++) {
if (groundTargets[i].targetType === 's300System' && groundTargets[i].y > -500 && groundTargets[i].y < 2000) {
currentS300Count++;
}
}
// Spawn S-300 systems in dead zones between roads with limit of 3
if (currentS300Count < 3 && Math.random() < 0.25) {
// 25% chance per spawn cycle, but only if under limit
var s300 = new GroundTarget('s300System');
s300.x = Math.random() * 1600 + 224;
s300.y = -100 - Math.random() * 200; // In dead zone area
groundTargets.push(s300);
game.addChild(s300);
}
// Spawn missile storage in dead zones between roads (more rare than S-300)
if (Math.random() < 0.08) {
// 8% chance per spawn cycle (more rare than S-300)
var missileStorage = new GroundTarget('missileStorage');
missileStorage.x = Math.random() * 1600 + 224;
missileStorage.y = -100 - Math.random() * 200; // In dead zone area
groundTargets.push(missileStorage);
game.addChild(missileStorage);
}
}
function spawnGroundTarget() {
// This function is now called less frequently and handles structured generation
generateRoadStructure();
spawnAntiAircraftSystems();
}
function createNuclearFacility() {
// Create nuclear facility at center of screen
nuclearFacility = LK.getAsset('nuclearFacility', {
anchorX: 0.5,
anchorY: 0.5
});
nuclearFacility.x = 1024;
nuclearFacility.y = 1366;
nuclearFacility.tint = 0x666666; // Dark gray color
game.addChild(nuclearFacility);
// Create road above the nuclear facility
var roadAbove = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5
});
roadAbove.x = 1024;
roadAbove.y = nuclearFacility.y - 300;
game.addChild(roadAbove);
// Create road below the nuclear facility
var roadBelow = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5
});
roadBelow.x = 1024;
roadBelow.y = nuclearFacility.y + 300;
game.addChild(roadBelow);
// Create 5 S-300 systems symmetrically around the facility
var facilityRadius = 400;
for (var i = 0; i < 5; i++) {
var angle = i / 5 * Math.PI * 2;
var s300 = new GroundTarget('s300System');
s300.x = nuclearFacility.x + Math.cos(angle) * facilityRadius;
s300.y = nuclearFacility.y + Math.sin(angle) * facilityRadius;
s300.canShootOnce = true; // Can only shoot once initially
s300.hasShot = false; // Track if it has shot
facilityS300Systems.push(s300);
groundTargets.push(s300);
game.addChild(s300);
}
// Create 5 vans on the road above the nuclear facility
for (var i = 0; i < 5; i++) {
var van = new GroundTarget('armyVan');
van.x = 200 + i * 400; // Space them across the road
van.y = roadAbove.y;
van.moveDirection = 4; // Move right
facilityTrucks.push(van);
groundTargets.push(van);
game.addChild(van);
}
// Create 5 vans on the road below the nuclear facility
for (var i = 0; i < 5; i++) {
var van = new GroundTarget('armyVan');
van.x = 200 + i * 400; // Space them across the road
van.y = roadBelow.y;
van.moveDirection = -4; // Move left
facilityTrucks.push(van);
groundTargets.push(van);
game.addChild(van);
}
}
function createExplosion(x, y) {
var explosion = new Explosion();
explosion.x = x;
explosion.y = y;
explosions.push(explosion);
game.addChild(explosion);
LK.getSound('explosion').play();
}
function checkMissileFlareCollision(missile) {
for (var f = flares.length - 1; f >= 0; f--) {
var flare = flares[f];
if (missile.intersects(flare)) {
// Flare explodes and destroys missiles in radius
createExplosion(flare.x, flare.y);
// Destroy all missiles within explosion radius
for (var m = missiles.length - 1; m >= 0; m--) {
var targetMissile = missiles[m];
var dx = targetMissile.x - flare.x;
var dy = targetMissile.y - flare.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 150) {
// Explosion radius
createExplosion(targetMissile.x, targetMissile.y);
// Apply missile interception scoring with night mode multiplier
var pointsToAdd = 1; // +1 point for missile destruction by flare
if (isNightMode) {
pointsToAdd *= 2; // Double points during night mode
}
LK.setScore(LK.getScore() + pointsToAdd);
updateScore();
// Track missile interception statistics
gameStats.missilesIntercepted++;
targetMissile.destroy();
missiles.splice(m, 1);
}
}
flare.destroy();
flares.splice(f, 1);
return true;
}
}
return false;
}
// Generate first road at game start
generateRoadStructure();
// Event handlers
game.move = function (x, y, obj) {
fighterJet.x = x;
fighterJet.y = y;
// Improved keyboard input simulation with better detection
var deltaX = Math.abs(x - lastMouseX);
var deltaY = Math.abs(y - lastMouseY);
// F key simulation: horizontal movement with more sensitive detection
if (deltaX > 30 && deltaY < 30) {
fKeyPressed = true;
} else {
fKeyPressed = false;
}
// B key simulation: vertical movement with more sensitive detection
if (deltaY > 30 && deltaX < 30) {
bKeyPressed = true;
} else {
bKeyPressed = false;
}
lastMouseX = x;
lastMouseY = y;
};
game.down = function (x, y, obj) {
// Check for special input zones for keyboard simulation
if (y < 200 && x < 300) {
// Top-left corner click - fire flare (F key simulation)
if (fighterJet.flareCount > 0) {
var flare = new Flare();
flare.x = fighterJet.x + (Math.random() - 0.5) * 100;
flare.y = fighterJet.y + (Math.random() - 0.5) * 100;
flares.push(flare);
game.addChild(flare);
fighterJet.flareCount--;
updateFlareDisplay();
LK.getSound('flare').play();
}
return;
} else if (y < 200 && x > 1748) {
// Top-right corner click - drop bomb (B key simulation)
var bomb = new Bomb();
bomb.x = fighterJet.x;
bomb.y = fighterJet.y + 50;
bombs.push(bomb);
game.addChild(bomb);
LK.getSound('bomb').play();
return;
}
if (obj.event && obj.event.button === 0) {
// Left click - fire bullet
var bullet = new Bullet();
bullet.x = fighterJet.x;
bullet.y = fighterJet.y - 50;
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
} else if (obj.event && obj.event.button === 2) {
// Right click - drop bomb
var bomb = new Bomb();
bomb.x = fighterJet.x;
bomb.y = fighterJet.y + 50;
bombs.push(bomb);
game.addChild(bomb);
LK.getSound('bomb').play();
} else if (!obj.event) {
// Default action for touch/tap (no event object) - fire bullet
var bullet = new Bullet();
bullet.x = fighterJet.x;
bullet.y = fighterJet.y - 50;
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
}
};
game.update = function () {
// Update glow effects for night mode
if (isNightMode) {
updateNightGlow();
}
// Handle keyboard input - using LK tick-based simulation
// Since document is not available, we'll use mouse position changes to detect special actions
var currentFlareKeyPressed = false; // Will be triggered by specific mouse positions
var currentBombKeyPressed = false; // Will be triggered by specific mouse positions
// Fire flare on F key press (only once per press)
if (fKeyPressed && !flareKeyPressed && fighterJet.flareCount > 0) {
var flare = new Flare();
flare.x = fighterJet.x + (Math.random() - 0.5) * 100;
flare.y = fighterJet.y + (Math.random() - 0.5) * 100;
flares.push(flare);
game.addChild(flare);
fighterJet.flareCount--;
updateFlareDisplay();
LK.getSound('flare').play();
}
flareKeyPressed = fKeyPressed;
// Handle bomb cooldown
if (bombCooldown > 0) {
bombCooldown--;
if (bombCooldown === 0) {
bombingActive = true;
updateBombingDisplay();
}
}
// Drop bomb on B key press (only once per press)
if (bKeyPressed && !bombKeyPressed && bombingActive) {
if (!bombTargetMarked) {
// Calculate exact target position based on circle's current position, 55 pixels behind
bombTargetX = fighterJet.x + fighterJet.bombTargetCircle.x;
bombTargetY = fighterJet.y + fighterJet.bombTargetCircle.y + 60;
bombTargetMarked = true;
// Create and drop the bomb
var bomb = new Bomb();
bomb.x = fighterJet.x;
bomb.y = fighterJet.y + 50;
bomb.targetX = bombTargetX;
bomb.targetY = bombTargetY;
// Animate bomb falling to target position
tween(bomb, {
x: bombTargetX,
y: bombTargetY
}, {
duration: 2000,
easing: tween.easeIn,
onFinish: function onFinish() {
// Create big explosion at exact target
createExplosion(bombTargetX, bombTargetY);
// Create persistent destruction mark
var destructionMark = new DestructionMark();
destructionMark.x = bombTargetX;
destructionMark.y = bombTargetY;
destructionMarks.push(destructionMark);
game.addChild(destructionMark);
// Check if bomb hit nuclear facility
if (nuclearFacilityReached && nuclearFacility) {
var facilityDx = nuclearFacility.x - bombTargetX;
var facilityDy = nuclearFacility.y - bombTargetY;
var facilityDistance = Math.sqrt(facilityDx * facilityDx + facilityDy * facilityDy);
if (facilityDistance < 200) {
nuclearFacilityHits++;
// Flash facility to indicate hit
LK.effects.flashObject(nuclearFacility, 0xff0000, 1000);
if (nuclearFacilityHits >= 3) {
// Nuclear facility destroyed - victory!
LK.setScore(LK.getScore() + 50);
updateScore();
LK.showYouWin();
return;
}
}
}
// Destroy ground targets in explosion radius
for (var gt = groundTargets.length - 1; gt >= 0; gt--) {
var target = groundTargets[gt];
var dx = target.x - bombTargetX;
var dy = target.y - bombTargetY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 200) {
// Big explosion radius
createExplosion(target.x, target.y);
// Apply scoring with night mode multiplier
var pointsToAdd = target.points;
if (isNightMode) {
pointsToAdd *= 2; // Double points during night mode
}
LK.setScore(LK.getScore() + pointsToAdd);
updateScore();
// Track statistics for building destruction
if (target.targetType === 'building') {
gameStats.buildingsDestroyed++;
} else if (target.targetType === 'targetBuilding') {
gameStats.targetBuildingsDestroyed++;
} else if (target.targetType === 's300System') {
gameStats.batteriesDestroyed++;
fighterJet.aaSystemsDestroyed++;
if (fighterJet.aaSystemsDestroyed >= 5 && fighterJet.lives < 3) {
fighterJet.lives++;
fighterJet.aaSystemsDestroyed = 0;
updateLivesDisplay();
}
} else if (target.targetType === 'missileStorage') {
gameStats.missileStorageDestroyed++;
}
target.destroy();
groundTargets.splice(gt, 1);
}
}
// Remove and destroy the bomb after explosion
bomb.destroy();
for (var i = bombs.length - 1; i >= 0; i--) {
if (bombs[i] === bomb) {
bombs.splice(i, 1);
break;
}
}
// Start cooldown
bombCooldown = 60; // 1 second at 60fps
bombingActive = false;
bombTargetMarked = false;
updateBombingDisplay();
}
});
bombs.push(bomb);
game.addChild(bomb);
LK.getSound('bomb').play();
}
}
bombKeyPressed = bKeyPressed;
spawnTimer++;
// Generate road structures and spawn targets
if (spawnTimer >= 150) {
// Every 2.5 seconds
spawnGroundTarget();
spawnTimer = 0;
}
// Update road system - move roads with scroll speed and clean up
for (var r = roadSystem.roads.length - 1; r >= 0; r--) {
var road = roadSystem.roads[r];
// Move road down with scroll speed
road.y += scrollSpeed;
// Move corresponding road graphics
if (roadSystem.roadGraphics[r]) {
roadSystem.roadGraphics[r].y += scrollSpeed;
}
// Remove roads that are off screen
if (road.y > 2800) {
// Destroy road graphics
if (roadSystem.roadGraphics[r]) {
roadSystem.roadGraphics[r].destroy();
roadSystem.roadGraphics.splice(r, 1);
}
roadSystem.roads.splice(r, 1);
}
}
// Show bomb target circle when mouse is in bombing position
if (fighterJet.y < 2500) {
fighterJet.bombTargetCircle.visible = true;
} else {
fighterJet.bombTargetCircle.visible = false;
}
// Update bullets
for (var b = bullets.length - 1; b >= 0; b--) {
var bullet = bullets[b];
if (bullet.y < -50) {
bullet.destroy();
bullets.splice(b, 1);
continue;
}
// Check bullet vs ground targets
for (var gt = groundTargets.length - 1; gt >= 0; gt--) {
var target = groundTargets[gt];
if (bullet.intersects(target)) {
// Only allow bullets to destroy certain targets - not S-300, missile buildings, or regular buildings
if (target.targetType === 's300System' || target.targetType === 'targetBuilding' || target.targetType === 'building') {
// Bullets cannot destroy S-300 systems, missile buildings, or regular buildings
bullet.destroy();
bullets.splice(b, 1);
break;
}
createExplosion(target.x, target.y);
// Apply scoring based on target type with night mode multiplier
var pointsToAdd = target.points;
if (isNightMode) {
pointsToAdd *= 2; // Double points during night mode
}
LK.setScore(LK.getScore() + pointsToAdd);
updateScore();
// Track vehicle destruction statistics
if (target.targetType === 'car' || target.targetType === 'armyVan') {
gameStats.vehiclesDestroyed++;
}
bullet.destroy();
bullets.splice(b, 1);
target.destroy();
groundTargets.splice(gt, 1);
break;
}
}
}
// Update bombs - cleanup only
for (var bo = bombs.length - 1; bo >= 0; bo--) {
var bomb = bombs[bo];
if (bomb.destroyed) {
bombs.splice(bo, 1);
}
}
// Update missiles
for (var m = missiles.length - 1; m >= 0; m--) {
var missile = missiles[m];
// Skip if missile doesn't exist (already removed)
if (!missile) {
continue;
}
// Update missile target to current fighter jet position
missile.targetX = fighterJet.x;
missile.targetY = fighterJet.y;
// Check if missile was intercepted by flare
if (checkMissileFlareCollision(missile)) {
// Reset nuclear facility S-300 shooting ability when missile is destroyed
for (var s = 0; s < facilityS300Systems.length; s++) {
var s300 = facilityS300Systems[s];
if (s300 && s300.hasShot) {
s300.canShootOnce = true;
s300.hasShot = false;
}
}
missiles.splice(m, 1);
continue;
}
// Check missile vs fighter jet (only the jet graphics, not the targeting circle)
// Calculate distance to jet center for more precise collision
var dx = missile.x - fighterJet.x;
var dy = missile.y - fighterJet.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Only consider collision if missile is close to the actual jet (not the targeting circle)
if (distance < 60) {
// Jet is 120x80, so radius of ~60 should cover the jet body
createExplosion(fighterJet.x, fighterJet.y);
// Flash screen red when taking damage
LK.effects.flashScreen(0xff0000, 500);
fighterJet.lives--;
updateLivesDisplay();
if (fighterJet.lives <= 0) {
showGameOverWithStats();
return;
}
// Reset nuclear facility S-300 shooting ability when missile hits target
for (var s = 0; s < facilityS300Systems.length; s++) {
var s300 = facilityS300Systems[s];
if (s300 && s300.hasShot) {
s300.canShootOnce = true;
s300.hasShot = false;
}
}
missile.destroy();
missiles.splice(m, 1);
continue;
}
// Remove missiles that go off screen
if (missile.y > 2800 || missile.y < -50 || missile.x < -50 || missile.x > 2100) {
// Reset nuclear facility S-300 shooting ability when missile goes off screen
for (var s = 0; s < facilityS300Systems.length; s++) {
var s300 = facilityS300Systems[s];
if (s300 && s300.hasShot) {
s300.canShootOnce = true;
s300.hasShot = false;
}
}
missile.destroy();
missiles.splice(m, 1);
}
}
// Update ground targets
for (var gt = groundTargets.length - 1; gt >= 0; gt--) {
var target = groundTargets[gt];
if (target.y > 2800) {
target.destroy();
groundTargets.splice(gt, 1);
}
}
// Update flares
for (var f = flares.length - 1; f >= 0; f--) {
var flare = flares[f];
if (flare.destroyed) {
flares.splice(f, 1);
}
}
// Update explosions
for (var e = explosions.length - 1; e >= 0; e--) {
var explosion = explosions[e];
if (explosion.destroyed) {
explosions.splice(e, 1);
}
}
// Update destruction marks
for (var dm = destructionMarks.length - 1; dm >= 0; dm--) {
var destructionMark = destructionMarks[dm];
if (destructionMark.y > 2800) {
destructionMark.destroy();
destructionMarks.splice(dm, 1);
}
}
// Update background stars
for (var bs = backgroundStars.length - 1; bs >= 0; bs--) {
var star = backgroundStars[bs];
if (star.destroyed) {
backgroundStars.splice(bs, 1);
}
}
};
} /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BackgroundStar = Container.expand(function () {
var self = Container.call(this);
var starGraphics = self.attachAsset('backgroundStar', {
anchorX: 0.5,
anchorY: 0.5
});
// Random movement properties
self.speedX = (Math.random() - 0.5) * 2; // -1 to 1
self.speedY = Math.random() * 2 + 1; // 1 to 3
self.originalColor = 0x4a5d4a; // Dark green for day
self.nightColor = 0xffffff; // White for night
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
// Wrap around screen edges
if (self.x < 0) self.x = 2048;
if (self.x > 2048) self.x = 0;
if (self.y > 2732) {
self.y = -10;
// Randomize position when wrapping
self.x = Math.random() * 2048;
}
};
return self;
});
var Bomb = Container.expand(function () {
var self = Container.call(this);
var bombGraphics = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.targetX = 0;
self.targetY = 0;
self.update = function () {
// Basic fallback movement if no tween is active
if (!self.targetX && !self.targetY) {
self.y += self.speed;
}
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -12;
self.lastX = 0;
self.lastY = 0;
self.stuckTimer = 0;
self.update = function () {
self.y += self.speed;
// Track last position to detect if bullet is stuck
var moveThreshold = 2; // Minimum movement required
var dx = Math.abs(self.x - self.lastX);
var dy = Math.abs(self.y - self.lastY);
var totalMovement = dx + dy;
// If bullet hasn't moved enough, increment stuck timer
if (totalMovement < moveThreshold) {
self.stuckTimer++;
} else {
self.stuckTimer = 0; // Reset timer if bullet is moving
}
// If stuck for more than 3 seconds (180 frames at 60fps), mark for destruction
if (self.stuckTimer >= 180) {
self.destroy();
return;
}
// Store current position for next frame comparison
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
var DestructionMark = Container.expand(function () {
var self = Container.call(this);
var markGraphics = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5
});
markGraphics.tint = 0x000000; // Black color
markGraphics.alpha = 0.8;
self.update = function () {
self.y += scrollSpeed; // Move with ground scroll
};
return self;
});
var Explosion = Container.expand(function () {
var self = Container.call(this);
var explosionGraphics = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifespan = 30;
self.update = function () {
self.lifespan--;
self.alpha = self.lifespan / 30;
if (self.lifespan <= 0) {
self.destroy();
}
};
return self;
});
var FighterJet = Container.expand(function () {
var self = Container.call(this);
var jetGraphics = self.attachAsset('fighterJet', {
anchorX: 0.5,
anchorY: 0.5
});
var bombTargetCircle = self.attachAsset('bombTarget', {
anchorX: 0.5,
anchorY: 0.5
});
bombTargetCircle.y = -400; // Position much further in front of the jet
bombTargetCircle.alpha = 0.6;
bombTargetCircle.visible = false;
self.bombTargetCircle = bombTargetCircle;
self.lives = 3;
self.flareCount = 3;
self.maxFlares = 3;
self.flareRechargeTimer = 0;
self.aaSystemsDestroyed = 0;
self.update = function () {
// Recharge flares over time
if (self.flareCount < self.maxFlares) {
self.flareRechargeTimer++;
if (self.flareRechargeTimer >= 150) {
// 2.5 seconds at 60fps
self.flareCount++;
self.flareRechargeTimer = 0;
updateFlareDisplay();
}
}
};
return self;
});
var Flare = Container.expand(function () {
var self = Container.call(this);
var flareGraphics = self.attachAsset('flare', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifespan = 240; // 4 seconds at 60fps
self.update = function () {
self.lifespan--;
// Track nearest missile
var nearestMissile = null;
var nearestDistance = Infinity;
for (var i = 0; i < missiles.length; i++) {
var missile = missiles[i];
var dx = missile.x - self.x;
var dy = missile.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestMissile = missile;
}
}
// Move towards nearest missile if within tracking range
if (nearestMissile && nearestDistance < 300) {
var dx = nearestMissile.x - self.x;
var dy = nearestMissile.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * 8;
self.y += dy / distance * 8;
}
}
// Destroy flare after 4 seconds no matter what
if (self.lifespan <= 0) {
self.destroy();
}
};
return self;
});
var GroundTarget = Container.expand(function (targetType) {
var self = Container.call(this);
var targetGraphics = self.attachAsset(targetType, {
anchorX: 0.5,
anchorY: 0.5
});
self.targetType = targetType;
self.points = getPointsForTarget(targetType);
self.canShootMissiles = targetType === 's300System';
self.missileTimer = 0;
// Set random movement direction for vehicles (only once at creation)
if (targetType === 'car' || targetType === 'armyVan') {
self.moveDirection = Math.random() < 0.5 ? -4 : 4; // Either -4 (left) or 4 (right)
} else {
self.moveDirection = 0;
}
self.update = function () {
self.y += scrollSpeed;
// Movement for cars and army vans in one consistent direction
if (self.targetType === 'car' || self.targetType === 'armyVan') {
// Don't move if moveDirection is 0 (stationary trucks at nuclear facility)
if (self.moveDirection !== 0) {
// Move consistently in the chosen direction
self.x += self.moveDirection;
// Mirror vehicle to face movement direction
if (self.moveDirection > 0) {
// Moving right - no flip needed (default orientation)
targetGraphics.scale.x = Math.abs(targetGraphics.scale.x);
} else {
// Moving left - flip horizontally
targetGraphics.scale.x = -Math.abs(targetGraphics.scale.x);
}
// Keep vehicles within screen bounds and reverse direction if hitting edges
if (self.x < 50) {
self.x = 50;
self.moveDirection = Math.abs(self.moveDirection); // Force positive direction
}
if (self.x > 1998) {
self.x = 1998;
self.moveDirection = -Math.abs(self.moveDirection); // Force negative direction
}
}
}
// S-300 systems shoot missiles at the player (modified for nuclear facility)
if (self.canShootMissiles && self.y > 200 && self.y < 2000) {
// Check if this is a nuclear facility S-300 with shooting restrictions
if (self.canShootOnce !== undefined) {
// Nuclear facility S-300 - can only shoot once until missile hits or is destroyed
if (self.canShootOnce && !self.hasShot) {
self.missileTimer++;
if (self.missileTimer >= 180) {
// Every 3 seconds
self.fireMissile();
self.hasShot = true;
self.canShootOnce = false;
self.missileTimer = 0;
}
}
} else {
// Regular S-300 - shoots normally
self.missileTimer++;
if (self.missileTimer >= 180) {
// Every 3 seconds
self.fireMissile();
self.missileTimer = 0;
}
}
}
};
self.fireMissile = function () {
var missile = new Missile();
missile.x = self.x;
missile.y = self.y;
missile.targetX = fighterJet.x;
missile.targetY = fighterJet.y;
missiles.push(missile);
game.addChild(missile);
LK.getSound('missile').play();
};
return self;
});
var Missile = Container.expand(function () {
var self = Container.call(this);
var missileGraphics = self.attachAsset('missile', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.targetX = 0;
self.targetY = 0;
self.lastX = 0;
self.lastY = 0;
self.stuckTimer = 0;
self.bottomTimer = 0; // Track time spent in bottom area
self.lifeTimer = 540; // 9 seconds at 60fps
self.update = function () {
// Decrease lifespan timer
self.lifeTimer--;
if (self.lifeTimer <= 0) {
self.destroy();
return;
}
// Track last position to detect if missile is stuck
var moveThreshold = 2; // Minimum movement required
var dx = Math.abs(self.x - self.lastX);
var dy = Math.abs(self.y - self.lastY);
var totalMovement = dx + dy;
// If missile hasn't moved enough, increment stuck timer
if (totalMovement < moveThreshold) {
self.stuckTimer++;
} else {
self.stuckTimer = 0; // Reset timer if missile is moving
}
// If stuck for more than 3 seconds (180 frames at 60fps), mark for destruction
if (self.stuckTimer >= 180) {
self.destroy();
return;
}
// Check if missile is in bottom 20% of screen (2732 * 0.8 = 2185.6)
if (self.y > 2185) {
self.bottomTimer++;
} else {
self.bottomTimer = 0; // Reset timer if missile leaves bottom area
}
// If missile has been in bottom area for more than 3 seconds (180 frames at 60fps), mark for destruction
if (self.bottomTimer >= 180) {
self.destroy();
return;
}
// Store current position for next frame comparison
self.lastX = self.x;
self.lastY = self.y;
// Move towards target (fighter jet)
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
var gameStarted = false;
var startScreen = null;
var startButton = null;
var fighterJet = null;
var scrollSpeed = 3;
var missiles = [];
// Game statistics tracking
var gameStats = {
buildingsDestroyed: 0,
targetBuildingsDestroyed: 0,
missilesIntercepted: 0,
batteriesDestroyed: 0,
missileStorageDestroyed: 0,
vehiclesDestroyed: 0
};
// Nuclear facility tracking
var nightsPassed = 0;
var nuclearFacilityReached = false;
var nuclearFacility = null;
var nuclearFacilityHits = 0;
var facilityS300Systems = [];
var facilityTrucks = [];
// Create start screen
startScreen = new Container();
game.addChild(startScreen);
// Add title text
var titleText = new Text2('B-2 Mission', {
size: 120,
fill: '#ffffff'
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
startScreen.addChild(titleText);
// Create start button
startButton = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 1.2
});
startButton.x = 1024;
startButton.y = 700;
startButton.tint = 0x27ae60;
startScreen.addChild(startButton);
// Add button text
var buttonText = new Text2('START', {
size: 120,
fill: '#ffffff'
});
buttonText.anchor.set(0.5, 0.5);
buttonText.x = 1024;
buttonText.y = 700;
startScreen.addChild(buttonText);
// Add mouse illustration
var mouseIllustration = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.15,
scaleY: 0.4
});
mouseIllustration.x = 1024;
mouseIllustration.y = 2000;
mouseIllustration.tint = 0xcccccc;
startScreen.addChild(mouseIllustration);
// Add legend container
var legendContainer = new Container();
startScreen.addChild(legendContainer);
// Legend title
var legendTitle = new Text2('TARGET LEGEND', {
size: 90,
fill: '#ffffff'
});
legendTitle.anchor.set(0.5, 0.5);
legendTitle.x = 1024;
legendTitle.y = 850;
startScreen.addChild(legendTitle);
// Create legend entries with visual representations
var legendY = 950;
var legendSpacing = 140;
// Buildings (Civilian) - Bombs only
var civilianBuildingGraphic = LK.getAsset('building', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
civilianBuildingGraphic.x = 300;
civilianBuildingGraphic.y = legendY;
startScreen.addChild(civilianBuildingGraphic);
var civilianBuildingText = new Text2('Civilian Building: -10 pts (Bombs Only)', {
size: 60,
fill: '#ff6666'
});
civilianBuildingText.anchor.set(0, 0.5);
civilianBuildingText.x = 450;
civilianBuildingText.y = legendY;
startScreen.addChild(civilianBuildingText);
// Target Buildings (Military) - Bombs only
legendY += legendSpacing;
var targetBuildingGraphic = LK.getAsset('targetBuilding', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
targetBuildingGraphic.x = 300;
targetBuildingGraphic.y = legendY;
startScreen.addChild(targetBuildingGraphic);
var targetBuildingText = new Text2('Military Building: +10 pts (Bombs Only)', {
size: 60,
fill: '#66ff66'
});
targetBuildingText.anchor.set(0, 0.5);
targetBuildingText.x = 450;
targetBuildingText.y = legendY;
startScreen.addChild(targetBuildingText);
// S-300 Systems - Bombs only
legendY += legendSpacing;
var s300Graphic = LK.getAsset('s300System', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
s300Graphic.x = 300;
s300Graphic.y = legendY;
startScreen.addChild(s300Graphic);
var s300Text = new Text2('S-300 System: +15 pts (Bombs Only)', {
size: 60,
fill: '#66ff66'
});
s300Text.anchor.set(0, 0.5);
s300Text.x = 450;
s300Text.y = legendY;
startScreen.addChild(s300Text);
// Missile Storage - Bombs only
legendY += legendSpacing;
var missileStorageGraphic = LK.getAsset('missileStorage', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
missileStorageGraphic.x = 300;
missileStorageGraphic.y = legendY;
startScreen.addChild(missileStorageGraphic);
var missileStorageText = new Text2('Missile Storage: +10 pts (Bombs Only)', {
size: 60,
fill: '#66ff66'
});
missileStorageText.anchor.set(0, 0.5);
missileStorageText.x = 450;
missileStorageText.y = legendY;
startScreen.addChild(missileStorageText);
// Civilian Cars - Cannon only
legendY += legendSpacing;
var carGraphic = LK.getAsset('car', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
carGraphic.x = 300;
carGraphic.y = legendY;
startScreen.addChild(carGraphic);
var carText = new Text2('Civilian Car: -5 pts (Cannon & Bombs)', {
size: 60,
fill: '#ff6666'
});
carText.anchor.set(0, 0.5);
carText.x = 450;
carText.y = legendY;
startScreen.addChild(carText);
// Army Vans - Cannon only
legendY += legendSpacing;
var armyVanGraphic = LK.getAsset('armyVan', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
armyVanGraphic.x = 300;
armyVanGraphic.y = legendY;
startScreen.addChild(armyVanGraphic);
var armyVanText = new Text2('Army Van: +5 pts (Cannon & Bombs)', {
size: 60,
fill: '#66ff66'
});
armyVanText.anchor.set(0, 0.5);
armyVanText.x = 450;
armyVanText.y = legendY;
startScreen.addChild(armyVanText);
// Nuclear Facility - Bombs only, requires 3 hits
legendY += legendSpacing;
var nuclearFacilityGraphic = LK.getAsset('nuclearFacility', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
nuclearFacilityGraphic.x = 300;
nuclearFacilityGraphic.y = legendY;
startScreen.addChild(nuclearFacilityGraphic);
var nuclearFacilityText = new Text2('Nuclear Facility: +50 pts (3 Bombs Required)', {
size: 60,
fill: '#ffff00'
});
nuclearFacilityText.anchor.set(0, 0.5);
nuclearFacilityText.x = 450;
nuclearFacilityText.y = legendY;
startScreen.addChild(nuclearFacilityText);
// Add weapon demonstrations
var weaponY = 2300;
var bombGraphic = LK.getAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.5
});
bombGraphic.x = 350;
bombGraphic.y = weaponY;
startScreen.addChild(bombGraphic);
var bombWeaponText = new Text2('BOMB - Move mouse forward - Destroys Buildings & S-300', {
size: 65,
fill: '#ffff00'
});
bombWeaponText.anchor.set(0, 0.5);
bombWeaponText.x = 450;
bombWeaponText.y = weaponY;
startScreen.addChild(bombWeaponText);
weaponY += 120;
var bulletGraphic = LK.getAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3
});
bulletGraphic.x = 350;
bulletGraphic.y = weaponY;
startScreen.addChild(bulletGraphic);
var bulletWeaponText = new Text2('CANNON - Click mouse - Destroys Vehicles Only', {
size: 65,
fill: '#ffff00'
});
bulletWeaponText.anchor.set(0, 0.5);
bulletWeaponText.x = 450;
bulletWeaponText.y = weaponY;
startScreen.addChild(bulletWeaponText);
weaponY += 120;
var flareGraphic = LK.getAsset('flare', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3
});
flareGraphic.x = 350;
flareGraphic.y = weaponY;
startScreen.addChild(flareGraphic);
var flareWeaponText = new Text2('FLARES - Shake the mouse left and right to use', {
size: 65,
fill: '#ffff00'
});
flareWeaponText.anchor.set(0, 0.5);
flareWeaponText.x = 450;
flareWeaponText.y = weaponY;
startScreen.addChild(flareWeaponText);
// Handle start button click
startButton.down = function (x, y, obj) {
if (gameStarted) return;
gameStarted = true;
startScreen.destroy();
// Initialize game after start button is clicked
initializeGame();
};
function getPointsForTarget(targetType) {
switch (targetType) {
case 'building':
return -10;
// Civilian building penalty
case 'targetBuilding':
return 10;
// Military building points
case 'car':
return -5;
// Regular truck penalty
case 'armyVan':
return 5;
// Military truck points
case 's300System':
return 15;
// S-300 system points
case 'missileStorage':
return 10;
// Missile storage points
default:
return 10;
}
}
function updateFlareDisplay() {
if (fighterJet && fighterJet.flareCount !== undefined) {
var flareText = LK.gui.top.children.find(function (child) {
return child.text && child.text.includes('Flares:');
});
if (flareText) {
flareText.setText('Flares: ' + fighterJet.flareCount);
}
}
}
function initializeGame() {
// Set initial desert background color
game.setBackgroundColor(0xC2B280); // Desert sandy color
// Start background color cycling after game begins
var backgroundTimer = 0;
var isNightMode = false;
var glowingElements = []; // Track all elements that should glow
var backgroundStars = []; // Track background stars
// Create initial background stars
for (var i = 0; i < 150; i++) {
var star = new BackgroundStar();
star.x = Math.random() * 2048;
star.y = Math.random() * 2732;
// Initialize stars to single pixel size at game start
star.children[0].scale.x = 0.1;
star.children[0].scale.y = 0.1;
backgroundStars.push(star);
game.addChild(star);
}
function updateStarColors() {
for (var i = 0; i < backgroundStars.length; i++) {
var star = backgroundStars[i];
if (isNightMode) {
star.children[0].tint = star.nightColor;
// Variable sizes during night - can be up to 2x normal size
var nightScale = 1 + Math.random() * 1.0; // 1 to 2 times normal size
star.children[0].scale.x = nightScale;
star.children[0].scale.y = nightScale;
} else {
star.children[0].tint = star.originalColor;
// Single pixel size during day (1/10th of original 10px size)
star.children[0].scale.x = 0.1;
star.children[0].scale.y = 0.1;
}
}
}
function addGlowEffect(element) {
if (element && !element.isGlowing) {
element.isGlowing = true;
element.originalTint = element.tint || 0xffffff;
glowingElements.push(element);
// Enhanced glow effect with multiple colors
var glowColors = [0x00ffff, 0xffff00, 0xff00ff, 0x00ff00];
var randomColor = glowColors[Math.floor(Math.random() * glowColors.length)];
// Create pulsing glow effect with random color
tween(element, {
tint: randomColor
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (element.isGlowing) {
tween(element, {
tint: 0xffffff
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (element.isGlowing) {
addGlowEffect(element); // Continue glow cycle
}
}
});
}
}
});
}
}
function removeGlowEffect(element) {
if (element && element.isGlowing) {
element.isGlowing = false;
tween.stop(element, {
tint: true
});
element.tint = element.originalTint || 0xffffff;
}
}
var nightGlowTimer = 0;
function enhanceGlowEffect(element) {
if (element && element.isGlowing) {
// Stop any existing tween
tween.stop(element, {
tint: true
});
// Create intense glow burst effect
var intenseBrightColors = [0x00ffff, 0xffff00, 0xff00ff, 0x00ff00, 0xff8800, 0x8800ff];
var randomColor = intenseBrightColors[Math.floor(Math.random() * intenseBrightColors.length)];
// Enhanced bright glow effect
tween(element, {
tint: randomColor
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (element.isGlowing) {
tween(element, {
tint: 0xffffff
}, {
duration: 300,
easing: tween.easeIn
});
}
}
});
}
}
function updateNightGlow() {
if (isNightMode) {
// Increment night glow timer
nightGlowTimer++;
// Every 1.5 seconds (90 frames at 60fps), enhance the glow
var shouldEnhanceGlow = nightGlowTimer % 90 === 0;
// Add glow to all missiles
for (var i = 0; i < missiles.length; i++) {
addGlowEffect(missiles[i]);
if (shouldEnhanceGlow) {
enhanceGlowEffect(missiles[i]);
}
}
// Add glow to all bullets
for (var i = 0; i < bullets.length; i++) {
addGlowEffect(bullets[i]);
if (shouldEnhanceGlow) {
enhanceGlowEffect(bullets[i]);
}
}
// Add glow to all bombs
for (var i = 0; i < bombs.length; i++) {
addGlowEffect(bombs[i]);
if (shouldEnhanceGlow) {
enhanceGlowEffect(bombs[i]);
}
}
// Add glow to all flares
for (var i = 0; i < flares.length; i++) {
addGlowEffect(flares[i]);
if (shouldEnhanceGlow) {
enhanceGlowEffect(flares[i]);
}
}
// Add glow to all explosions
for (var i = 0; i < explosions.length; i++) {
addGlowEffect(explosions[i]);
if (shouldEnhanceGlow) {
enhanceGlowEffect(explosions[i]);
}
}
// Add glow to all ground targets
for (var i = 0; i < groundTargets.length; i++) {
addGlowEffect(groundTargets[i]);
if (shouldEnhanceGlow) {
enhanceGlowEffect(groundTargets[i]);
}
}
// Add glow to all destruction marks
for (var i = 0; i < destructionMarks.length; i++) {
addGlowEffect(destructionMarks[i]);
if (shouldEnhanceGlow) {
enhanceGlowEffect(destructionMarks[i]);
}
}
// Add glow to fighter jet
addGlowEffect(fighterJet);
if (shouldEnhanceGlow) {
enhanceGlowEffect(fighterJet);
}
// Add glow to fighter jet bomb target circle
if (fighterJet && fighterJet.bombTargetCircle) {
addGlowEffect(fighterJet.bombTargetCircle);
if (shouldEnhanceGlow) {
enhanceGlowEffect(fighterJet.bombTargetCircle);
}
}
} else {
// Reset night glow timer when not in night mode
nightGlowTimer = 0;
// Remove glow from all elements
for (var i = glowingElements.length - 1; i >= 0; i--) {
var element = glowingElements[i];
if (element && !element.destroyed) {
removeGlowEffect(element);
}
glowingElements.splice(i, 1);
}
}
}
function cycleBackground() {
if (!isNightMode) {
// Switch to night (dark desert)
tween(game, {}, {
duration: 1000,
onFinish: function onFinish() {
game.setBackgroundColor(0x2C1810); // Very dark desert color
isNightMode = true;
updateStarColors(); // Update star colors for night
updateNightGlow(); // Apply glow effects
LK.setTimeout(function () {
cycleBackground();
}, 10000); // 10 seconds night
}
});
} else {
// Switch back to day (desert)
tween(game, {}, {
duration: 1000,
onFinish: function onFinish() {
game.setBackgroundColor(0xC2B280); // Desert sandy color
isNightMode = false;
nightsPassed++; // Increment nights passed when day starts
updateStarColors(); // Update star colors for day
updateNightGlow(); // Remove glow effects
// Check if we should trigger nuclear facility after second night
if (nightsPassed >= 2 && !nuclearFacilityReached) {
nuclearFacilityReached = true;
scrollSpeed = 0; // Stop scrolling
// Clear everything from the board before creating nuclear facility
// Clear all missiles
for (var i = missiles.length - 1; i >= 0; i--) {
if (missiles[i]) {
missiles[i].destroy();
missiles.splice(i, 1);
}
}
// Clear all bullets
for (var i = bullets.length - 1; i >= 0; i--) {
if (bullets[i]) {
bullets[i].destroy();
bullets.splice(i, 1);
}
}
// Clear all bombs
for (var i = bombs.length - 1; i >= 0; i--) {
if (bombs[i]) {
bombs[i].destroy();
bombs.splice(i, 1);
}
}
// Clear all flares
for (var i = flares.length - 1; i >= 0; i--) {
if (flares[i]) {
flares[i].destroy();
flares.splice(i, 1);
}
}
// Clear all ground targets
for (var i = groundTargets.length - 1; i >= 0; i--) {
if (groundTargets[i]) {
groundTargets[i].destroy();
groundTargets.splice(i, 1);
}
}
// Clear all explosions
for (var i = explosions.length - 1; i >= 0; i--) {
if (explosions[i]) {
explosions[i].destroy();
explosions.splice(i, 1);
}
}
// Clear all destruction marks
for (var i = destructionMarks.length - 1; i >= 0; i--) {
if (destructionMarks[i]) {
destructionMarks[i].destroy();
destructionMarks.splice(i, 1);
}
}
// Clear all road graphics
for (var i = roadSystem.roadGraphics.length - 1; i >= 0; i--) {
if (roadSystem.roadGraphics[i]) {
roadSystem.roadGraphics[i].destroy();
roadSystem.roadGraphics.splice(i, 1);
}
}
// Clear road system data
roadSystem.roads = [];
createNuclearFacility(); // Create the nuclear facility
} else {
LK.setTimeout(function () {
cycleBackground();
}, 20000); // 20 seconds day
}
}
});
}
}
// One-time verification that stars are single pixel size after 1 second
LK.setTimeout(function () {
for (var i = 0; i < backgroundStars.length; i++) {
var star = backgroundStars[i];
if (!isNightMode) {
// Ensure stars are single pixel size during day
star.children[0].scale.x = 0.1;
star.children[0].scale.y = 0.1;
}
}
}, 1000);
// Start the first cycle after 20 seconds
LK.setTimeout(function () {
cycleBackground();
}, 20000);
// Create fighter jet instance
fighterJet = new FighterJet();
game.addChild(fighterJet);
fighterJet.x = 1024;
fighterJet.y = 2200;
var bullets = [];
var bombs = [];
var flares = [];
var groundTargets = [];
var explosions = [];
var destructionMarks = [];
var spawnTimer = 0;
var gameSpeed = 1;
var roadSystem = {
roads: [],
// Array to track active roads
roadGraphics: [],
// Array to track road graphics for movement
nextRoadTime: 0,
// Time for next road generation
roadHeight: 80,
// Height of road area
generateNewRoad: true // Flag to generate road immediately at start
};
var bombKeyPressed = false;
var flareKeyPressed = false;
var keyStates = {};
var fKeyPressed = false;
var bKeyPressed = false;
var lastMouseX = 0;
var lastMouseY = 0;
// Store bomb target position when bomb is released
var bombTargetX = 0;
var bombTargetY = 0;
var bombCooldown = 0;
var bombingActive = true;
var bombTargetMarked = false;
// UI Elements
var scoreText = new Text2('Score: 0', {
size: 60,
fill: '#ffffff'
});
scoreText.anchor.set(0, 0);
scoreText.x = 150;
scoreText.y = 50;
LK.gui.topLeft.addChild(scoreText);
var livesText = new Text2('Lives: 3', {
size: 60,
fill: '#ffffff'
});
livesText.anchor.set(1, 0);
LK.gui.topRight.addChild(livesText);
var flareText = new Text2('Flares: 3', {
size: 60,
fill: '#ffffff'
});
flareText.anchor.set(0.5, 0);
LK.gui.top.addChild(flareText);
var bombingText = new Text2('Bombing: Active', {
size: 60,
fill: '#ffffff'
});
bombingText.anchor.set(0.5, 0);
bombingText.y = 70;
LK.gui.top.addChild(bombingText);
function updateScore() {
scoreText.setText('Score: ' + LK.getScore());
}
function updateLivesDisplay() {
livesText.setText('Lives: ' + fighterJet.lives);
}
function updateFlareDisplay() {
flareText.setText('Flares: ' + fighterJet.flareCount);
}
function updateBombingDisplay() {
bombingText.setText('Bombing: ' + (bombingActive ? 'Active' : 'Disabled'));
}
function showGameOverWithStats() {
// Stop all game movement by setting scrollSpeed to 0
scrollSpeed = 0;
// Stop background cycling timers - clear specific timers manually
// Note: Since we don't have references to specific timers, we'll rely on the
// game state changes to prevent further timer actions
// Stop all missile movement
for (var i = 0; i < missiles.length; i++) {
if (missiles[i] && missiles[i].update) {
missiles[i].update = function () {}; // Override update to do nothing
}
}
// Stop all bullet movement
for (var i = 0; i < bullets.length; i++) {
if (bullets[i] && bullets[i].update) {
bullets[i].update = function () {}; // Override update to do nothing
}
}
// Stop all bomb movement
for (var i = 0; i < bombs.length; i++) {
if (bombs[i] && bombs[i].update) {
bombs[i].update = function () {}; // Override update to do nothing
}
}
// Stop all ground target movement
for (var i = 0; i < groundTargets.length; i++) {
if (groundTargets[i] && groundTargets[i].update) {
groundTargets[i].update = function () {}; // Override update to do nothing
}
}
// Stop all flare movement
for (var i = 0; i < flares.length; i++) {
if (flares[i] && flares[i].update) {
flares[i].update = function () {}; // Override update to do nothing
}
}
// Stop background star movement
for (var i = 0; i < backgroundStars.length; i++) {
if (backgroundStars[i] && backgroundStars[i].update) {
backgroundStars[i].update = function () {}; // Override update to do nothing
}
}
// Reset to day mode if currently in night mode
if (isNightMode) {
game.setBackgroundColor(0xC2B280); // Desert sandy color
isNightMode = false;
// Update star colors for day mode
for (var i = 0; i < backgroundStars.length; i++) {
var star = backgroundStars[i];
if (star && star.children && star.children[0]) {
star.children[0].tint = star.originalColor;
star.children[0].scale.x = 0.1;
star.children[0].scale.y = 0.1;
}
}
// Remove all glow effects
for (var i = glowingElements.length - 1; i >= 0; i--) {
var element = glowingElements[i];
if (element && !element.destroyed) {
element.isGlowing = false;
tween.stop(element, {
tint: true
});
element.tint = element.originalTint || 0xffffff;
}
glowingElements.splice(i, 1);
}
}
// Create game over statistics screen
var gameOverContainer = new Container();
game.addChild(gameOverContainer);
// Background - Main dark background
var bgRect = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 2.5
});
bgRect.x = 1024;
bgRect.y = 1366;
bgRect.tint = 0x000000;
bgRect.alpha = 0.9;
gameOverContainer.addChild(bgRect);
// Statistics background - Dark background for statistics section
var statsBgRect = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 6.6,
scaleY: 32.4
});
statsBgRect.x = 1024;
statsBgRect.y = 1200;
statsBgRect.tint = 0x000000;
statsBgRect.alpha = 0.85;
gameOverContainer.addChild(statsBgRect);
// Game Over title
var gameOverTitle = new Text2('GAME OVER', {
size: 100,
fill: '#ffffff'
});
gameOverTitle.anchor.set(0.5, 0.5);
gameOverTitle.x = 1024;
gameOverTitle.y = 500;
gameOverContainer.addChild(gameOverTitle);
// Final Score
var finalScoreText = new Text2('Final Score: ' + LK.getScore(), {
size: 80,
fill: '#ffff00'
});
finalScoreText.anchor.set(0.5, 0.5);
finalScoreText.x = 1024;
finalScoreText.y = 600;
gameOverContainer.addChild(finalScoreText);
// Mission briefing data
var missionBriefingTitle = new Text2('MISSION DATA', {
size: 60,
fill: '#ffff00'
});
missionBriefingTitle.anchor.set(0.5, 0.5);
missionBriefingTitle.x = 1024;
missionBriefingTitle.y = 680;
gameOverContainer.addChild(missionBriefingTitle);
var missionDurationText = new Text2('Duration: ' + Math.floor(LK.ticks / 60) + ' seconds', {
size: 50,
fill: '#cccccc'
});
missionDurationText.anchor.set(0.5, 0.5);
missionDurationText.x = 1024;
missionDurationText.y = 720;
gameOverContainer.addChild(missionDurationText);
// Statistics title
var statsTitle = new Text2('MISSION STATISTICS', {
size: 70,
fill: '#ffffff'
});
statsTitle.anchor.set(0.5, 0.5);
statsTitle.x = 1024;
statsTitle.y = 780;
gameOverContainer.addChild(statsTitle);
var statsY = 880;
var statsSpacing = 120;
// Civilian Buildings Destroyed
var civilianBuildingIcon = LK.getAsset('building', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
civilianBuildingIcon.x = 400;
civilianBuildingIcon.y = statsY;
gameOverContainer.addChild(civilianBuildingIcon);
var civilianBuildingStatText = new Text2('Civilian Buildings: ' + gameStats.buildingsDestroyed, {
size: 60,
fill: '#ff6666'
});
civilianBuildingStatText.anchor.set(0, 0.5);
civilianBuildingStatText.x = 500;
civilianBuildingStatText.y = statsY;
gameOverContainer.addChild(civilianBuildingStatText);
// Military Buildings Destroyed
statsY += statsSpacing;
var militaryBuildingIcon = LK.getAsset('targetBuilding', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
militaryBuildingIcon.x = 400;
militaryBuildingIcon.y = statsY;
gameOverContainer.addChild(militaryBuildingIcon);
var militaryBuildingStatText = new Text2('Military Buildings: ' + gameStats.targetBuildingsDestroyed, {
size: 60,
fill: '#66ff66'
});
militaryBuildingStatText.anchor.set(0, 0.5);
militaryBuildingStatText.x = 500;
militaryBuildingStatText.y = statsY;
gameOverContainer.addChild(militaryBuildingStatText);
// S-300 Batteries Destroyed
statsY += statsSpacing;
var batteryIcon = LK.getAsset('s300System', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
batteryIcon.x = 400;
batteryIcon.y = statsY;
gameOverContainer.addChild(batteryIcon);
var batteryStatText = new Text2('S-300 Batteries: ' + gameStats.batteriesDestroyed, {
size: 60,
fill: '#66ff66'
});
batteryStatText.anchor.set(0, 0.5);
batteryStatText.x = 500;
batteryStatText.y = statsY;
gameOverContainer.addChild(batteryStatText);
// Missile Storage Destroyed
statsY += statsSpacing;
var missileStorageIcon = LK.getAsset('missileStorage', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
missileStorageIcon.x = 400;
missileStorageIcon.y = statsY;
gameOverContainer.addChild(missileStorageIcon);
var missileStorageStatText = new Text2('Missile Storage: ' + gameStats.missileStorageDestroyed, {
size: 60,
fill: '#66ff66'
});
missileStorageStatText.anchor.set(0, 0.5);
missileStorageStatText.x = 500;
missileStorageStatText.y = statsY;
gameOverContainer.addChild(missileStorageStatText);
// Vehicles Destroyed
statsY += statsSpacing;
var vehicleIcon = LK.getAsset('car', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
vehicleIcon.x = 400;
vehicleIcon.y = statsY;
gameOverContainer.addChild(vehicleIcon);
var vehicleStatText = new Text2('Vehicles Destroyed: ' + gameStats.vehiclesDestroyed, {
size: 60,
fill: '#ffff66'
});
vehicleStatText.anchor.set(0, 0.5);
vehicleStatText.x = 500;
vehicleStatText.y = statsY;
gameOverContainer.addChild(vehicleStatText);
// Nuclear Facility Hits
statsY += statsSpacing;
var nuclearIcon = LK.getAsset('nuclearFacility', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
nuclearIcon.x = 400;
nuclearIcon.y = statsY;
gameOverContainer.addChild(nuclearIcon);
var nuclearStatText = new Text2('Nuclear Facility Hits: ' + nuclearFacilityHits + '/3', {
size: 60,
fill: '#ffff00'
});
nuclearStatText.anchor.set(0, 0.5);
nuclearStatText.x = 500;
nuclearStatText.y = statsY;
gameOverContainer.addChild(nuclearStatText);
// Missiles Intercepted
statsY += statsSpacing;
var missileIcon = LK.getAsset('missile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
missileIcon.x = 400;
missileIcon.y = statsY;
gameOverContainer.addChild(missileIcon);
var missileStatText = new Text2('Missiles Intercepted: ' + gameStats.missilesIntercepted, {
size: 60,
fill: '#66ffff'
});
missileStatText.anchor.set(0, 0.5);
missileStatText.x = 500;
missileStatText.y = statsY;
gameOverContainer.addChild(missileStatText);
// Continue button
var continueButton = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 1
});
continueButton.x = 1024;
continueButton.y = 2000;
continueButton.tint = 0x27ae60;
gameOverContainer.addChild(continueButton);
var continueButtonText = new Text2('CONTINUE', {
size: 80,
fill: '#ffffff'
});
continueButtonText.anchor.set(0.5, 0.5);
continueButtonText.x = 1024;
continueButtonText.y = 2000;
gameOverContainer.addChild(continueButtonText);
// Handle continue button click
continueButton.down = function () {
gameOverContainer.destroy();
LK.showGameOver();
};
// Auto continue after 10 seconds
LK.setTimeout(function () {
if (!gameOverContainer.destroyed) {
gameOverContainer.destroy();
LK.showGameOver();
}
}, 10000);
}
function generateRoadStructure() {
// Don't generate roads if nuclear facility is reached
if (nuclearFacilityReached) {
return;
}
// Check if we need to create a new road based on time
if (roadSystem.generateNewRoad || LK.ticks >= roadSystem.nextRoadTime) {
// Create new road at top of screen (y = 0)
var roadY = 0;
var road = {
y: roadY,
hasVehicles: true,
// Always have vehicles (minimum 1)
hasBuildings: true,
// Always have buildings (minimum 1)
vehicleCount: Math.floor(Math.random() * 4) + 1,
// 1-4 vehicles
buildingCount: Math.floor(Math.random() * 4) + 2 // 2-5 buildings
};
roadSystem.roads.push(road);
// Draw the actual road
var roadGraphics = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5
});
roadGraphics.x = 1024; // Center of screen
roadGraphics.y = roadY;
roadSystem.roadGraphics.push(roadGraphics);
game.addChild(roadGraphics);
// Spawn vehicles on road (always at least 1)
for (var v = 0; v < road.vehicleCount; v++) {
var vehicleType = Math.random() < 0.6 ? 'car' : 'armyVan';
var vehicle = new GroundTarget(vehicleType);
vehicle.x = Math.random() * 1700 + 174; // Increased travel distance
vehicle.y = roadY;
groundTargets.push(vehicle);
game.addChild(vehicle);
}
// Spawn buildings adjacent to road (above it) - always at least 1
var placedBuildings = []; // Track placed building positions
for (var b = 0; b < road.buildingCount; b++) {
var buildingType;
var rand = Math.random();
if (rand < 0.3) {
buildingType = 'building';
} else if (rand < 0.6) {
buildingType = 'targetBuilding';
} else if (rand < 0.8) {
buildingType = 's300System';
} else {
buildingType = 's300System';
}
// Find valid position with minimum spacing
var attempts = 0;
var validPosition = false;
var buildingX, buildingY;
while (!validPosition && attempts < 20) {
buildingX = Math.random() * 1400 + 324; // Slightly more constrained
buildingY = roadY - 150; // Position above road
validPosition = true;
// Check distance from other buildings
for (var pb = 0; pb < placedBuildings.length; pb++) {
var placedBuilding = placedBuildings[pb];
var dx = buildingX - placedBuilding.x;
var dy = buildingY - placedBuilding.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Minimum distance of 250 pixels between buildings
if (distance < 250) {
validPosition = false;
break;
}
}
attempts++;
}
// Only place building if valid position found
if (validPosition) {
var building = new GroundTarget(buildingType);
building.x = buildingX;
// Raise regular buildings by 25 pixels (original 5 + additional 20)
if (buildingType === 'building') {
building.y = buildingY - 25;
} else if (buildingType === 's300System') {
building.y = buildingY + 5; // Lower military buildings by 5 pixels
} else {
building.y = buildingY;
}
placedBuildings.push({
x: buildingX,
y: buildingY
});
groundTargets.push(building);
game.addChild(building);
}
}
// Calculate next road time: 5.5 seconds average ± 1.5 seconds (330 ± 90 ticks at 60fps)
var baseTime = 330; // 5.5 seconds at 60fps
var variance = 180; // ±3 seconds total range (1.5 second variance)
var nextRoadDelay = baseTime + (Math.random() * variance - variance / 2);
roadSystem.nextRoadTime = LK.ticks + nextRoadDelay;
roadSystem.generateNewRoad = false;
}
}
function spawnAntiAircraftSystems() {
// Don't spawn additional systems if nuclear facility is reached
if (nuclearFacilityReached) {
return;
}
// Count current S-300 systems in the active area (between roads)
var currentS300Count = 0;
for (var i = 0; i < groundTargets.length; i++) {
if (groundTargets[i].targetType === 's300System' && groundTargets[i].y > -500 && groundTargets[i].y < 2000) {
currentS300Count++;
}
}
// Spawn S-300 systems in dead zones between roads with limit of 3
if (currentS300Count < 3 && Math.random() < 0.25) {
// 25% chance per spawn cycle, but only if under limit
var s300 = new GroundTarget('s300System');
s300.x = Math.random() * 1600 + 224;
s300.y = -100 - Math.random() * 200; // In dead zone area
groundTargets.push(s300);
game.addChild(s300);
}
// Spawn missile storage in dead zones between roads (more rare than S-300)
if (Math.random() < 0.08) {
// 8% chance per spawn cycle (more rare than S-300)
var missileStorage = new GroundTarget('missileStorage');
missileStorage.x = Math.random() * 1600 + 224;
missileStorage.y = -100 - Math.random() * 200; // In dead zone area
groundTargets.push(missileStorage);
game.addChild(missileStorage);
}
}
function spawnGroundTarget() {
// This function is now called less frequently and handles structured generation
generateRoadStructure();
spawnAntiAircraftSystems();
}
function createNuclearFacility() {
// Create nuclear facility at center of screen
nuclearFacility = LK.getAsset('nuclearFacility', {
anchorX: 0.5,
anchorY: 0.5
});
nuclearFacility.x = 1024;
nuclearFacility.y = 1366;
nuclearFacility.tint = 0x666666; // Dark gray color
game.addChild(nuclearFacility);
// Create road above the nuclear facility
var roadAbove = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5
});
roadAbove.x = 1024;
roadAbove.y = nuclearFacility.y - 300;
game.addChild(roadAbove);
// Create road below the nuclear facility
var roadBelow = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5
});
roadBelow.x = 1024;
roadBelow.y = nuclearFacility.y + 300;
game.addChild(roadBelow);
// Create 5 S-300 systems symmetrically around the facility
var facilityRadius = 400;
for (var i = 0; i < 5; i++) {
var angle = i / 5 * Math.PI * 2;
var s300 = new GroundTarget('s300System');
s300.x = nuclearFacility.x + Math.cos(angle) * facilityRadius;
s300.y = nuclearFacility.y + Math.sin(angle) * facilityRadius;
s300.canShootOnce = true; // Can only shoot once initially
s300.hasShot = false; // Track if it has shot
facilityS300Systems.push(s300);
groundTargets.push(s300);
game.addChild(s300);
}
// Create 5 vans on the road above the nuclear facility
for (var i = 0; i < 5; i++) {
var van = new GroundTarget('armyVan');
van.x = 200 + i * 400; // Space them across the road
van.y = roadAbove.y;
van.moveDirection = 4; // Move right
facilityTrucks.push(van);
groundTargets.push(van);
game.addChild(van);
}
// Create 5 vans on the road below the nuclear facility
for (var i = 0; i < 5; i++) {
var van = new GroundTarget('armyVan');
van.x = 200 + i * 400; // Space them across the road
van.y = roadBelow.y;
van.moveDirection = -4; // Move left
facilityTrucks.push(van);
groundTargets.push(van);
game.addChild(van);
}
}
function createExplosion(x, y) {
var explosion = new Explosion();
explosion.x = x;
explosion.y = y;
explosions.push(explosion);
game.addChild(explosion);
LK.getSound('explosion').play();
}
function checkMissileFlareCollision(missile) {
for (var f = flares.length - 1; f >= 0; f--) {
var flare = flares[f];
if (missile.intersects(flare)) {
// Flare explodes and destroys missiles in radius
createExplosion(flare.x, flare.y);
// Destroy all missiles within explosion radius
for (var m = missiles.length - 1; m >= 0; m--) {
var targetMissile = missiles[m];
var dx = targetMissile.x - flare.x;
var dy = targetMissile.y - flare.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 150) {
// Explosion radius
createExplosion(targetMissile.x, targetMissile.y);
// Apply missile interception scoring with night mode multiplier
var pointsToAdd = 1; // +1 point for missile destruction by flare
if (isNightMode) {
pointsToAdd *= 2; // Double points during night mode
}
LK.setScore(LK.getScore() + pointsToAdd);
updateScore();
// Track missile interception statistics
gameStats.missilesIntercepted++;
targetMissile.destroy();
missiles.splice(m, 1);
}
}
flare.destroy();
flares.splice(f, 1);
return true;
}
}
return false;
}
// Generate first road at game start
generateRoadStructure();
// Event handlers
game.move = function (x, y, obj) {
fighterJet.x = x;
fighterJet.y = y;
// Improved keyboard input simulation with better detection
var deltaX = Math.abs(x - lastMouseX);
var deltaY = Math.abs(y - lastMouseY);
// F key simulation: horizontal movement with more sensitive detection
if (deltaX > 30 && deltaY < 30) {
fKeyPressed = true;
} else {
fKeyPressed = false;
}
// B key simulation: vertical movement with more sensitive detection
if (deltaY > 30 && deltaX < 30) {
bKeyPressed = true;
} else {
bKeyPressed = false;
}
lastMouseX = x;
lastMouseY = y;
};
game.down = function (x, y, obj) {
// Check for special input zones for keyboard simulation
if (y < 200 && x < 300) {
// Top-left corner click - fire flare (F key simulation)
if (fighterJet.flareCount > 0) {
var flare = new Flare();
flare.x = fighterJet.x + (Math.random() - 0.5) * 100;
flare.y = fighterJet.y + (Math.random() - 0.5) * 100;
flares.push(flare);
game.addChild(flare);
fighterJet.flareCount--;
updateFlareDisplay();
LK.getSound('flare').play();
}
return;
} else if (y < 200 && x > 1748) {
// Top-right corner click - drop bomb (B key simulation)
var bomb = new Bomb();
bomb.x = fighterJet.x;
bomb.y = fighterJet.y + 50;
bombs.push(bomb);
game.addChild(bomb);
LK.getSound('bomb').play();
return;
}
if (obj.event && obj.event.button === 0) {
// Left click - fire bullet
var bullet = new Bullet();
bullet.x = fighterJet.x;
bullet.y = fighterJet.y - 50;
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
} else if (obj.event && obj.event.button === 2) {
// Right click - drop bomb
var bomb = new Bomb();
bomb.x = fighterJet.x;
bomb.y = fighterJet.y + 50;
bombs.push(bomb);
game.addChild(bomb);
LK.getSound('bomb').play();
} else if (!obj.event) {
// Default action for touch/tap (no event object) - fire bullet
var bullet = new Bullet();
bullet.x = fighterJet.x;
bullet.y = fighterJet.y - 50;
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
}
};
game.update = function () {
// Update glow effects for night mode
if (isNightMode) {
updateNightGlow();
}
// Handle keyboard input - using LK tick-based simulation
// Since document is not available, we'll use mouse position changes to detect special actions
var currentFlareKeyPressed = false; // Will be triggered by specific mouse positions
var currentBombKeyPressed = false; // Will be triggered by specific mouse positions
// Fire flare on F key press (only once per press)
if (fKeyPressed && !flareKeyPressed && fighterJet.flareCount > 0) {
var flare = new Flare();
flare.x = fighterJet.x + (Math.random() - 0.5) * 100;
flare.y = fighterJet.y + (Math.random() - 0.5) * 100;
flares.push(flare);
game.addChild(flare);
fighterJet.flareCount--;
updateFlareDisplay();
LK.getSound('flare').play();
}
flareKeyPressed = fKeyPressed;
// Handle bomb cooldown
if (bombCooldown > 0) {
bombCooldown--;
if (bombCooldown === 0) {
bombingActive = true;
updateBombingDisplay();
}
}
// Drop bomb on B key press (only once per press)
if (bKeyPressed && !bombKeyPressed && bombingActive) {
if (!bombTargetMarked) {
// Calculate exact target position based on circle's current position, 55 pixels behind
bombTargetX = fighterJet.x + fighterJet.bombTargetCircle.x;
bombTargetY = fighterJet.y + fighterJet.bombTargetCircle.y + 60;
bombTargetMarked = true;
// Create and drop the bomb
var bomb = new Bomb();
bomb.x = fighterJet.x;
bomb.y = fighterJet.y + 50;
bomb.targetX = bombTargetX;
bomb.targetY = bombTargetY;
// Animate bomb falling to target position
tween(bomb, {
x: bombTargetX,
y: bombTargetY
}, {
duration: 2000,
easing: tween.easeIn,
onFinish: function onFinish() {
// Create big explosion at exact target
createExplosion(bombTargetX, bombTargetY);
// Create persistent destruction mark
var destructionMark = new DestructionMark();
destructionMark.x = bombTargetX;
destructionMark.y = bombTargetY;
destructionMarks.push(destructionMark);
game.addChild(destructionMark);
// Check if bomb hit nuclear facility
if (nuclearFacilityReached && nuclearFacility) {
var facilityDx = nuclearFacility.x - bombTargetX;
var facilityDy = nuclearFacility.y - bombTargetY;
var facilityDistance = Math.sqrt(facilityDx * facilityDx + facilityDy * facilityDy);
if (facilityDistance < 200) {
nuclearFacilityHits++;
// Flash facility to indicate hit
LK.effects.flashObject(nuclearFacility, 0xff0000, 1000);
if (nuclearFacilityHits >= 3) {
// Nuclear facility destroyed - victory!
LK.setScore(LK.getScore() + 50);
updateScore();
LK.showYouWin();
return;
}
}
}
// Destroy ground targets in explosion radius
for (var gt = groundTargets.length - 1; gt >= 0; gt--) {
var target = groundTargets[gt];
var dx = target.x - bombTargetX;
var dy = target.y - bombTargetY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 200) {
// Big explosion radius
createExplosion(target.x, target.y);
// Apply scoring with night mode multiplier
var pointsToAdd = target.points;
if (isNightMode) {
pointsToAdd *= 2; // Double points during night mode
}
LK.setScore(LK.getScore() + pointsToAdd);
updateScore();
// Track statistics for building destruction
if (target.targetType === 'building') {
gameStats.buildingsDestroyed++;
} else if (target.targetType === 'targetBuilding') {
gameStats.targetBuildingsDestroyed++;
} else if (target.targetType === 's300System') {
gameStats.batteriesDestroyed++;
fighterJet.aaSystemsDestroyed++;
if (fighterJet.aaSystemsDestroyed >= 5 && fighterJet.lives < 3) {
fighterJet.lives++;
fighterJet.aaSystemsDestroyed = 0;
updateLivesDisplay();
}
} else if (target.targetType === 'missileStorage') {
gameStats.missileStorageDestroyed++;
}
target.destroy();
groundTargets.splice(gt, 1);
}
}
// Remove and destroy the bomb after explosion
bomb.destroy();
for (var i = bombs.length - 1; i >= 0; i--) {
if (bombs[i] === bomb) {
bombs.splice(i, 1);
break;
}
}
// Start cooldown
bombCooldown = 60; // 1 second at 60fps
bombingActive = false;
bombTargetMarked = false;
updateBombingDisplay();
}
});
bombs.push(bomb);
game.addChild(bomb);
LK.getSound('bomb').play();
}
}
bombKeyPressed = bKeyPressed;
spawnTimer++;
// Generate road structures and spawn targets
if (spawnTimer >= 150) {
// Every 2.5 seconds
spawnGroundTarget();
spawnTimer = 0;
}
// Update road system - move roads with scroll speed and clean up
for (var r = roadSystem.roads.length - 1; r >= 0; r--) {
var road = roadSystem.roads[r];
// Move road down with scroll speed
road.y += scrollSpeed;
// Move corresponding road graphics
if (roadSystem.roadGraphics[r]) {
roadSystem.roadGraphics[r].y += scrollSpeed;
}
// Remove roads that are off screen
if (road.y > 2800) {
// Destroy road graphics
if (roadSystem.roadGraphics[r]) {
roadSystem.roadGraphics[r].destroy();
roadSystem.roadGraphics.splice(r, 1);
}
roadSystem.roads.splice(r, 1);
}
}
// Show bomb target circle when mouse is in bombing position
if (fighterJet.y < 2500) {
fighterJet.bombTargetCircle.visible = true;
} else {
fighterJet.bombTargetCircle.visible = false;
}
// Update bullets
for (var b = bullets.length - 1; b >= 0; b--) {
var bullet = bullets[b];
if (bullet.y < -50) {
bullet.destroy();
bullets.splice(b, 1);
continue;
}
// Check bullet vs ground targets
for (var gt = groundTargets.length - 1; gt >= 0; gt--) {
var target = groundTargets[gt];
if (bullet.intersects(target)) {
// Only allow bullets to destroy certain targets - not S-300, missile buildings, or regular buildings
if (target.targetType === 's300System' || target.targetType === 'targetBuilding' || target.targetType === 'building') {
// Bullets cannot destroy S-300 systems, missile buildings, or regular buildings
bullet.destroy();
bullets.splice(b, 1);
break;
}
createExplosion(target.x, target.y);
// Apply scoring based on target type with night mode multiplier
var pointsToAdd = target.points;
if (isNightMode) {
pointsToAdd *= 2; // Double points during night mode
}
LK.setScore(LK.getScore() + pointsToAdd);
updateScore();
// Track vehicle destruction statistics
if (target.targetType === 'car' || target.targetType === 'armyVan') {
gameStats.vehiclesDestroyed++;
}
bullet.destroy();
bullets.splice(b, 1);
target.destroy();
groundTargets.splice(gt, 1);
break;
}
}
}
// Update bombs - cleanup only
for (var bo = bombs.length - 1; bo >= 0; bo--) {
var bomb = bombs[bo];
if (bomb.destroyed) {
bombs.splice(bo, 1);
}
}
// Update missiles
for (var m = missiles.length - 1; m >= 0; m--) {
var missile = missiles[m];
// Skip if missile doesn't exist (already removed)
if (!missile) {
continue;
}
// Update missile target to current fighter jet position
missile.targetX = fighterJet.x;
missile.targetY = fighterJet.y;
// Check if missile was intercepted by flare
if (checkMissileFlareCollision(missile)) {
// Reset nuclear facility S-300 shooting ability when missile is destroyed
for (var s = 0; s < facilityS300Systems.length; s++) {
var s300 = facilityS300Systems[s];
if (s300 && s300.hasShot) {
s300.canShootOnce = true;
s300.hasShot = false;
}
}
missiles.splice(m, 1);
continue;
}
// Check missile vs fighter jet (only the jet graphics, not the targeting circle)
// Calculate distance to jet center for more precise collision
var dx = missile.x - fighterJet.x;
var dy = missile.y - fighterJet.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Only consider collision if missile is close to the actual jet (not the targeting circle)
if (distance < 60) {
// Jet is 120x80, so radius of ~60 should cover the jet body
createExplosion(fighterJet.x, fighterJet.y);
// Flash screen red when taking damage
LK.effects.flashScreen(0xff0000, 500);
fighterJet.lives--;
updateLivesDisplay();
if (fighterJet.lives <= 0) {
showGameOverWithStats();
return;
}
// Reset nuclear facility S-300 shooting ability when missile hits target
for (var s = 0; s < facilityS300Systems.length; s++) {
var s300 = facilityS300Systems[s];
if (s300 && s300.hasShot) {
s300.canShootOnce = true;
s300.hasShot = false;
}
}
missile.destroy();
missiles.splice(m, 1);
continue;
}
// Remove missiles that go off screen
if (missile.y > 2800 || missile.y < -50 || missile.x < -50 || missile.x > 2100) {
// Reset nuclear facility S-300 shooting ability when missile goes off screen
for (var s = 0; s < facilityS300Systems.length; s++) {
var s300 = facilityS300Systems[s];
if (s300 && s300.hasShot) {
s300.canShootOnce = true;
s300.hasShot = false;
}
}
missile.destroy();
missiles.splice(m, 1);
}
}
// Update ground targets
for (var gt = groundTargets.length - 1; gt >= 0; gt--) {
var target = groundTargets[gt];
if (target.y > 2800) {
target.destroy();
groundTargets.splice(gt, 1);
}
}
// Update flares
for (var f = flares.length - 1; f >= 0; f--) {
var flare = flares[f];
if (flare.destroyed) {
flares.splice(f, 1);
}
}
// Update explosions
for (var e = explosions.length - 1; e >= 0; e--) {
var explosion = explosions[e];
if (explosion.destroyed) {
explosions.splice(e, 1);
}
}
// Update destruction marks
for (var dm = destructionMarks.length - 1; dm >= 0; dm--) {
var destructionMark = destructionMarks[dm];
if (destructionMark.y > 2800) {
destructionMark.destroy();
destructionMarks.splice(dm, 1);
}
}
// Update background stars
for (var bs = backgroundStars.length - 1; bs >= 0; bs--) {
var star = backgroundStars[bs];
if (star.destroyed) {
backgroundStars.splice(bs, 1);
}
}
};
}
B-2 bombing jet. In-Game asset. 2d. High contrast. No shadows
S-300 Anti Air system from the bird view. In-Game asset. 2d. High contrast. No shadows
Hi-Teck buikding. In-Game asset. 2d. High contrast. No shadows
Tiny missile pointing up. In-Game asset. 2d. High contrast. No shadows
Strait anti air missile. In-Game asset. 2d. High contrast. No shadows
strait flare missile. In-Game asset. 2d. High contrast. No shadows
WW2 bomb. In-Game asset. 2d. High contrast. No shadows
Army building. In-Game asset. 2d. High contrast. No shadows
רכב צבאי ארוך וגדול, כמו תובלתית, מכוסה בברזנט, מבט מהפרופיל.
Missiles storage. In-Game asset. 2d. High contrast. No shadows
מתקן ענק לשיגור טיל שעליו מוכן טיל גרעיני. In-Game asset. 2d. High contrast. No shadows