User prompt
Add SSR spawns to the guide. โช๐ก Consider importing and using the following plugins: @upit/storage.v1
User prompt
Add enemy SSR bias (MiG-23, Yak-38 and Drones in Ukraine, MiG-23, Su-15, SAMs in Russia, Yak-38 (swarms), drones and Shilkas in Kazakhstan and SAMs, Su-15s, Shilkas and MiG-23s in Belarus)
User prompt
Add a Shilka SPAA enemy (Behaves similarly to a drone, but shoots in bursts (bursts include 5x 3-bullet groups) and rolls vertically)
User prompt
Now make a guide with 2 tips on each enemy, how to use travel and upgrades and Basic maneuvers.
User prompt
Now the Interceptor Split (MiG-23, Su-15, Yak-38) โช๐ก Consider importing and using the following plugins: @upit/storage.v1
User prompt
You can do a Enemy CODEX, as I'm planning the Interceptor Type Split. โช๐ก Consider importing and using the following plugins: @upit/storage.v1
User prompt
Add traveling to different Soviet SSRs.
User prompt
No, the popup for the store is either invisible or does not show.
User prompt
Upgrade store won't show.
User prompt
Add upgrades for the Viggen that you buy with score โช๐ก Consider importing and using the following plugins: @upit/storage.v1
User prompt
Make SAM spawning rarer, it's the main challenge
User prompt
Add powerups
User prompt
Make the viggen shoot bullets at the enemies
User prompt
Clean up shots from interceptor after a bit, should help
User prompt
Game starts lagging significantly after a few dodges
Code edit (1 edits merged)
Please save this source code
User prompt
Viggen Strike: Soviet Skies
Initial prompt
A bullet hell videogame where you control an Saab 37 Viggen through Soviet airspace while dodging interceptors, SAMs and drones.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Drone = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('drone', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 1.5; self.direction = 1; self.fireTimer = 0; self.fireRate = 45; self.update = function () { var speedMultiplier = currentSSR ? currentSSR.enemySpeedMultiplier : 1.0; self.x += self.speed * self.direction * speedMultiplier; self.y += self.speed * 0.5 * speedMultiplier; if (self.x <= 50 || self.x >= 1998) { self.direction *= -1; } self.fireTimer++; if (self.fireTimer >= self.fireRate) { self.fireTimer = 0; var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y; bullet.targetX = viggen.x; bullet.targetY = viggen.y; bullet.age = 0; game.addChild(bullet); enemyBullets.push(bullet); } graphics.rotation += 0.05; }; return self; }); var EnemyBullet = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('enemyBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 4; self.targetX = 0; self.targetY = 0; self.update = function () { self.age = (self.age || 0) + 1; var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { var speedMultiplier = currentSSR ? currentSSR.enemySpeedMultiplier : 1.0; self.x += dx / distance * self.speed * speedMultiplier; self.y += dy / distance * self.speed * speedMultiplier; } }; return self; }); var MiG23 = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('mig23', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2; self.fireTimer = 0; self.fireRate = 60; self.interceptorType = 'mig23'; self.scoreValue = 100; self.update = function () { var speedMultiplier = currentSSR ? currentSSR.enemySpeedMultiplier : 1.0; self.y += self.speed * speedMultiplier; self.fireTimer++; if (self.fireTimer >= self.fireRate) { self.fireTimer = 0; var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y; bullet.targetX = viggen.x; bullet.targetY = viggen.y; bullet.age = 0; game.addChild(bullet); enemyBullets.push(bullet); } }; return self; }); var Powerup = Container.expand(function () { var self = Container.call(this); self.powerupType = 'speed'; // 'speed', 'shield', 'rapidfire' self.speed = 2; self.bobOffset = 0; self.bobSpeed = 0.1; self.setPowerupType = function (type) { self.powerupType = type; if (self.graphics) { self.removeChild(self.graphics); } var assetName = 'powerupSpeed'; if (type === 'shield') { assetName = 'powerupShield'; } else if (type === 'rapidfire') { assetName = 'powerupRapidFire'; } self.graphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); }; self.update = function () { self.y += self.speed; self.bobOffset += self.bobSpeed; self.graphics.y = Math.sin(self.bobOffset) * 10; self.graphics.rotation += 0.05; }; return self; }); var SAMMissile = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('samMissile', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 6; self.targetX = 0; self.targetY = 0; self.update = function () { 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; } graphics.rotation += 0.1; }; return self; }); var SAMSite = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('sam', { anchorX: 0.5, anchorY: 0.5 }); self.fireTimer = 0; self.fireRate = 90; self.update = function () { self.fireTimer++; if (self.fireTimer >= self.fireRate) { self.fireTimer = 0; var missile = new SAMMissile(); missile.x = self.x; missile.y = self.y; missile.targetX = viggen.x; missile.targetY = viggen.y; game.addChild(missile); samMissiles.push(missile); LK.getSound('missile').play(); } }; return self; }); var SSR = Container.expand(function () { var self = Container.call(this); self.name = ''; self.difficultyMultiplier = 1.0; self.enemySpeedMultiplier = 1.0; self.spawnRateMultiplier = 1.0; self.scoreMultiplier = 1.0; self.backgroundColor = 0x1a1a2e; self.description = ''; self.setSSRData = function (data) { self.name = data.name; self.difficultyMultiplier = data.difficultyMultiplier; self.enemySpeedMultiplier = data.enemySpeedMultiplier; self.spawnRateMultiplier = data.spawnRateMultiplier; self.scoreMultiplier = data.scoreMultiplier; self.backgroundColor = data.backgroundColor; self.description = data.description; if (self.graphics) { self.removeChild(self.graphics); } self.graphics = self.attachAsset(data.assetName, { anchorX: 0.5, anchorY: 0.5 }); }; return self; }); var Su15 = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('su15', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2.5; self.fireTimer = 0; self.fireRate = 45; self.interceptorType = 'su15'; self.scoreValue = 150; self.update = function () { var speedMultiplier = currentSSR ? currentSSR.enemySpeedMultiplier : 1.0; self.y += self.speed * speedMultiplier; self.fireTimer++; if (self.fireTimer >= self.fireRate) { self.fireTimer = 0; // Su-15 fires two bullets for (var i = 0; i < 2; i++) { var bullet = new EnemyBullet(); bullet.x = self.x + (i === 0 ? -10 : 10); bullet.y = self.y; bullet.targetX = viggen.x; bullet.targetY = viggen.y; bullet.age = 0; game.addChild(bullet); enemyBullets.push(bullet); } } }; return self; }); var Viggen = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('viggen', { anchorX: 0.5, anchorY: 0.5 }); return self; }); var ViggenBullet = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('viggenBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8 + (upgrades.bulletSpeed - 1) * 2; self.damage = upgrades.bulletDamage; self.update = function () { self.y -= self.speed; }; return self; }); var Yak38 = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('yak38', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 1.5; self.fireTimer = 0; self.fireRate = 90; self.interceptorType = 'yak38'; self.scoreValue = 80; self.sideMovement = 0; self.update = function () { var speedMultiplier = currentSSR ? currentSSR.enemySpeedMultiplier : 1.0; self.y += self.speed * speedMultiplier; // Yak-38 has slight side-to-side movement self.sideMovement += 0.05; self.x += Math.sin(self.sideMovement) * 0.8; self.fireTimer++; if (self.fireTimer >= self.fireRate) { self.fireTimer = 0; var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y; bullet.targetX = viggen.x; bullet.targetY = viggen.y; bullet.age = 0; game.addChild(bullet); enemyBullets.push(bullet); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a2e }); /**** * Game Code ****/ var viggen = game.addChild(new Viggen()); viggen.x = 1024; viggen.y = 2400; var shieldEffect = null; // Upgrade system variables var upgradeMenuOpen = false; var upgradeButton = null; var upgradeMenu = null; var upgrades = { fireRate: storage.fireRateLevel || 1, bulletSpeed: storage.bulletSpeedLevel || 1, bulletDamage: storage.bulletDamageLevel || 1, shieldDuration: storage.shieldDurationLevel || 1 }; var upgradeCosts = { fireRate: [100, 250, 500, 1000, 2000], bulletSpeed: [150, 300, 600, 1200, 2400], bulletDamage: [200, 400, 800, 1600, 3200], shieldDuration: [300, 600, 1200, 2400, 4800] }; var upgradeTexts = []; // SSR Travel System var currentSSR = null; var ssrMenuOpen = false; var ssrButton = null; var ssrMenu = null; var ssrUnlocked = { ukraine: true, russia: storage.russiaUnlocked || false, kazakh: storage.kazakhUnlocked || false, belarus: storage.belarusUnlocked || false }; // Enemy Codex System var codexMenuOpen = false; var codexMenu = null; var enemiesEncountered = { mig23: storage.mig23Encountered || false, su15: storage.su15Encountered || false, yak38: storage.yak38Encountered || false, drone: storage.droneEncountered || false, samSite: storage.samSiteEncountered || false }; var enemyCodexData = { mig23: { name: 'MiG-23 Flogger', description: 'Standard Soviet interceptor. Fast attack aircraft with guided missiles. Flies straight down and fires single tracking bullets.', threat: 'Medium', speed: 'Fast', fireRate: 'Medium', health: '1 Hit', tips: 'Predictable flight path makes them easy targets', assetName: 'mig23' }, su15: { name: 'Su-15 Flagon', description: 'Heavy interceptor with twin cannons. Faster than MiG-23 and fires dual tracking bullets simultaneously.', threat: 'High', speed: 'Very Fast', fireRate: 'Fast', health: '1 Hit', tips: 'Dual fire pattern - dodge sideways to avoid both bullets', assetName: 'su15' }, yak38: { name: 'Yak-38 Forger', description: 'VTOL light interceptor with erratic movement. Slower but unpredictable side-to-side flight pattern.', threat: 'Medium-Low', speed: 'Slow', fireRate: 'Slow', health: '1 Hit', tips: 'Unpredictable movement but slower fire rate. Lead your shots.', assetName: 'yak38' }, drone: { name: 'Reconnaissance Drone', description: 'Agile unmanned aircraft with side-to-side movement. Fires tracking bullets while weaving.', threat: 'High', speed: 'Medium', fireRate: 'Fast', health: '1 Hit', tips: 'Unpredictable movement pattern. Lead your shots.', assetName: 'drone' }, samSite: { name: 'SA-6 SAM Site', description: 'Surface-to-Air Missile launcher. Fires homing missiles that track your position.', threat: 'Very High', speed: 'Stationary', fireRate: 'Slow', health: '1 Hit', tips: 'Priority target. Missiles are fast and accurate.', assetName: 'sam' } }; var ssrData = { ukraine: { name: 'Ukrainian SSR', assetName: 'ssrUkraine', difficultyMultiplier: 1.0, enemySpeedMultiplier: 1.0, spawnRateMultiplier: 1.0, scoreMultiplier: 1.0, backgroundColor: 0x1a1a2e, description: 'Starting region with standard difficulty', unlockScore: 0 }, russia: { name: 'Russian SFSR', assetName: 'ssrRussia', difficultyMultiplier: 1.3, enemySpeedMultiplier: 1.2, spawnRateMultiplier: 1.1, scoreMultiplier: 1.5, backgroundColor: 0x2e1a1a, description: 'Harsh winter conditions, faster enemies', unlockScore: 2000 }, kazakh: { name: 'Kazakh SSR', assetName: 'ssrKazakh', difficultyMultiplier: 1.5, enemySpeedMultiplier: 1.1, spawnRateMultiplier: 1.3, scoreMultiplier: 2.0, backgroundColor: 0x2e2e1a, description: 'Desert warfare, more frequent spawns', unlockScore: 5000 }, belarus: { name: 'Byelorussian SSR', assetName: 'ssrBelarus', difficultyMultiplier: 1.8, enemySpeedMultiplier: 1.4, spawnRateMultiplier: 1.2, scoreMultiplier: 2.5, backgroundColor: 0x1a2e1a, description: 'Forest combat, elite enemy units', unlockScore: 10000 } }; var interceptors = []; var mig23s = []; var su15s = []; var yak38s = []; var samSites = []; var drones = []; var enemyBullets = []; var samMissiles = []; var viggenBullets = []; var powerups = []; var spawnTimer = 0; var powerupSpawnTimer = 0; var fireTimer = 0; var difficultyTimer = 0; var survivalTime = 0; var baseSpawnRate = 180; var currentSpawnRate = baseSpawnRate; var dragNode = null; var lastIntersecting = false; var speedBoostActive = false; var speedBoostTimer = 0; var shieldActive = false; var shieldTimer = 0; var rapidFireActive = false; var rapidFireTimer = 0; var normalFireRate = 15; var scoreTxt = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var timeTxt = new Text2('Time: 0s', { size: 50, fill: 0xFFFFFF }); timeTxt.anchor.set(1, 0); timeTxt.x = -20; timeTxt.y = 20; LK.gui.topRight.addChild(timeTxt); upgradeButton = new Text2('UPGRADES', { size: 40, fill: 0x00ff00 }); upgradeButton.anchor.set(0.5, 0); upgradeButton.x = 0; upgradeButton.y = 100; upgradeButton.interactive = true; LK.gui.top.addChild(upgradeButton); ssrButton = new Text2('TRAVEL', { size: 40, fill: 0x0088ff }); ssrButton.anchor.set(0.5, 0); ssrButton.x = 0; ssrButton.y = 160; ssrButton.interactive = true; LK.gui.top.addChild(ssrButton); var codexButton = new Text2('CODEX', { size: 40, fill: 0xff8800 }); codexButton.anchor.set(0.5, 0); codexButton.x = 0; codexButton.y = 220; codexButton.interactive = true; LK.gui.top.addChild(codexButton); // Initialize with Ukrainian SSR currentSSR = new SSR(); currentSSR.setSSRData(ssrData.ukraine); game.setBackgroundColor(currentSSR.backgroundColor); function handleMove(x, y, obj) { if (dragNode) { var moveSpeed = speedBoostActive ? 1.5 : 1.0; var targetX = x; var targetY = y; if (speedBoostActive) { // Smoother movement with speed boost var dx = targetX - dragNode.x; var dy = targetY - dragNode.y; dragNode.x = Math.max(40, Math.min(2008, dragNode.x + dx * moveSpeed)); dragNode.y = Math.max(60, Math.min(2672, dragNode.y + dy * moveSpeed)); } else { dragNode.x = Math.max(40, Math.min(2008, targetX)); dragNode.y = Math.max(60, Math.min(2672, targetY)); } } // Check viggen bullets vs enemies for (var i = viggenBullets.length - 1; i >= 0; i--) { var bullet = viggenBullets[i]; var hit = false; // Check vs interceptors for (var j = interceptors.length - 1; j >= 0; j--) { if (bullet.intersects(interceptors[j])) { LK.effects.flashObject(interceptors[j], 0xff0000, 200); var scoreValue = interceptors[j].scoreValue || 100; var interceptorType = interceptors[j].interceptorType; // Remove from specific type array if (interceptorType === 'mig23') { for (var k = mig23s.length - 1; k >= 0; k--) { if (mig23s[k] === interceptors[j]) { mig23s.splice(k, 1); break; } } } else if (interceptorType === 'su15') { for (var k = su15s.length - 1; k >= 0; k--) { if (su15s[k] === interceptors[j]) { su15s.splice(k, 1); break; } } } else if (interceptorType === 'yak38') { for (var k = yak38s.length - 1; k >= 0; k--) { if (yak38s[k] === interceptors[j]) { yak38s.splice(k, 1); break; } } } interceptors[j].destroy(); interceptors.splice(j, 1); bullet.destroy(); viggenBullets.splice(i, 1); var baseScore = scoreValue * bullet.damage; var ssrMultiplier = currentSSR ? currentSSR.scoreMultiplier : 1.0; LK.setScore(LK.getScore() + Math.floor(baseScore * ssrMultiplier)); hit = true; break; } } if (hit) continue; // Check vs drones for (var j = drones.length - 1; j >= 0; j--) { if (bullet.intersects(drones[j])) { LK.effects.flashObject(drones[j], 0xff0000, 200); drones[j].destroy(); drones.splice(j, 1); bullet.destroy(); viggenBullets.splice(i, 1); var baseScore = 150 * bullet.damage; var ssrMultiplier = currentSSR ? currentSSR.scoreMultiplier : 1.0; LK.setScore(LK.getScore() + Math.floor(baseScore * ssrMultiplier)); hit = true; break; } } if (hit) continue; // Check vs SAM sites for (var j = samSites.length - 1; j >= 0; j--) { if (bullet.intersects(samSites[j])) { LK.effects.flashObject(samSites[j], 0xff0000, 200); samSites[j].destroy(); samSites.splice(j, 1); bullet.destroy(); viggenBullets.splice(i, 1); var baseScore = 200 * bullet.damage; var ssrMultiplier = currentSSR ? currentSSR.scoreMultiplier : 1.0; LK.setScore(LK.getScore() + Math.floor(baseScore * ssrMultiplier)); hit = true; break; } } } // Check viggen vs powerups for (var i = powerups.length - 1; i >= 0; i--) { var powerup = powerups[i]; if (viggen.intersects(powerup)) { // Apply powerup effect if (powerup.powerupType === 'speed') { speedBoostActive = true; speedBoostTimer = 300; // 5 seconds at 60fps LK.effects.flashObject(viggen, 0x00ff00, 300); } else if (powerup.powerupType === 'shield') { shieldActive = true; shieldTimer = 600 + (upgrades.shieldDuration - 1) * 180; // Base 10s + upgrades if (!shieldEffect) { shieldEffect = LK.getAsset('powerupShield', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 2.5 }); shieldEffect.alpha = 0.3; viggen.addChild(shieldEffect); } } else if (powerup.powerupType === 'rapidfire') { rapidFireActive = true; rapidFireTimer = 450; // 7.5 seconds at 60fps LK.effects.flashObject(viggen, 0xff8800, 300); } LK.getSound('powerup').play(); LK.setScore(LK.getScore() + 50); powerup.destroy(); powerups.splice(i, 1); } } } game.move = handleMove; game.down = function (x, y, obj) { dragNode = viggen; handleMove(x, y, obj); }; game.up = function (x, y, obj) { dragNode = null; }; upgradeButton.down = function (x, y, obj) { if (!upgradeMenuOpen) { openUpgradeMenu(); } else { closeUpgradeMenu(); } }; ssrButton.down = function (x, y, obj) { if (!ssrMenuOpen) { openSSRMenu(); } else { closeSSRMenu(); } }; codexButton.down = function (x, y, obj) { if (!codexMenuOpen) { openCodexMenu(); } else { closeCodexMenu(); } }; function spawnInterceptor() { var interceptorType = Math.random(); var interceptor; if (interceptorType < 0.5) { interceptor = new MiG23(); mig23s.push(interceptor); markEnemyEncountered('mig23'); } else if (interceptorType < 0.8) { interceptor = new Su15(); su15s.push(interceptor); markEnemyEncountered('su15'); } else { interceptor = new Yak38(); yak38s.push(interceptor); markEnemyEncountered('yak38'); } interceptor.x = Math.random() * 1848 + 100; interceptor.y = -100; game.addChild(interceptor); interceptors.push(interceptor); } function spawnSAMSite() { var sam = new SAMSite(); sam.x = Math.random() * 1848 + 100; sam.y = Math.random() * 400 + 100; game.addChild(sam); samSites.push(sam); markEnemyEncountered('samSite'); } function spawnDrone() { var drone = new Drone(); drone.x = Math.random() * 1848 + 100; drone.y = -50; game.addChild(drone); drones.push(drone); markEnemyEncountered('drone'); } function spawnPowerup() { var powerup = new Powerup(); powerup.x = Math.random() * 1600 + 224; powerup.y = -50; var powerupTypes = ['speed', 'shield', 'rapidfire']; var randomType = powerupTypes[Math.floor(Math.random() * powerupTypes.length)]; powerup.setPowerupType(randomType); game.addChild(powerup); powerups.push(powerup); } function openUpgradeMenu() { upgradeMenuOpen = true; upgradeButton.setText('CLOSE'); upgradeMenu = new Container(); upgradeMenu.x = 0; upgradeMenu.y = 0; game.addChild(upgradeMenu); var menuBg = LK.getAsset('sam', { anchorX: 0.5, anchorY: 0.5, scaleX: 15, scaleY: 20 }); menuBg.x = 1024; menuBg.y = 1366; menuBg.alpha = 0.9; menuBg.tint = 0x000066; upgradeMenu.addChild(menuBg); var titleText = new Text2('UPGRADES', { size: 80, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 1000; upgradeMenu.addChild(titleText); var currentScore = LK.getScore(); var scoreText = new Text2('Score: ' + currentScore, { size: 50, fill: 0xFFFF00 }); scoreText.anchor.set(0.5, 0.5); scoreText.x = 1024; scoreText.y = 1100; upgradeMenu.addChild(scoreText); createUpgradeOption('Fire Rate', 'fireRate', 1200); createUpgradeOption('Bullet Speed', 'bulletSpeed', 1300); createUpgradeOption('Bullet Damage', 'bulletDamage', 1400); createUpgradeOption('Shield Duration', 'shieldDuration', 1500); } function createUpgradeOption(name, upgradeType, yPos) { var level = upgrades[upgradeType]; var maxLevel = upgradeCosts[upgradeType].length; var cost = level < maxLevel ? upgradeCosts[upgradeType][level - 1] : 'MAX'; var upgradeText = new Text2(name + ' Lv.' + level + ' - Cost: ' + cost, { size: 40, fill: level < maxLevel && LK.getScore() >= cost ? 0x00ff00 : 0xff6666 }); upgradeText.anchor.set(0.5, 0.5); upgradeText.x = 1024; upgradeText.y = yPos; upgradeText.interactive = true; upgradeMenu.addChild(upgradeText); upgradeTexts.push({ text: upgradeText, type: upgradeType }); if (level < maxLevel && LK.getScore() >= cost) { upgradeText.down = function () { buyUpgrade(upgradeType); }; } } function buyUpgrade(upgradeType) { var level = upgrades[upgradeType]; var maxLevel = upgradeCosts[upgradeType].length; var cost = upgradeCosts[upgradeType][level - 1]; if (level < maxLevel && LK.getScore() >= cost) { LK.setScore(LK.getScore() - cost); upgrades[upgradeType]++; // Save to storage if (upgradeType === 'fireRate') storage.fireRateLevel = upgrades[upgradeType];else if (upgradeType === 'bulletSpeed') storage.bulletSpeedLevel = upgrades[upgradeType];else if (upgradeType === 'bulletDamage') storage.bulletDamageLevel = upgrades[upgradeType];else if (upgradeType === 'shieldDuration') storage.shieldDurationLevel = upgrades[upgradeType]; scoreTxt.setText('Score: ' + LK.getScore()); LK.getSound('powerup').play(); closeUpgradeMenu(); } } function closeUpgradeMenu() { upgradeMenuOpen = false; upgradeButton.setText('UPGRADES'); if (upgradeMenu) { game.removeChild(upgradeMenu); upgradeMenu = null; upgradeTexts = []; } } function openSSRMenu() { ssrMenuOpen = true; ssrButton.setText('CLOSE'); ssrMenu = new Container(); ssrMenu.x = 0; ssrMenu.y = 0; game.addChild(ssrMenu); var menuBg = LK.getAsset('sam', { anchorX: 0.5, anchorY: 0.5, scaleX: 15, scaleY: 20 }); menuBg.x = 1024; menuBg.y = 1366; menuBg.alpha = 0.9; menuBg.tint = 0x006666; ssrMenu.addChild(menuBg); var titleText = new Text2('SOVIET SOCIALIST REPUBLICS', { size: 60, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 900; ssrMenu.addChild(titleText); var currentText = new Text2('Current: ' + currentSSR.name, { size: 40, fill: 0xFFFF00 }); currentText.anchor.set(0.5, 0.5); currentText.x = 1024; currentText.y = 980; ssrMenu.addChild(currentText); var yPos = 1100; for (var ssrKey in ssrData) { createSSROption(ssrKey, ssrData[ssrKey], yPos); yPos += 100; } // Check for unlocks checkSSRUnlocks(); } function createSSROption(ssrKey, data, yPos) { var isUnlocked = ssrUnlocked[ssrKey]; var isCurrent = currentSSR.name === data.name; var statusText = isCurrent ? ' (CURRENT)' : isUnlocked ? ' - AVAILABLE' : ' - LOCKED (Score: ' + data.unlockScore + ')'; var optionText = new Text2(data.name + statusText, { size: 35, fill: isCurrent ? 0xFFFF00 : isUnlocked ? 0x00ff00 : 0xff6666 }); optionText.anchor.set(0.5, 0.5); optionText.x = 1024; optionText.y = yPos; optionText.interactive = true; ssrMenu.addChild(optionText); var descText = new Text2(data.description, { size: 25, fill: 0xCCCCCC }); descText.anchor.set(0.5, 0.5); descText.x = 1024; descText.y = yPos + 30; ssrMenu.addChild(descText); if (isUnlocked && !isCurrent) { optionText.down = function () { travelToSSR(ssrKey); }; } } function travelToSSR(ssrKey) { var data = ssrData[ssrKey]; currentSSR.setSSRData(data); game.setBackgroundColor(currentSSR.backgroundColor); LK.getSound('travel').play(); LK.effects.flashScreen(0x00ffff, 500); // Reset some game state for new region spawnTimer = 0; currentSpawnRate = Math.floor(baseSpawnRate / currentSSR.spawnRateMultiplier); closeSSRMenu(); } function closeSSRMenu() { ssrMenuOpen = false; ssrButton.setText('TRAVEL'); if (ssrMenu) { game.removeChild(ssrMenu); ssrMenu = null; } } function checkSSRUnlocks() { var currentScore = LK.getScore(); for (var ssrKey in ssrData) { if (!ssrUnlocked[ssrKey] && currentScore >= ssrData[ssrKey].unlockScore) { ssrUnlocked[ssrKey] = true; // Save unlock status if (ssrKey === 'russia') storage.russiaUnlocked = true;else if (ssrKey === 'kazakh') storage.kazakhUnlocked = true;else if (ssrKey === 'belarus') storage.belarusUnlocked = true; LK.effects.flashScreen(0x00ff00, 300); } } } function openCodexMenu() { codexMenuOpen = true; codexButton.setText('CLOSE'); codexMenu = new Container(); codexMenu.x = 0; codexMenu.y = 0; game.addChild(codexMenu); var menuBg = LK.getAsset('sam', { anchorX: 0.5, anchorY: 0.5, scaleX: 15, scaleY: 20 }); menuBg.x = 1024; menuBg.y = 1366; menuBg.alpha = 0.9; menuBg.tint = 0x660066; codexMenu.addChild(menuBg); var titleText = new Text2('ENEMY CODEX', { size: 80, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 900; codexMenu.addChild(titleText); var instructionText = new Text2('Encounter enemies to unlock their data', { size: 35, fill: 0xCCCCCC }); instructionText.anchor.set(0.5, 0.5); instructionText.x = 1024; instructionText.y = 980; codexMenu.addChild(instructionText); var yPos = 1100; for (var enemyKey in enemyCodexData) { createCodexEntry(enemyKey, enemyCodexData[enemyKey], yPos); yPos += 200; } } function createCodexEntry(enemyKey, data, yPos) { var isEncountered = enemiesEncountered[enemyKey]; var nameText = new Text2(isEncountered ? data.name : '??? CLASSIFIED ???', { size: 45, fill: isEncountered ? 0x00ff00 : 0x666666 }); nameText.anchor.set(0.5, 0.5); nameText.x = 1024; nameText.y = yPos; codexMenu.addChild(nameText); if (isEncountered) { var descText = new Text2(data.description, { size: 30, fill: 0xFFFFFF }); descText.anchor.set(0.5, 0.5); descText.x = 1024; descText.y = yPos + 40; codexMenu.addChild(descText); var statsText = new Text2('Threat: ' + data.threat + ' | Speed: ' + data.speed + ' | Fire Rate: ' + data.fireRate, { size: 25, fill: 0xFFFF88 }); statsText.anchor.set(0.5, 0.5); statsText.x = 1024; statsText.y = yPos + 75; codexMenu.addChild(statsText); var tipsText = new Text2('Tips: ' + data.tips, { size: 25, fill: 0x88FF88 }); tipsText.anchor.set(0.5, 0.5); tipsText.x = 1024; tipsText.y = yPos + 105; codexMenu.addChild(tipsText); } else { var lockedText = new Text2('Encounter this enemy type to unlock data', { size: 25, fill: 0x666666 }); lockedText.anchor.set(0.5, 0.5); lockedText.x = 1024; lockedText.y = yPos + 40; codexMenu.addChild(lockedText); } } function closeCodexMenu() { codexMenuOpen = false; codexButton.setText('CODEX'); if (codexMenu) { game.removeChild(codexMenu); codexMenu = null; } } function markEnemyEncountered(enemyType) { if (!enemiesEncountered[enemyType]) { enemiesEncountered[enemyType] = true; // Save to storage if (enemyType === 'mig23') storage.mig23Encountered = true;else if (enemyType === 'su15') storage.su15Encountered = true;else if (enemyType === 'yak38') storage.yak38Encountered = true;else if (enemyType === 'drone') storage.droneEncountered = true;else if (enemyType === 'samSite') storage.samSiteEncountered = true; // Flash effect for discovery LK.effects.flashScreen(0x00ffff, 200); } } function checkCollisions() { if (shieldActive) { // Shield protects from all damage return; } for (var i = 0; i < enemyBullets.length; i++) { if (viggen.intersects(enemyBullets[i])) { LK.effects.flashScreen(0xff0000, 1000); LK.getSound('explosion').play(); LK.showGameOver(); return; } } for (var i = 0; i < samMissiles.length; i++) { if (viggen.intersects(samMissiles[i])) { LK.effects.flashScreen(0xff0000, 1000); LK.getSound('explosion').play(); LK.showGameOver(); return; } } for (var i = 0; i < interceptors.length; i++) { if (viggen.intersects(interceptors[i])) { LK.effects.flashScreen(0xff0000, 1000); LK.getSound('explosion').play(); LK.showGameOver(); return; } } for (var i = 0; i < drones.length; i++) { if (viggen.intersects(drones[i])) { LK.effects.flashScreen(0xff0000, 1000); LK.getSound('explosion').play(); LK.showGameOver(); return; } } } function cleanupOffscreenEntities() { for (var i = interceptors.length - 1; i >= 0; i--) { if (interceptors[i].y > 2800) { var interceptorType = interceptors[i].interceptorType; // Remove from specific type array if (interceptorType === 'mig23') { for (var k = mig23s.length - 1; k >= 0; k--) { if (mig23s[k] === interceptors[i]) { mig23s.splice(k, 1); break; } } } else if (interceptorType === 'su15') { for (var k = su15s.length - 1; k >= 0; k--) { if (su15s[k] === interceptors[i]) { su15s.splice(k, 1); break; } } } else if (interceptorType === 'yak38') { for (var k = yak38s.length - 1; k >= 0; k--) { if (yak38s[k] === interceptors[i]) { yak38s.splice(k, 1); break; } } } interceptors[i].destroy(); interceptors.splice(i, 1); } } for (var i = drones.length - 1; i >= 0; i--) { if (drones[i].y > 2800) { drones[i].destroy(); drones.splice(i, 1); } } for (var i = enemyBullets.length - 1; i >= 0; i--) { var bullet = enemyBullets[i]; if (bullet.x < -20 || bullet.x > 2068 || bullet.y < -20 || bullet.y > 2752 || bullet.age && bullet.age > 300) { bullet.destroy(); enemyBullets.splice(i, 1); } } for (var i = samMissiles.length - 1; i >= 0; i--) { var missile = samMissiles[i]; if (missile.x < -50 || missile.x > 2098 || missile.y < -50 || missile.y > 2782) { missile.destroy(); samMissiles.splice(i, 1); } } for (var i = samSites.length - 1; i >= 0; i--) { if (samSites[i].y > 2800 || samSites[i].x < -100 || samSites[i].x > 2148) { samSites[i].destroy(); samSites.splice(i, 1); } } // Clean up viggen bullets for (var i = viggenBullets.length - 1; i >= 0; i--) { if (viggenBullets[i].y < -50) { viggenBullets[i].destroy(); viggenBullets.splice(i, 1); } } // Clean up powerups for (var i = powerups.length - 1; i >= 0; i--) { if (powerups[i].y > 2800) { powerups[i].destroy(); powerups.splice(i, 1); } } } game.update = function () { survivalTime++; difficultyTimer++; spawnTimer++; if (LK.ticks % 60 === 0) { var timeInSeconds = Math.floor(survivalTime / 60); timeTxt.setText('Time: ' + timeInSeconds + 's'); var score = timeInSeconds * 10 + Math.floor(survivalTime / 6); LK.setScore(score); scoreTxt.setText('Score: ' + score); } if (difficultyTimer >= 300) { difficultyTimer = 0; currentSpawnRate = Math.max(60, currentSpawnRate - 10); } // Apply SSR spawn rate multiplier var ssrSpawnRate = currentSSR ? Math.floor(currentSpawnRate / currentSSR.spawnRateMultiplier) : currentSpawnRate; if (spawnTimer >= ssrSpawnRate) { spawnTimer = 0; var spawnType = Math.random(); if (spawnType < 0.4) { spawnInterceptor(); } else if (spawnType < 0.9) { spawnDrone(); } else { spawnSAMSite(); } } // Check for SSR unlocks checkSSRUnlocks(); // Spawn powerups occasionally powerupSpawnTimer++; if (powerupSpawnTimer >= 900) { // Every 15 seconds powerupSpawnTimer = 0; spawnPowerup(); } // Update powerup effects if (speedBoostTimer > 0) { speedBoostTimer--; if (speedBoostTimer === 0) { speedBoostActive = false; } } if (shieldTimer > 0) { shieldTimer--; if (shieldTimer === 0) { shieldActive = false; if (shieldEffect) { viggen.removeChild(shieldEffect); shieldEffect = null; } } } if (rapidFireTimer > 0) { rapidFireTimer--; if (rapidFireTimer === 0) { rapidFireActive = false; } } // Fire viggen bullets fireTimer++; var upgradeFireRate = Math.max(5, normalFireRate - (upgrades.fireRate - 1) * 2); var currentFireRate = rapidFireActive ? Math.floor(upgradeFireRate / 3) : upgradeFireRate; if (fireTimer >= currentFireRate) { fireTimer = 0; var bullet = new ViggenBullet(); bullet.x = viggen.x; bullet.y = viggen.y - 60; game.addChild(bullet); viggenBullets.push(bullet); LK.getSound('shoot').play(); } checkCollisions(); cleanupOffscreenEntities(); if (enemyBullets.length > 200) { for (var i = 0; i < 50; i++) { if (enemyBullets[i]) { enemyBullets[i].destroy(); enemyBullets.splice(i, 1); } } } if (samMissiles.length > 100) { for (var i = 0; i < 25; i++) { if (samMissiles[i]) { samMissiles[i].destroy(); samMissiles.splice(i, 1); } } } }; LK.playMusic('combat');
===================================================================
--- original.js
+++ change.js
@@ -61,17 +61,19 @@
}
};
return self;
});
-var Interceptor = Container.expand(function () {
+var MiG23 = Container.expand(function () {
var self = Container.call(this);
- var graphics = self.attachAsset('interceptor', {
+ var graphics = self.attachAsset('mig23', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.fireTimer = 0;
self.fireRate = 60;
+ self.interceptorType = 'mig23';
+ self.scoreValue = 100;
self.update = function () {
var speedMultiplier = currentSSR ? currentSSR.enemySpeedMultiplier : 1.0;
self.y += self.speed * speedMultiplier;
self.fireTimer++;
@@ -190,8 +192,40 @@
});
};
return self;
});
+var Su15 = Container.expand(function () {
+ var self = Container.call(this);
+ var graphics = self.attachAsset('su15', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.speed = 2.5;
+ self.fireTimer = 0;
+ self.fireRate = 45;
+ self.interceptorType = 'su15';
+ self.scoreValue = 150;
+ self.update = function () {
+ var speedMultiplier = currentSSR ? currentSSR.enemySpeedMultiplier : 1.0;
+ self.y += self.speed * speedMultiplier;
+ self.fireTimer++;
+ if (self.fireTimer >= self.fireRate) {
+ self.fireTimer = 0;
+ // Su-15 fires two bullets
+ for (var i = 0; i < 2; i++) {
+ var bullet = new EnemyBullet();
+ bullet.x = self.x + (i === 0 ? -10 : 10);
+ bullet.y = self.y;
+ bullet.targetX = viggen.x;
+ bullet.targetY = viggen.y;
+ bullet.age = 0;
+ game.addChild(bullet);
+ enemyBullets.push(bullet);
+ }
+ }
+ };
+ return self;
+});
var Viggen = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('viggen', {
anchorX: 0.5,
@@ -211,8 +245,41 @@
self.y -= self.speed;
};
return self;
});
+var Yak38 = Container.expand(function () {
+ var self = Container.call(this);
+ var graphics = self.attachAsset('yak38', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.speed = 1.5;
+ self.fireTimer = 0;
+ self.fireRate = 90;
+ self.interceptorType = 'yak38';
+ self.scoreValue = 80;
+ self.sideMovement = 0;
+ self.update = function () {
+ var speedMultiplier = currentSSR ? currentSSR.enemySpeedMultiplier : 1.0;
+ self.y += self.speed * speedMultiplier;
+ // Yak-38 has slight side-to-side movement
+ self.sideMovement += 0.05;
+ self.x += Math.sin(self.sideMovement) * 0.8;
+ self.fireTimer++;
+ if (self.fireTimer >= self.fireRate) {
+ self.fireTimer = 0;
+ var bullet = new EnemyBullet();
+ bullet.x = self.x;
+ bullet.y = self.y;
+ bullet.targetX = viggen.x;
+ bullet.targetY = viggen.y;
+ bullet.age = 0;
+ game.addChild(bullet);
+ enemyBullets.push(bullet);
+ }
+ };
+ return self;
+});
/****
* Initialize Game
****/
@@ -258,23 +325,45 @@
// Enemy Codex System
var codexMenuOpen = false;
var codexMenu = null;
var enemiesEncountered = {
- interceptor: storage.interceptorEncountered || false,
+ mig23: storage.mig23Encountered || false,
+ su15: storage.su15Encountered || false,
+ yak38: storage.yak38Encountered || false,
drone: storage.droneEncountered || false,
samSite: storage.samSiteEncountered || false
};
var enemyCodexData = {
- interceptor: {
- name: 'MiG-23 Interceptor',
- description: 'Fast attack aircraft with guided missiles. Flies straight down and fires tracking bullets.',
+ mig23: {
+ name: 'MiG-23 Flogger',
+ description: 'Standard Soviet interceptor. Fast attack aircraft with guided missiles. Flies straight down and fires single tracking bullets.',
threat: 'Medium',
speed: 'Fast',
fireRate: 'Medium',
health: '1 Hit',
tips: 'Predictable flight path makes them easy targets',
- assetName: 'interceptor'
+ assetName: 'mig23'
},
+ su15: {
+ name: 'Su-15 Flagon',
+ description: 'Heavy interceptor with twin cannons. Faster than MiG-23 and fires dual tracking bullets simultaneously.',
+ threat: 'High',
+ speed: 'Very Fast',
+ fireRate: 'Fast',
+ health: '1 Hit',
+ tips: 'Dual fire pattern - dodge sideways to avoid both bullets',
+ assetName: 'su15'
+ },
+ yak38: {
+ name: 'Yak-38 Forger',
+ description: 'VTOL light interceptor with erratic movement. Slower but unpredictable side-to-side flight pattern.',
+ threat: 'Medium-Low',
+ speed: 'Slow',
+ fireRate: 'Slow',
+ health: '1 Hit',
+ tips: 'Unpredictable movement but slower fire rate. Lead your shots.',
+ assetName: 'yak38'
+ },
drone: {
name: 'Reconnaissance Drone',
description: 'Agile unmanned aircraft with side-to-side movement. Fires tracking bullets while weaving.',
threat: 'High',
@@ -341,8 +430,11 @@
unlockScore: 10000
}
};
var interceptors = [];
+var mig23s = [];
+var su15s = [];
+var yak38s = [];
var samSites = [];
var drones = [];
var enemyBullets = [];
var samMissiles = [];
@@ -432,13 +524,38 @@
// Check vs interceptors
for (var j = interceptors.length - 1; j >= 0; j--) {
if (bullet.intersects(interceptors[j])) {
LK.effects.flashObject(interceptors[j], 0xff0000, 200);
+ var scoreValue = interceptors[j].scoreValue || 100;
+ var interceptorType = interceptors[j].interceptorType;
+ // Remove from specific type array
+ if (interceptorType === 'mig23') {
+ for (var k = mig23s.length - 1; k >= 0; k--) {
+ if (mig23s[k] === interceptors[j]) {
+ mig23s.splice(k, 1);
+ break;
+ }
+ }
+ } else if (interceptorType === 'su15') {
+ for (var k = su15s.length - 1; k >= 0; k--) {
+ if (su15s[k] === interceptors[j]) {
+ su15s.splice(k, 1);
+ break;
+ }
+ }
+ } else if (interceptorType === 'yak38') {
+ for (var k = yak38s.length - 1; k >= 0; k--) {
+ if (yak38s[k] === interceptors[j]) {
+ yak38s.splice(k, 1);
+ break;
+ }
+ }
+ }
interceptors[j].destroy();
interceptors.splice(j, 1);
bullet.destroy();
viggenBullets.splice(i, 1);
- var baseScore = 100 * bullet.damage;
+ var baseScore = scoreValue * bullet.damage;
var ssrMultiplier = currentSSR ? currentSSR.scoreMultiplier : 1.0;
LK.setScore(LK.getScore() + Math.floor(baseScore * ssrMultiplier));
hit = true;
break;
@@ -540,14 +657,27 @@
closeCodexMenu();
}
};
function spawnInterceptor() {
- var interceptor = new Interceptor();
+ var interceptorType = Math.random();
+ var interceptor;
+ if (interceptorType < 0.5) {
+ interceptor = new MiG23();
+ mig23s.push(interceptor);
+ markEnemyEncountered('mig23');
+ } else if (interceptorType < 0.8) {
+ interceptor = new Su15();
+ su15s.push(interceptor);
+ markEnemyEncountered('su15');
+ } else {
+ interceptor = new Yak38();
+ yak38s.push(interceptor);
+ markEnemyEncountered('yak38');
+ }
interceptor.x = Math.random() * 1848 + 100;
interceptor.y = -100;
game.addChild(interceptor);
interceptors.push(interceptor);
- markEnemyEncountered('interceptor');
}
function spawnSAMSite() {
var sam = new SAMSite();
sam.x = Math.random() * 1848 + 100;
@@ -856,9 +986,9 @@
function markEnemyEncountered(enemyType) {
if (!enemiesEncountered[enemyType]) {
enemiesEncountered[enemyType] = true;
// Save to storage
- if (enemyType === 'interceptor') storage.interceptorEncountered = true;else if (enemyType === 'drone') storage.droneEncountered = true;else if (enemyType === 'samSite') storage.samSiteEncountered = true;
+ if (enemyType === 'mig23') storage.mig23Encountered = true;else if (enemyType === 'su15') storage.su15Encountered = true;else if (enemyType === 'yak38') storage.yak38Encountered = true;else if (enemyType === 'drone') storage.droneEncountered = true;else if (enemyType === 'samSite') storage.samSiteEncountered = true;
// Flash effect for discovery
LK.effects.flashScreen(0x00ffff, 200);
}
}
@@ -902,8 +1032,32 @@
}
function cleanupOffscreenEntities() {
for (var i = interceptors.length - 1; i >= 0; i--) {
if (interceptors[i].y > 2800) {
+ var interceptorType = interceptors[i].interceptorType;
+ // Remove from specific type array
+ if (interceptorType === 'mig23') {
+ for (var k = mig23s.length - 1; k >= 0; k--) {
+ if (mig23s[k] === interceptors[i]) {
+ mig23s.splice(k, 1);
+ break;
+ }
+ }
+ } else if (interceptorType === 'su15') {
+ for (var k = su15s.length - 1; k >= 0; k--) {
+ if (su15s[k] === interceptors[i]) {
+ su15s.splice(k, 1);
+ break;
+ }
+ }
+ } else if (interceptorType === 'yak38') {
+ for (var k = yak38s.length - 1; k >= 0; k--) {
+ if (yak38s[k] === interceptors[i]) {
+ yak38s.splice(k, 1);
+ break;
+ }
+ }
+ }
interceptors[i].destroy();
interceptors.splice(i, 1);
}
}
Modern App Store icon, high definition, square with rounded corners, for a game titled "Viggen Strike: Soviet Skies" and with the description "Pilot a Saab 37 Viggen through Soviet airspace in this intense bullet hell game, dodging interceptors, SAMs, and drones in a test of aerial survival.". No text on icon!
A Saab 37 Viggen fighter jet.. In-Game asset. 2d. High contrast. No shadows. Top down view
A surface-to-air (SAM) missile.. In-Game asset. 2d. High contrast. No shadows
A surface-to-air (SAM) missile launcher.. In-Game asset. 2d. High contrast. No shadows. Top down view
A Orion fighter UAV.. In-Game asset. 2d. High contrast. No shadows. Top down view
A Su-15 Flagon heavy Interceptor aircraft.. In-Game asset. 2d. High contrast. No shadows. Top down view