User prompt
Still, nothing add a starting page, where you can name the pilot and it will store your score using this name for normal building, or regular van, decrease points ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
The primary change needed is to move the line that updates lastKeys to the very beginning of your game.update function. Here's why and how: The Problem Currently, your code updates the lastKeys object (which stores the state of keys from the previous frame) at the end of the game.update function. This means that when you check if (keys['Digit2'] && !lastKeys['Digit2']), lastKeys['Digit2'] might already be true if you pressed and released the key quickly within the same frame. This causes the condition !lastKeys['Digit2'] to be false, and thus the weapon doesn't switch. The Solution By moving the lastKeys update to the start of the game.update function, you ensure that lastKeys always accurately reflects the state of the keys from the immediately preceding frame. This allows the !lastKeys['Digit2'] check to correctly detect when the '2' key is initially pressed down.
User prompt
flares doesnt work pressing 2 doesnt change the weapon fix the input from keyboard
User prompt
Pressing 2 or 1 doesnt change the weapon. Only firing with left click works flares still doesnt work while pressing spacebar
User prompt
Flares doesnt work. Also the bombing. Only the regular cannon work. Lets do it like this: You have 2 states. One is normal cannon, the other is bombing. You choose using pressing 1\2. bombing will be also using click. when you are on the bombing, you have a circle in front of the jet, showing the area that will damage with the bomb. cooldown of 5 seconds. and fix the flare, still doesnt work
User prompt
The flare and the bombing doesnt work, make the backround looks like a desert
User prompt
make the target building way more rare and the normal bulding rare too S-300 a little bit more rare make the bombing usable via button B, and not tighr click flares using spacebar, now the flares doesnt work
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'button')' in or related to this line: 'if (obj.event.button === 0) {' Line Number: 270
Code edit (1 edits merged)
Please save this source code
User prompt
Fighter Jet Strike
Initial prompt
An fighter jet attack game. You control a fighter jet, with the mouse. Where the mouse is where the jet. You have regular cannon that fire in strait line, that fire pressing left mouse. Bombing, that drop under the jet, using right click. There are S-300 AA on the ground sometimes, that fires to your jet. Uisng spacebar you can shoot flames that makes thr AA miss you. Its reload over time. There are regular buildings, target buldings, normal cars, army vans. Target buildings and AA the player destroy using bombing. Army cars, using normal cannon. It spawns while you fly forward. You have 3 lives, every taget you kill you get some score, after killing 5 AA you gain +1 live butno more than 3.
/**** * 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