/**** * 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