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 Interceptor = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('interceptor', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2; self.fireTimer = 0; self.fireRate = 60; 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 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; }); /**** * 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 }; 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 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); // 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); interceptors[j].destroy(); interceptors.splice(j, 1); bullet.destroy(); viggenBullets.splice(i, 1); var baseScore = 100 * 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(); } }; function spawnInterceptor() { var interceptor = new Interceptor(); 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); } function spawnDrone() { var drone = new Drone(); drone.x = Math.random() * 1848 + 100; drone.y = -50; game.addChild(drone); drones.push(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 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) { 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
@@ -17,10 +17,11 @@
self.direction = 1;
self.fireTimer = 0;
self.fireRate = 45;
self.update = function () {
- self.x += self.speed * self.direction;
- self.y += self.speed * 0.5;
+ 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++;
@@ -53,10 +54,11 @@
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;
+ var speedMultiplier = currentSSR ? currentSSR.enemySpeedMultiplier : 1.0;
+ self.x += dx / distance * self.speed * speedMultiplier;
+ self.y += dy / distance * self.speed * speedMultiplier;
}
};
return self;
});
@@ -69,9 +71,10 @@
self.speed = 2;
self.fireTimer = 0;
self.fireRate = 60;
self.update = function () {
- self.y += self.speed;
+ 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();
@@ -160,8 +163,35 @@
}
};
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 Viggen = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('viggen', {
anchorX: 0.5,
@@ -213,8 +243,65 @@
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
+};
+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 samSites = [];
var drones = [];
var enemyBullets = [];
@@ -259,8 +346,21 @@
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);
+// 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;
@@ -287,9 +387,11 @@
interceptors[j].destroy();
interceptors.splice(j, 1);
bullet.destroy();
viggenBullets.splice(i, 1);
- LK.setScore(LK.getScore() + 100 * bullet.damage);
+ var baseScore = 100 * bullet.damage;
+ var ssrMultiplier = currentSSR ? currentSSR.scoreMultiplier : 1.0;
+ LK.setScore(LK.getScore() + Math.floor(baseScore * ssrMultiplier));
hit = true;
break;
}
}
@@ -301,9 +403,11 @@
drones[j].destroy();
drones.splice(j, 1);
bullet.destroy();
viggenBullets.splice(i, 1);
- LK.setScore(LK.getScore() + 150 * bullet.damage);
+ var baseScore = 150 * bullet.damage;
+ var ssrMultiplier = currentSSR ? currentSSR.scoreMultiplier : 1.0;
+ LK.setScore(LK.getScore() + Math.floor(baseScore * ssrMultiplier));
hit = true;
break;
}
}
@@ -315,9 +419,11 @@
samSites[j].destroy();
samSites.splice(j, 1);
bullet.destroy();
viggenBullets.splice(i, 1);
- LK.setScore(LK.getScore() + 200 * bullet.damage);
+ var baseScore = 200 * bullet.damage;
+ var ssrMultiplier = currentSSR ? currentSSR.scoreMultiplier : 1.0;
+ LK.setScore(LK.getScore() + Math.floor(baseScore * ssrMultiplier));
hit = true;
break;
}
}
@@ -370,8 +476,15 @@
} else {
closeUpgradeMenu();
}
};
+ssrButton.down = function (x, y, obj) {
+ if (!ssrMenuOpen) {
+ openSSRMenu();
+ } else {
+ closeSSRMenu();
+ }
+};
function spawnInterceptor() {
var interceptor = new Interceptor();
interceptor.x = Math.random() * 1848 + 100;
interceptor.y = -100;
@@ -487,8 +600,107 @@
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 checkCollisions() {
if (shieldActive) {
// Shield protects from all damage
return;
@@ -588,9 +800,11 @@
if (difficultyTimer >= 300) {
difficultyTimer = 0;
currentSpawnRate = Math.max(60, currentSpawnRate - 10);
}
- if (spawnTimer >= currentSpawnRate) {
+ // 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();
@@ -599,8 +813,10 @@
} else {
spawnSAMSite();
}
}
+ // Check for SSR unlocks
+ checkSSRUnlocks();
// Spawn powerups occasionally
powerupSpawnTimer++;
if (powerupSpawnTimer >= 900) {
// Every 15 seconds
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