/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Cube = Container.expand(function () { var self = Container.call(this); var cubeGraphics = self.attachAsset('cube', { anchorX: 0.5, anchorY: 0.5 }); // Physics properties self.vy = 0; self.vx = 0; self.gravity = 0.5; self.bounce = 0.4; self.lastY = 0; self.lastX = 0; self.lastWasIntersecting = false; self.update = function () { // Store last position self.lastX = self.x; self.lastY = self.y; // Basic follow player behavior if (sprunki) { // Calculate direction to player var dx = sprunki.x - self.x; var dy = sprunki.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Only follow if within certain range if (distance > 50 && distance < 800) { // Normalize direction and apply a "following" force self.vx += dx / distance * 0.3; self.vy += dy / distance * 0.2; // Show "happy" effect when following player (yellow tint) if (LK.ticks % 60 < 30) { cubeGraphics.tint = 0xFFFF00; } else { cubeGraphics.tint = 0xFFFFFF; } } else { // Reset tint when not actively following cubeGraphics.tint = 0xFFFFFF; } // Apply maximum speed limits so cubes don't move too fast var maxSpeed = 4; if (self.vx > maxSpeed) self.vx = maxSpeed; if (self.vx < -maxSpeed) self.vx = -maxSpeed; if (self.vy > maxSpeed) self.vy = maxSpeed; if (self.vy < -maxSpeed) self.vy = -maxSpeed; } // Apply gravity (reduced when following player) self.vy += self.gravity * 0.7; // Update position self.y += self.vy; self.x += self.vx; // Rotate cube while following (slower when following closely) var rotationSpeed = 0.02; if (sprunki && Math.abs(sprunki.x - self.x) < 200) { rotationSpeed = 0.05; // Spin faster when close to player (excited) } cubeGraphics.rotation += rotationSpeed; // Check boundaries if (self.y > 2732 - 50) { self.y = 2732 - 50; self.vy *= -self.bounce; // Slow down horizontal movement when hitting the ground self.vx *= 0.8; } if (self.x < 50) { self.x = 50; self.vx *= -self.bounce; } else if (self.x > 2048 - 50) { self.x = 2048 - 50; self.vx *= -self.bounce; } }; return self; }); // Create cubes var Goal = Container.expand(function () { var self = Container.call(this); var goalGraphics = self.attachAsset('goal', { anchorX: 0.5, anchorY: 1.0 }); self.lastWasIntersecting = false; self.update = function () { // Waving flag animation goalGraphics.rotation = Math.sin(LK.ticks / 20) * 0.1; }; return self; }); var Hazard = Container.expand(function () { var self = Container.call(this); var hazardGraphics = self.attachAsset('hazard', { anchorX: 0.5, anchorY: 0.5 }); self.lastWasIntersecting = false; return self; }); var HelpButton = Container.expand(function () { var self = Container.call(this); // Create rectangular button using a shape var buttonGraphics = self.attachAsset('platform', { anchorX: 0.5, anchorY: 0.5 }); // Change color to blue for help button buttonGraphics.tint = 0x0000FF; // Make it smaller buttonGraphics.width = 100; buttonGraphics.height = 60; // Add question mark text var questionMark = new Text2('?', { size: 50, fill: 0xFFFFFF }); questionMark.anchor.set(0.5, 0.5); self.addChild(questionMark); // Instructions text based on level self.instructions = ""; // Make button interactive self.interactive = true; // Handle tap on help button self.down = function (x, y, obj) { if (self.instructions) { showInstructions(self.instructions); } }; return self; }); var IntroScreen = Container.expand(function () { var self = Container.call(this); // Create background var bg = LK.getAsset('platform', { anchorX: 0.5, anchorY: 0.5 }); bg.width = 2048; bg.height = 2732; bg.tint = 0x3498db; // Blue background self.addChild(bg); // Create title var title = new Text2('Sprunki:1986', { size: 120, fill: 0xFFFFFF }); title.anchor.set(0.5, 0.5); title.y = -300; self.addChild(title); // Create play button var playButton = LK.getAsset('platform', { anchorX: 0.5, anchorY: 0.5 }); playButton.width = 400; playButton.height = 150; playButton.tint = 0x27ae60; // Green button self.addChild(playButton); // Add text to play button var playText = new Text2('PLAY', { size: 80, fill: 0xFFFFFF }); playText.anchor.set(0.5, 0.5); self.addChild(playText); // Make the button interactive self.interactive = true; // Event handler for tap self.down = function (x, y, obj) { self.visible = false; startGame(); }; return self; }); var Monster = Container.expand(function () { var self = Container.call(this); // Create monster graphics var monsterGraphics = self.attachAsset('hazard', { anchorX: 0.5, anchorY: 0.5 }); // Make it bigger to look more like a monster monsterGraphics.scale.set(2, 2); // Movement properties self.speed = 2; self.direction = 1; // 1 = right, -1 = left self.moveRange = 300; self.startX = 0; self.lastWasIntersecting = false; // Update monster movement self.update = function () { // Move back and forth self.x += self.speed * self.direction; // Change direction at range limits if (Math.abs(self.x - self.startX) > self.moveRange) { self.direction *= -1; } // Visual effect based on direction monsterGraphics.scale.x = 2 * self.direction; }; return self; }); var PauseButton = Container.expand(function () { var self = Container.call(this); // Create button background var pauseButton = self.attachAsset('platform', { anchorX: 0.5, anchorY: 0.5 }); pauseButton.tint = 0x00FF00; // Green color pauseButton.width = 100; pauseButton.height = 60; // Add pause symbol var pauseSymbol = new Text2('||', { size: 40, fill: 0xFFFFFF }); pauseSymbol.anchor.set(0.5, 0.5); self.addChild(pauseSymbol); self.interactive = true; // Handle tap/click on pause button self.down = function (x, y, obj) { // Trigger the game's pause event using LK's event system LK.dispatchEvent('pause'); }; return self; }); // Set game background var Platform = Container.expand(function () { var self = Container.call(this); var platformGraphics = self.attachAsset('platform', { anchorX: 0.5, anchorY: 0.0 }); self.checkCollision = function (sprunki) { if (!sprunki.compressed && sprunki.vy > 0) { // Only check collisions when Sprunki is falling var lastWasAbove = sprunki.lastY + 100 <= self.y; var nowBelow = sprunki.y + 100 > self.y; var horizontalOverlap = Math.abs(sprunki.x - self.x) < platformGraphics.width / 2 + 50; if (lastWasAbove && nowBelow && horizontalOverlap) { sprunki.y = self.y - 100; sprunki.vy *= -self.bounce; sprunki.grounded = true; sprunki.onPlatform = true; LK.getSound('bounce').play(); return true; } } return false; }; return self; }); var Soul = Container.expand(function () { var self = Container.call(this); //{soul-1} // Create soul graphics - using same asset as star for now var soulGraphics = self.attachAsset('star', { anchorX: 0.5, //{soul-2} anchorY: 0.5 //{soul-3} }); //{soul-4} // Make soul purplish to distinguish from regular stars soulGraphics.tint = 0x8A2BE2; // Purple color for souls self.collected = false; self.lastWasIntersecting = false; //{soul-5} // Give souls a different animation than stars self.update = function () { //{soul-6} // Pulse effect for souls var scale = 1 + 0.2 * Math.sin(LK.ticks / 15); soulGraphics.scale.set(scale, scale); // Floating animation with different pattern self.y += Math.cos(LK.ticks / 25) * 0.8; // Slow spin soulGraphics.rotation += 0.01; }; //{soul-7} return self; //{soul-8} }); var Spring = Container.expand(function () { var self = Container.call(this); var springGraphics = self.attachAsset('spring', { anchorX: 0.5, anchorY: 0.5 }); // Spring properties self.power = 30; // High jump power to reach platform 3 self.lastWasIntersecting = false; // Animate the spring self.update = function () { // Subtle bouncing animation springGraphics.scale.y = 1 + Math.sin(LK.ticks / 15) * 0.1; }; return self; }); var Sprunki = Container.expand(function () { var self = Container.call(this); // Create spring character var springGraphics = self.attachAsset('spring', { anchorX: 0.5, anchorY: 0.5 }); // Physics properties self.vx = 0; self.vy = 0; self.gravity = 0.5; self.bounce = 0.7; self.grounded = false; self.onPlatform = false; // Track if player is on a platform self.compressed = false; self.compressHeight = 0; self.maxCompressHeight = 80; self.lastX = 0; self.lastY = 0; self.lastHitGround = false; // Track if player just hit the ground self.lastHitGround = false; // Track if player just hit the ground self.lastWasIntersecting = false; // Swipe detection self.swipeStartY = 0; self.swipeDirection = null; // Switch to compressed spring self.compress = function () { if (self.compressed) return; self.compressed = true; self.removeChild(springGraphics); springGraphics = self.attachAsset('spring_compressed', { anchorX: 0.5, anchorY: 0.5 }); // Animate the compression tween(self.scale, { x: 1.4, y: 0.7 }, { duration: 200, easing: tween.easeOut }); }; // Switch back to normal spring and launch self.release = function () { if (!self.compressed) return; // Calculate launch velocity based on compression time var launchPower = 10 + self.compressHeight / 10; self.compressed = false; self.removeChild(springGraphics); springGraphics = self.attachAsset('spring', { anchorX: 0.5, anchorY: 0.5 }); // Add upward velocity based on compression time self.vy = -launchPower; // Add slight horizontal movement self.vx = Math.random() * 10 - 5; // Animate the spring stretching effect tween(self.scale, { x: 0.7, y: 1.4 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { // Return to normal scale tween(self.scale, { x: 1, y: 1 }, { duration: 200, easing: tween.easeInOut }); } }); // Play bounce sound LK.getSound('bounce').play(); // Reset compression height self.compressHeight = 0; }; // Apply physics and check collisions self.update = function () { // Store last position for collision detection self.lastX = self.x; self.lastY = self.y; if (!self.compressed) { // Check if currently on a platform var onPlatform = false; for (var i = 0; i < platforms.length; i++) { var platform = platforms[i]; if (Math.abs(self.x - platform.x) < platform.children[0].width / 2 + 50 && Math.abs(self.y + 100 - platform.y) < 10) { onPlatform = true; self.onPlatform = true; self.grounded = true; self.vy = 0; break; } } // Reset onPlatform flag if no longer on any platform if (!onPlatform) { self.onPlatform = false; } // Apply gravity if not on platform if (!onPlatform) { self.vy += self.gravity; } // Only move vertically if not on platform or explicitly directed to move vertically if (!onPlatform || self.swipeDirection && (self.swipeDirection.includes("up") || self.swipeDirection.includes("down"))) { self.y += self.vy; } // Horizontal movement removed as per requirements // Screen boundaries if (self.x < 50) { self.x = 50; self.vx *= -0.5; } else if (self.x > 2048 - 50) { self.x = 2048 - 50; self.vx *= -0.5; } // Bottom boundary with bounce if (self.y > 2732 - 50) { self.y = 2732 - 50; self.vy *= -self.bounce; self.grounded = true; LK.getSound('bounce').play(); } else if (!onPlatform) { self.grounded = false; } // Dampen horizontal and diagonal movement self.vx *= 0.98; // Reset swipe direction after movement slows down if (Math.abs(self.vx) < 0.5 && Math.abs(self.vy) < 2) { self.swipeDirection = null; } } }; // User input handlers self.down = function (x, y, obj) { if (self.grounded) { self.compress(); self.compressHeight = 0; } // Track start position for swipe detection self.swipeStartY = y; self.swipeStartX = x; // Track X position for horizontal and diagonal swipes self.swipeDirection = null; // Reset swipe direction // Reset idle timer on any interaction idleStartTime = Date.now(); }; self.up = function (x, y, obj) { if (self.compressed) { self.release(); } // Calculate swipe distance and direction var swipeDist = y - self.swipeStartY; var swipeHorizontal = x - self.swipeStartX; // Detect any swipe if (Math.abs(swipeDist) > 10 || Math.abs(swipeHorizontal) > 10) { // Award points for any swipe LK.setScore(LK.getScore() + 1); // Reset idle timer on swipe idleStartTime = Date.now(); // Horizontal swipe detection for level 3 if (currentLevel === 3 && Math.abs(swipeHorizontal) > Math.abs(swipeDist)) { if (swipeHorizontal < 0) { // Swipe left - collect 100 stars collectStarsBySwipe(1); } else { // Swipe right - collect 700 stars collectStarsBySwipe(7); } } // Vertical swipe detection for levels 1-2 else if (Math.abs(swipeDist) > Math.abs(swipeHorizontal)) { if (swipeDist < 0) { // Swipe up - move player up and collect 3 stars self.vy = -20; // Add strong upward velocity self.swipeDirection = "up"; self.onPlatform = false; // Explicitly leave platform on up swipe collectStarsBySwipe(3); } else { // Swipe down - move player down and collect all stars and go to next level self.vy = 15; // Add downward velocity self.swipeDirection = "down"; self.onPlatform = false; // Explicitly leave platform on down swipe collectStarsBySwipe(100); // A large number to ensure all stars are collected if (currentLevel < 3) { loadLevel(currentLevel + 1); } else { LK.showYouWin(); // Show win after final level } } } } }; return self; }); var Star = Container.expand(function () { var self = Container.call(this); var starGraphics = self.attachAsset('star', { anchorX: 0.5, anchorY: 0.5 }); self.collected = false; self.lastWasIntersecting = false; self.update = function () { // Rotate star for visual effect starGraphics.rotation += 0.02; // Floating animation self.y += Math.sin(LK.ticks / 20) * 0.5; }; return self; }); /**** * Initialize Game ****/ //{soul-9} var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Set game background game.setBackgroundColor(0x87CEEB); // Sky blue background // Initialize idle timer var idleStartTime = Date.now(); // Create intro screen var introScreen = new IntroScreen(); introScreen.x = 2048 / 2; introScreen.y = 2732 / 2; game.addChild(introScreen); // Create Sprunki instance but don't add it to the game yet var sprunki = new Sprunki(); sprunki.x = 200; sprunki.y = 200; // Game elements initialized but not added until game starts var gameStarted = false; // Function to start the game function startGame() { if (!gameStarted) { gameStarted = true; game.addChild(sprunki); loadLevel(1); } } // Create game elements // Create platforms var platforms = []; function createPlatform(x, y) { var platform = new Platform(); platform.x = x; platform.y = y; platforms.push(platform); game.addChild(platform); return platform; } // Create monsters var monsters = []; function createMonster(x, y, range) { var monster = new Monster(); monster.x = x; monster.y = y; monster.startX = x; monster.moveRange = range || 300; monsters.push(monster); game.addChild(monster); return monster; } // Create hazards var hazards = []; function createHazard(x, y) { var hazard = new Hazard(); hazard.x = x; hazard.y = y; hazards.push(hazard); game.addChild(hazard); return hazard; } // Create stars var stars = []; function createStar(x, y) { var star = new Star(); star.x = x; star.y = y; stars.push(star); game.addChild(star); return star; } // Create souls var souls = []; function createSoul(x, y) { var soul = new Soul(); soul.x = x; soul.y = y; souls.push(soul); game.addChild(soul); return soul; } // Create goal variable var goal = null; // Create score display and level tracking var currentLevel = 1; var starCount = 0; var maxStars = 0; // Create the scoreTxt before loadLevel is called var scoreTxt = new Text2('Level: 1 - Stars: 0/0', { size: 70, fill: 0xFFFFFF }); // Function to load level based on current level number function loadLevel(levelNumber) { if (!gameStarted) return; // Clear existing game elements clearLevel(); // Set current level currentLevel = levelNumber; // Reset star count starCount = 0; // Add grass to the bottom var grass = LK.getAsset('grass', { anchorX: 0.5, anchorY: 0.0 }); grass.x = 2048 / 2; grass.y = 2732 - 100; game.addChild(grass); if (levelNumber === 1) { // Level 1 setup createPlatform(500, 800); // Platform 1 createPlatform(1200, 1000); // Platform 2 // Platform 3 removed per request createPlatform(400, 1800); createPlatform(1400, 1600); createPlatform(1000, 2200); createPlatform(600, 2500); createStar(500, 700); createStar(1200, 900); // Star on platform 3 removed createStar(400, 1700); createStar(1400, 1500); createHazard(700, 1200); createHazard(1100, 1800); createHazard(500, 2400); createMonster(500, 750, 200); createMonster(1200, 950, 400); createMonster(800, 1350, 300); // Add only one cube in the grass createCube(1024, 2632); // Position it in the grass (bottom of screen) // Springs removed per request // Create goal goal = new Goal(); goal.x = 600; goal.y = 2500; game.addChild(goal); // Create help button var helpButton = new HelpButton(); helpButton.x = 150; helpButton.y = 2600; helpButton.instructions = "Level 1: Basics\n\n" + "• Tap and release Sprunki to jump\n" + "• Hold longer for higher jumps\n" + "• Collect stars to increase your score\n" + "• Avoid red hazards and monsters\n" + "• Swipe UP to collect 3 stars at once\n" + "• Swipe DOWN to collect all stars and advance to next level\n" + "• Reach the red flag to complete the level"; game.addChild(helpButton); // Add pause button at the top right var pauseButton = new PauseButton(); pauseButton.x = 2048 - 150; pauseButton.y = 150; game.addChild(pauseButton); } else if (levelNumber === 2) { // Level 2 setup - more challenging createPlatform(400, 600); createPlatform(900, 500); createPlatform(1500, 700); createPlatform(1800, 1100); createPlatform(1300, 1400); createPlatform(800, 1600); createPlatform(400, 1900); createPlatform(1000, 2200); createPlatform(1600, 2400); createStar(400, 500); createStar(900, 400); createStar(1500, 600); createStar(1800, 1000); createStar(1300, 1300); createStar(800, 1500); createStar(400, 1800); createHazard(600, 800); createHazard(1200, 1000); createHazard(1500, 1800); createHazard(900, 2000); // More monsters in level 2 createMonster(400, 550, 150); createMonster(900, 450, 300); createMonster(1500, 650, 200); createMonster(1300, 1350, 250); createMonster(800, 1550, 200); // Add only one cube in the grass createCube(1024, 2632); // Position it in the grass (bottom of screen) // Create goal goal = new Goal(); goal.x = 1600; goal.y = 2400; game.addChild(goal); // Create help button for level 2 var helpButton = new HelpButton(); helpButton.x = 150; helpButton.y = 2600; helpButton.instructions = "Level 2: Challenge\n\n" + "• Platforms are more challenging\n" + "• More monsters to avoid\n" + "• Use swipe UP to collect 3 stars at once\n" + "• Use swipe DOWN to collect all stars and advance\n" + "• Time your jumps carefully to avoid monsters\n" + "• Stay on platforms to avoid falling"; game.addChild(helpButton); // Add pause button at the top right var pauseButton = new PauseButton(); pauseButton.x = 2048 - 150; pauseButton.y = 150; game.addChild(pauseButton); } else if (levelNumber === 3) { // Level 3 setup - with 800 stars to collect // Create platforms in a challenging pattern createPlatform(300, 600); createPlatform(800, 500); createPlatform(1300, 700); createPlatform(1800, 900); createPlatform(1500, 1200); createPlatform(1000, 1400); createPlatform(500, 1600); createPlatform(1200, 1800); createPlatform(1700, 2000); createPlatform(900, 2200); createPlatform(400, 2400); // Create 800 stars (we'll add 8 stars worth 100 points each) // Stars for left swipe (100 points each) createStar(300, 500); createStar(500, 1500); createStar(400, 2300); createStar(900, 2100); // Stars for right swipe (700 points each - will be collected in the swipe handler) createStar(800, 400); createStar(1300, 600); createStar(1500, 1100); createStar(1700, 1900); // Add hazards createHazard(600, 900); createHazard(1200, 1600); createHazard(800, 2000); createHazard(1500, 2200); // Add monsters with different movement patterns createMonster(300, 550, 200); createMonster(800, 450, 300); createMonster(1300, 650, 250); createMonster(1000, 1350, 300); createMonster(500, 1550, 200); createMonster(1200, 1750, 250); createMonster(1700, 1950, 200); createMonster(900, 2150, 300); // Add additional monster on one of the platforms createMonster(400, 2350, 150); // Monster on bottom platform // Add only one cube in the grass createCube(1024, 2632); // Position it in the grass (bottom of screen) // Create goal goal = new Goal(); goal.x = 400; goal.y = 2400; game.addChild(goal); // Create help button for level 3 var helpButton = new HelpButton(); helpButton.x = 150; helpButton.y = 2600; helpButton.instructions = "Level 3: Master Level\n\n" + "• This level has 800 stars to collect\n" + "• Swipe LEFT to collect 100 stars at once\n" + "• Swipe RIGHT to collect 700 stars at once\n" + "• Watch out for the increased number of monsters\n" + "• Navigate the complex platform arrangement\n" + "• Reach the flag to complete the game"; game.addChild(helpButton); // Add pause button at the top right var pauseButton = new PauseButton(); pauseButton.x = 2048 - 150; pauseButton.y = 150; game.addChild(pauseButton); } // Reset position sprunki.x = 200; sprunki.y = 200; sprunki.vx = 0; sprunki.vy = 0; // Update max stars count maxStars = stars.length; scoreTxt.setText('Level: ' + currentLevel + ' - Stars: ' + starCount + '/' + maxStars); } // Function to clear existing level elements function clearLevel() { // Remove all platforms for (var i = platforms.length - 1; i >= 0; i--) { game.removeChild(platforms[i]); platforms.splice(i, 1); } // Remove all stars for (var i = stars.length - 1; i >= 0; i--) { game.removeChild(stars[i]); stars.splice(i, 1); } // Remove all souls in celebration level for (var i = souls.length - 1; i >= 0; i--) { game.removeChild(souls[i]); souls.splice(i, 1); } // Remove all cubes for (var i = cubes.length - 1; i >= 0; i--) { game.removeChild(cubes[i]); cubes.splice(i, 1); } // Remove all springs for (var i = springs.length - 1; i >= 0; i--) { game.removeChild(springs[i]); springs.splice(i, 1); } // Remove all hazards for (var i = hazards.length - 1; i >= 0; i--) { game.removeChild(hazards[i]); hazards.splice(i, 1); } // Remove all monsters for (var i = monsters.length - 1; i >= 0; i--) { game.removeChild(monsters[i]); monsters.splice(i, 1); } // Remove goal if it exists if (goal) { game.removeChild(goal); goal = null; } } // Function to collect stars by swipe function collectStarsBySwipe(count) { var collected = 0; for (var i = 0; i < stars.length && collected < count; i++) { if (!stars[i].collected) { stars[i].collected = true; stars[i].visible = false; starCount++; LK.getSound('collect').play(); LK.setScore(LK.getScore() + 100); collected++; } } scoreTxt.setText('Level: ' + currentLevel + ' - Stars: ' + starCount + '/' + maxStars); } scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Don't load initial level until play button is pressed // Initial score setup scoreTxt.setText('Level: 1 - Stars: 0/0'); // Function to show instructions function showInstructions(text) { // Create instruction popup var popup = new Container(); // Background for popup var bg = LK.getAsset('platform', { anchorX: 0.5, anchorY: 0.5 }); bg.tint = 0x000080; bg.width = 1400; bg.height = 800; popup.addChild(bg); // Instruction text var instructionText = new Text2(text, { size: 50, fill: 0xFFFFFF, wordWrap: true, wordWrapWidth: 1300 }); instructionText.anchor.set(0.5, 0.5); popup.addChild(instructionText); // Close button text var closeText = new Text2("TAP ANYWHERE TO CLOSE", { size: 40, fill: 0xFFFF00 }); closeText.anchor.set(0.5, 0.5); closeText.y = 300; popup.addChild(closeText); // Position popup in center of screen popup.x = 2048 / 2; popup.y = 2732 / 2; // Add to game game.addChild(popup); // Close on tap popup.interactive = true; popup.down = function () { game.removeChild(popup); }; } // Handle user interaction game.down = function (x, y, obj) { sprunki.down(x, y, obj); }; game.up = function (x, y, obj) { sprunki.up(x, y, obj); }; // Compress indicator game.move = function (x, y, obj) { if (sprunki.compressed) { sprunki.compressHeight = Math.min(sprunki.compressHeight + 2, sprunki.maxCompressHeight); // Visual feedback for compression var lastSwipeTime = 0; var compressionScale = 1 + sprunki.compressHeight / 200; sprunki.scale.set(compressionScale, 1 / compressionScale); } }; // Main game loop game.update = function () { // Update Sprunki sprunki.update(); // Check if player has been idle for more than 5 seconds var currentTime = Date.now(); if (currentTime - idleStartTime > 5000) { // Award 1 million points for being idle LK.setScore(LK.getScore() + 1000000); // Play collect sound for feedback LK.getSound('collect').play(); // Reset idle timer idleStartTime = currentTime; // Visual effect for massive points LK.effects.flashScreen(0xFFD700, 500); // Gold flash } // Update game elements for (var i = 0; i < stars.length; i++) { if (!stars[i].collected) { stars[i].update(); // Check star collection if (!stars[i].lastWasIntersecting && sprunki.intersects(stars[i])) { stars[i].collected = true; stars[i].visible = false; starCount++; scoreTxt.setText('Level: ' + currentLevel + ' - Stars: ' + starCount + '/' + maxStars); LK.getSound('collect').play(); LK.setScore(LK.getScore() + 100); } stars[i].lastWasIntersecting = sprunki.intersects(stars[i]); } } // Update souls in celebration level if (currentLevel === 4) { for (var i = 0; i < souls.length; i++) { if (!souls[i].collected) { souls[i].update(); // Check soul collection if (!souls[i].lastWasIntersecting && sprunki.intersects(souls[i])) { souls[i].collected = true; souls[i].visible = false; starCount++; scoreTxt.setText('CELEBRATION - Souls: ' + starCount + '/' + maxStars + ' - Score: ' + LK.getScore()); LK.getSound('collect').play(); LK.setScore(LK.getScore() + 500); // Souls are worth more points // Flash the screen with a purple color when collecting a soul LK.effects.flashScreen(0x8A2BE2, 300); } souls[i].lastWasIntersecting = sprunki.intersects(souls[i]); } } } // Update score text to show total score including glitch points scoreTxt.setText('Level: ' + currentLevel + ' - Stars: ' + starCount + '/' + maxStars + ' - Score: ' + LK.getScore()); // Update goal if (goal) { goal.update(); } // Check if reached goal if (goal && !goal.lastWasIntersecting && sprunki.intersects(goal)) { // Level complete! if (currentLevel < 3) { // Go to next level loadLevel(currentLevel + 1); } else { // Final level completed - show celebration level loadCelebrationLevel(); } } // Load a celebration level with souls instead of stars function loadCelebrationLevel() { // Clear existing level elements clearLevel(); // Set special celebration level number currentLevel = 4; // Create a background platform for the celebration var celebrationPlatform = LK.getAsset('platform', { anchorX: 0.5, anchorY: 0.5 }); celebrationPlatform.width = 2048; celebrationPlatform.height = 2732; celebrationPlatform.tint = 0x191970; // Midnight blue for celebration background game.addChild(celebrationPlatform); // Create a pattern of souls around the screen var soulCount = 30; // Number of souls to create // Create souls in a spiral pattern for (var i = 0; i < soulCount; i++) { var angle = 0.5 * i; var radius = 100 + i * 20; var x = 2048 / 2 + radius * Math.cos(angle); var y = 2732 / 2 + radius * Math.sin(angle); createSoul(x, y); } // Create a congratulatory message var congratsText = new Text2('CONGRATULATIONS!', { size: 120, fill: 0xFFD700 // Gold text }); congratsText.anchor.set(0.5, 0.5); congratsText.x = 2048 / 2; congratsText.y = 500; game.addChild(congratsText); // Create subtitle var subtitleText = new Text2('You\'ve collected all the missing souls!', { size: 70, fill: 0xFFFFFF }); subtitleText.anchor.set(0.5, 0.5); subtitleText.x = 2048 / 2; subtitleText.y = 600; game.addChild(subtitleText); // Add celebration text about cube family var cubeText = new Text2('Your cube children are so proud of you!', { size: 60, fill: 0xFFD700 // Gold text }); cubeText.anchor.set(0.5, 0.5); cubeText.x = 2048 / 2; cubeText.y = 700; game.addChild(cubeText); // Add only one cube in the grass for celebration level createCube(1024, 2632); // Position it in the grass (bottom of screen) // Create a "Continue" button var continueButton = LK.getAsset('platform', { anchorX: 0.5, anchorY: 0.5 }); continueButton.width = 500; continueButton.height = 150; continueButton.tint = 0x4B0082; // Indigo for button continueButton.x = 2048 / 2; continueButton.y = 2000; game.addChild(continueButton); // Add text to button var continueText = new Text2('CONTINUE', { size: 80, fill: 0xFFFFFF }); continueText.anchor.set(0.5, 0.5); continueText.x = 2048 / 2; continueText.y = 2000; game.addChild(continueText); // Make button interactive continueButton.interactive = true; continueButton.down = function () { // Show win screen when button is pressed LK.showYouWin(); }; // Update score text scoreTxt.setText('CELEBRATION - Souls: 0/' + souls.length + ' - Score: ' + LK.getScore()); // Reset sprunki position sprunki.x = 2048 / 2; sprunki.y = 1500; sprunki.vx = 0; sprunki.vy = 0; // Make sprunki jump higher in celebration level sprunki.gravity = 0.3; sprunki.bounce = 0.9; // Set max souls maxStars = souls.length; } if (goal) { goal.lastWasIntersecting = sprunki.intersects(goal); } // Check platform collisions for (var i = 0; i < platforms.length; i++) { platforms[i].checkCollision(sprunki); } // Check spring collisions for (var i = 0; i < springs.length; i++) { springs[i].update(); // Check if player is touching spring if (!springs[i].lastWasIntersecting && sprunki.intersects(springs[i])) { // Launch player upward with strong force to reach platform 3 sprunki.vy = -springs[i].power; // Add slight horizontal velocity toward platform 3 sprunki.vx = (800 - sprunki.x) / 20; // Visual feedback LK.effects.flashObject(springs[i], 0x00FF00, 500); // Play bounce sound LK.getSound('bounce').play(); // Animation effect using tween tween(springs[i].scale, { x: 1.5, y: 0.5 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(springs[i].scale, { x: 1.0, y: 1.0 }, { duration: 300, easing: tween.elasticOut }); } }); } springs[i].lastWasIntersecting = sprunki.intersects(springs[i]); } // Check hazard collisions for (var i = 0; i < hazards.length; i++) { if (!hazards[i].lastWasIntersecting && sprunki.intersects(hazards[i])) { // Game over when hitting hazard LK.effects.flashScreen(0xFF0000, 1000); LK.showGameOver(); } hazards[i].lastWasIntersecting = sprunki.intersects(hazards[i]); } // Update and check monster collisions for (var i = 0; i < monsters.length; i++) { // Update monster movement monsters[i].update(); // Check collision with sprunki if (!monsters[i].lastWasIntersecting && sprunki.intersects(monsters[i])) { // Game over when colliding with monster LK.effects.flashScreen(0xFF0000, 1000); LK.showGameOver(); } monsters[i].lastWasIntersecting = sprunki.intersects(monsters[i]); } // Update cubes and check collisions for (var i = 0; i < cubes.length; i++) { // Safety check if the cube exists if (!cubes[i]) continue; cubes[i].update(); // Check if cube hits Sprunki if (cubes[i] && !cubes[i].lastWasIntersecting && sprunki.intersects(cubes[i])) { // Create "happy" effect - cubes are happy to be near their "father" // Store reference to the current cube to avoid closure issues with i changing var currentCube = cubes[i]; // Make sure the cube still exists and has a scale property before tweening if (currentCube && currentCube.scale) { tween(currentCube.scale, { x: 1.5, y: 1.5 }, { duration: 200, easing: tween.elasticOut, onFinish: function onFinish() { // Ensure currentCube still exists when callback happens if (currentCube && currentCube.scale && cubes.indexOf(currentCube) !== -1) { tween(currentCube.scale, { x: 1.0, y: 1.0 }, { duration: 300, easing: tween.easeOut }); } } }); } // Bounce Sprunki gentler when hit by a cube (family is gentle) sprunki.vy = -8; sprunki.vx = (sprunki.x - cubes[i].x) / 8; // Make cube bounce away happily cubes[i].vy = -6; cubes[i].vx = (cubes[i].x - sprunki.x) / 6; // Add points for cube interaction (family reunion) LK.setScore(LK.getScore() + 100); // Visual effect for cube collision - happy yellow glow LK.effects.flashObject(cubes[i], 0xFFFF00, 500); } cubes[i].lastWasIntersecting = sprunki.intersects(cubes[i]); // Create a "family" formation when multiple cubes are close to each other var nearbyCount = 0; for (var j = 0; j < cubes.length; j++) { if (i !== j && cubes[j]) { var distance = Math.sqrt(Math.pow(cubes[i].x - cubes[j].x, 2) + Math.pow(cubes[i].y - cubes[j].y, 2)); if (distance < 150) { nearbyCount++; } } } // If many cubes are together, make them glow occasionally (family gathering) if (nearbyCount >= 2 && LK.ticks % 120 < 20 && cubes[i].children && cubes[i].children[0]) { cubes[i].children[0].tint = 0xFFA500; // Orange glow } // Also check if cube hits platforms for (var j = 0; j < platforms.length; j++) { var platform = platforms[j]; var lastWasAbove = cubes[i].lastY + 50 <= platform.y; var nowBelow = cubes[i].y + 50 > platform.y; var horizontalOverlap = Math.abs(cubes[i].x - platform.x) < platform.children[0].width / 2 + 50; if (lastWasAbove && nowBelow && horizontalOverlap) { cubes[i].y = platform.y - 50; cubes[i].vy *= -0.6; // Add small random horizontal movement on bounce cubes[i].vx += (Math.random() - 0.5) * 2; } } } // Player glitches back and gets points if they hit the ground if (sprunki.y > 2732 - 50 && !sprunki.lastHitGround) { // Glitch player back to a random platform if (platforms.length > 0) { var randomPlatform = platforms[Math.floor(Math.random() * platforms.length)]; sprunki.x = randomPlatform.x; sprunki.y = randomPlatform.y - 100; // Position above the platform sprunki.vy = 0; sprunki.vx = 0; // Award points for the glitch LK.setScore(LK.getScore() + 200); // Update score display when glitching back scoreTxt.setText('Level: ' + currentLevel + ' - Stars: ' + starCount + '/' + maxStars + ' - Score: ' + LK.getScore()); // Visual effect for glitch LK.effects.flashObject(sprunki, 0x00FFFF, 500); // Set flag to prevent continuous glitching sprunki.lastHitGround = true; } } else if (sprunki.y < 2732 - 100) { // Reset the ground hit flag when player is away from the ground sprunki.lastHitGround = false; } // Visual feedback for compression if (sprunki.compressed) { var scale = 1 + sprunki.compressHeight / 200; sprunki.scale.set(scale, 1 / scale); } else { sprunki.scale.set(1, 1); } // Cubes no longer falling from the sky per request }; // Create cubes var cubes = []; function createCube(x, y) { var cube = new Cube(); cube.x = x; cube.y = y; // Add some random initial velocity cube.vx = Math.random() * 4 - 2; cube.vy = Math.random() * 2; cubes.push(cube); game.addChild(cube); return cube; } // Create springs array and function var springs = []; function createSpring(x, y) { var spring = new Spring(); spring.x = x; spring.y = y; springs.push(spring); game.addChild(spring); return spring; }
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Cube = Container.expand(function () {
var self = Container.call(this);
var cubeGraphics = self.attachAsset('cube', {
anchorX: 0.5,
anchorY: 0.5
});
// Physics properties
self.vy = 0;
self.vx = 0;
self.gravity = 0.5;
self.bounce = 0.4;
self.lastY = 0;
self.lastX = 0;
self.lastWasIntersecting = false;
self.update = function () {
// Store last position
self.lastX = self.x;
self.lastY = self.y;
// Basic follow player behavior
if (sprunki) {
// Calculate direction to player
var dx = sprunki.x - self.x;
var dy = sprunki.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Only follow if within certain range
if (distance > 50 && distance < 800) {
// Normalize direction and apply a "following" force
self.vx += dx / distance * 0.3;
self.vy += dy / distance * 0.2;
// Show "happy" effect when following player (yellow tint)
if (LK.ticks % 60 < 30) {
cubeGraphics.tint = 0xFFFF00;
} else {
cubeGraphics.tint = 0xFFFFFF;
}
} else {
// Reset tint when not actively following
cubeGraphics.tint = 0xFFFFFF;
}
// Apply maximum speed limits so cubes don't move too fast
var maxSpeed = 4;
if (self.vx > maxSpeed) self.vx = maxSpeed;
if (self.vx < -maxSpeed) self.vx = -maxSpeed;
if (self.vy > maxSpeed) self.vy = maxSpeed;
if (self.vy < -maxSpeed) self.vy = -maxSpeed;
}
// Apply gravity (reduced when following player)
self.vy += self.gravity * 0.7;
// Update position
self.y += self.vy;
self.x += self.vx;
// Rotate cube while following (slower when following closely)
var rotationSpeed = 0.02;
if (sprunki && Math.abs(sprunki.x - self.x) < 200) {
rotationSpeed = 0.05; // Spin faster when close to player (excited)
}
cubeGraphics.rotation += rotationSpeed;
// Check boundaries
if (self.y > 2732 - 50) {
self.y = 2732 - 50;
self.vy *= -self.bounce;
// Slow down horizontal movement when hitting the ground
self.vx *= 0.8;
}
if (self.x < 50) {
self.x = 50;
self.vx *= -self.bounce;
} else if (self.x > 2048 - 50) {
self.x = 2048 - 50;
self.vx *= -self.bounce;
}
};
return self;
});
// Create cubes
var Goal = Container.expand(function () {
var self = Container.call(this);
var goalGraphics = self.attachAsset('goal', {
anchorX: 0.5,
anchorY: 1.0
});
self.lastWasIntersecting = false;
self.update = function () {
// Waving flag animation
goalGraphics.rotation = Math.sin(LK.ticks / 20) * 0.1;
};
return self;
});
var Hazard = Container.expand(function () {
var self = Container.call(this);
var hazardGraphics = self.attachAsset('hazard', {
anchorX: 0.5,
anchorY: 0.5
});
self.lastWasIntersecting = false;
return self;
});
var HelpButton = Container.expand(function () {
var self = Container.call(this);
// Create rectangular button using a shape
var buttonGraphics = self.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.5
});
// Change color to blue for help button
buttonGraphics.tint = 0x0000FF;
// Make it smaller
buttonGraphics.width = 100;
buttonGraphics.height = 60;
// Add question mark text
var questionMark = new Text2('?', {
size: 50,
fill: 0xFFFFFF
});
questionMark.anchor.set(0.5, 0.5);
self.addChild(questionMark);
// Instructions text based on level
self.instructions = "";
// Make button interactive
self.interactive = true;
// Handle tap on help button
self.down = function (x, y, obj) {
if (self.instructions) {
showInstructions(self.instructions);
}
};
return self;
});
var IntroScreen = Container.expand(function () {
var self = Container.call(this);
// Create background
var bg = LK.getAsset('platform', {
anchorX: 0.5,
anchorY: 0.5
});
bg.width = 2048;
bg.height = 2732;
bg.tint = 0x3498db; // Blue background
self.addChild(bg);
// Create title
var title = new Text2('Sprunki:1986', {
size: 120,
fill: 0xFFFFFF
});
title.anchor.set(0.5, 0.5);
title.y = -300;
self.addChild(title);
// Create play button
var playButton = LK.getAsset('platform', {
anchorX: 0.5,
anchorY: 0.5
});
playButton.width = 400;
playButton.height = 150;
playButton.tint = 0x27ae60; // Green button
self.addChild(playButton);
// Add text to play button
var playText = new Text2('PLAY', {
size: 80,
fill: 0xFFFFFF
});
playText.anchor.set(0.5, 0.5);
self.addChild(playText);
// Make the button interactive
self.interactive = true;
// Event handler for tap
self.down = function (x, y, obj) {
self.visible = false;
startGame();
};
return self;
});
var Monster = Container.expand(function () {
var self = Container.call(this);
// Create monster graphics
var monsterGraphics = self.attachAsset('hazard', {
anchorX: 0.5,
anchorY: 0.5
});
// Make it bigger to look more like a monster
monsterGraphics.scale.set(2, 2);
// Movement properties
self.speed = 2;
self.direction = 1; // 1 = right, -1 = left
self.moveRange = 300;
self.startX = 0;
self.lastWasIntersecting = false;
// Update monster movement
self.update = function () {
// Move back and forth
self.x += self.speed * self.direction;
// Change direction at range limits
if (Math.abs(self.x - self.startX) > self.moveRange) {
self.direction *= -1;
}
// Visual effect based on direction
monsterGraphics.scale.x = 2 * self.direction;
};
return self;
});
var PauseButton = Container.expand(function () {
var self = Container.call(this);
// Create button background
var pauseButton = self.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.5
});
pauseButton.tint = 0x00FF00; // Green color
pauseButton.width = 100;
pauseButton.height = 60;
// Add pause symbol
var pauseSymbol = new Text2('||', {
size: 40,
fill: 0xFFFFFF
});
pauseSymbol.anchor.set(0.5, 0.5);
self.addChild(pauseSymbol);
self.interactive = true;
// Handle tap/click on pause button
self.down = function (x, y, obj) {
// Trigger the game's pause event using LK's event system
LK.dispatchEvent('pause');
};
return self;
});
// Set game background
var Platform = Container.expand(function () {
var self = Container.call(this);
var platformGraphics = self.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.0
});
self.checkCollision = function (sprunki) {
if (!sprunki.compressed && sprunki.vy > 0) {
// Only check collisions when Sprunki is falling
var lastWasAbove = sprunki.lastY + 100 <= self.y;
var nowBelow = sprunki.y + 100 > self.y;
var horizontalOverlap = Math.abs(sprunki.x - self.x) < platformGraphics.width / 2 + 50;
if (lastWasAbove && nowBelow && horizontalOverlap) {
sprunki.y = self.y - 100;
sprunki.vy *= -self.bounce;
sprunki.grounded = true;
sprunki.onPlatform = true;
LK.getSound('bounce').play();
return true;
}
}
return false;
};
return self;
});
var Soul = Container.expand(function () {
var self = Container.call(this);
//{soul-1}
// Create soul graphics - using same asset as star for now
var soulGraphics = self.attachAsset('star', {
anchorX: 0.5,
//{soul-2}
anchorY: 0.5 //{soul-3}
}); //{soul-4}
// Make soul purplish to distinguish from regular stars
soulGraphics.tint = 0x8A2BE2; // Purple color for souls
self.collected = false;
self.lastWasIntersecting = false; //{soul-5}
// Give souls a different animation than stars
self.update = function () {
//{soul-6}
// Pulse effect for souls
var scale = 1 + 0.2 * Math.sin(LK.ticks / 15);
soulGraphics.scale.set(scale, scale);
// Floating animation with different pattern
self.y += Math.cos(LK.ticks / 25) * 0.8;
// Slow spin
soulGraphics.rotation += 0.01;
}; //{soul-7}
return self; //{soul-8}
});
var Spring = Container.expand(function () {
var self = Container.call(this);
var springGraphics = self.attachAsset('spring', {
anchorX: 0.5,
anchorY: 0.5
});
// Spring properties
self.power = 30; // High jump power to reach platform 3
self.lastWasIntersecting = false;
// Animate the spring
self.update = function () {
// Subtle bouncing animation
springGraphics.scale.y = 1 + Math.sin(LK.ticks / 15) * 0.1;
};
return self;
});
var Sprunki = Container.expand(function () {
var self = Container.call(this);
// Create spring character
var springGraphics = self.attachAsset('spring', {
anchorX: 0.5,
anchorY: 0.5
});
// Physics properties
self.vx = 0;
self.vy = 0;
self.gravity = 0.5;
self.bounce = 0.7;
self.grounded = false;
self.onPlatform = false; // Track if player is on a platform
self.compressed = false;
self.compressHeight = 0;
self.maxCompressHeight = 80;
self.lastX = 0;
self.lastY = 0;
self.lastHitGround = false; // Track if player just hit the ground
self.lastHitGround = false; // Track if player just hit the ground
self.lastWasIntersecting = false;
// Swipe detection
self.swipeStartY = 0;
self.swipeDirection = null;
// Switch to compressed spring
self.compress = function () {
if (self.compressed) return;
self.compressed = true;
self.removeChild(springGraphics);
springGraphics = self.attachAsset('spring_compressed', {
anchorX: 0.5,
anchorY: 0.5
});
// Animate the compression
tween(self.scale, {
x: 1.4,
y: 0.7
}, {
duration: 200,
easing: tween.easeOut
});
};
// Switch back to normal spring and launch
self.release = function () {
if (!self.compressed) return;
// Calculate launch velocity based on compression time
var launchPower = 10 + self.compressHeight / 10;
self.compressed = false;
self.removeChild(springGraphics);
springGraphics = self.attachAsset('spring', {
anchorX: 0.5,
anchorY: 0.5
});
// Add upward velocity based on compression time
self.vy = -launchPower;
// Add slight horizontal movement
self.vx = Math.random() * 10 - 5;
// Animate the spring stretching effect
tween(self.scale, {
x: 0.7,
y: 1.4
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Return to normal scale
tween(self.scale, {
x: 1,
y: 1
}, {
duration: 200,
easing: tween.easeInOut
});
}
});
// Play bounce sound
LK.getSound('bounce').play();
// Reset compression height
self.compressHeight = 0;
};
// Apply physics and check collisions
self.update = function () {
// Store last position for collision detection
self.lastX = self.x;
self.lastY = self.y;
if (!self.compressed) {
// Check if currently on a platform
var onPlatform = false;
for (var i = 0; i < platforms.length; i++) {
var platform = platforms[i];
if (Math.abs(self.x - platform.x) < platform.children[0].width / 2 + 50 && Math.abs(self.y + 100 - platform.y) < 10) {
onPlatform = true;
self.onPlatform = true;
self.grounded = true;
self.vy = 0;
break;
}
}
// Reset onPlatform flag if no longer on any platform
if (!onPlatform) {
self.onPlatform = false;
}
// Apply gravity if not on platform
if (!onPlatform) {
self.vy += self.gravity;
}
// Only move vertically if not on platform or explicitly directed to move vertically
if (!onPlatform || self.swipeDirection && (self.swipeDirection.includes("up") || self.swipeDirection.includes("down"))) {
self.y += self.vy;
}
// Horizontal movement removed as per requirements
// Screen boundaries
if (self.x < 50) {
self.x = 50;
self.vx *= -0.5;
} else if (self.x > 2048 - 50) {
self.x = 2048 - 50;
self.vx *= -0.5;
}
// Bottom boundary with bounce
if (self.y > 2732 - 50) {
self.y = 2732 - 50;
self.vy *= -self.bounce;
self.grounded = true;
LK.getSound('bounce').play();
} else if (!onPlatform) {
self.grounded = false;
}
// Dampen horizontal and diagonal movement
self.vx *= 0.98;
// Reset swipe direction after movement slows down
if (Math.abs(self.vx) < 0.5 && Math.abs(self.vy) < 2) {
self.swipeDirection = null;
}
}
};
// User input handlers
self.down = function (x, y, obj) {
if (self.grounded) {
self.compress();
self.compressHeight = 0;
}
// Track start position for swipe detection
self.swipeStartY = y;
self.swipeStartX = x; // Track X position for horizontal and diagonal swipes
self.swipeDirection = null; // Reset swipe direction
// Reset idle timer on any interaction
idleStartTime = Date.now();
};
self.up = function (x, y, obj) {
if (self.compressed) {
self.release();
}
// Calculate swipe distance and direction
var swipeDist = y - self.swipeStartY;
var swipeHorizontal = x - self.swipeStartX;
// Detect any swipe
if (Math.abs(swipeDist) > 10 || Math.abs(swipeHorizontal) > 10) {
// Award points for any swipe
LK.setScore(LK.getScore() + 1);
// Reset idle timer on swipe
idleStartTime = Date.now();
// Horizontal swipe detection for level 3
if (currentLevel === 3 && Math.abs(swipeHorizontal) > Math.abs(swipeDist)) {
if (swipeHorizontal < 0) {
// Swipe left - collect 100 stars
collectStarsBySwipe(1);
} else {
// Swipe right - collect 700 stars
collectStarsBySwipe(7);
}
}
// Vertical swipe detection for levels 1-2
else if (Math.abs(swipeDist) > Math.abs(swipeHorizontal)) {
if (swipeDist < 0) {
// Swipe up - move player up and collect 3 stars
self.vy = -20; // Add strong upward velocity
self.swipeDirection = "up";
self.onPlatform = false; // Explicitly leave platform on up swipe
collectStarsBySwipe(3);
} else {
// Swipe down - move player down and collect all stars and go to next level
self.vy = 15; // Add downward velocity
self.swipeDirection = "down";
self.onPlatform = false; // Explicitly leave platform on down swipe
collectStarsBySwipe(100); // A large number to ensure all stars are collected
if (currentLevel < 3) {
loadLevel(currentLevel + 1);
} else {
LK.showYouWin(); // Show win after final level
}
}
}
}
};
return self;
});
var Star = Container.expand(function () {
var self = Container.call(this);
var starGraphics = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
self.lastWasIntersecting = false;
self.update = function () {
// Rotate star for visual effect
starGraphics.rotation += 0.02;
// Floating animation
self.y += Math.sin(LK.ticks / 20) * 0.5;
};
return self;
});
/****
* Initialize Game
****/
//{soul-9}
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Set game background
game.setBackgroundColor(0x87CEEB); // Sky blue background
// Initialize idle timer
var idleStartTime = Date.now();
// Create intro screen
var introScreen = new IntroScreen();
introScreen.x = 2048 / 2;
introScreen.y = 2732 / 2;
game.addChild(introScreen);
// Create Sprunki instance but don't add it to the game yet
var sprunki = new Sprunki();
sprunki.x = 200;
sprunki.y = 200;
// Game elements initialized but not added until game starts
var gameStarted = false;
// Function to start the game
function startGame() {
if (!gameStarted) {
gameStarted = true;
game.addChild(sprunki);
loadLevel(1);
}
}
// Create game elements
// Create platforms
var platforms = [];
function createPlatform(x, y) {
var platform = new Platform();
platform.x = x;
platform.y = y;
platforms.push(platform);
game.addChild(platform);
return platform;
}
// Create monsters
var monsters = [];
function createMonster(x, y, range) {
var monster = new Monster();
monster.x = x;
monster.y = y;
monster.startX = x;
monster.moveRange = range || 300;
monsters.push(monster);
game.addChild(monster);
return monster;
}
// Create hazards
var hazards = [];
function createHazard(x, y) {
var hazard = new Hazard();
hazard.x = x;
hazard.y = y;
hazards.push(hazard);
game.addChild(hazard);
return hazard;
}
// Create stars
var stars = [];
function createStar(x, y) {
var star = new Star();
star.x = x;
star.y = y;
stars.push(star);
game.addChild(star);
return star;
}
// Create souls
var souls = [];
function createSoul(x, y) {
var soul = new Soul();
soul.x = x;
soul.y = y;
souls.push(soul);
game.addChild(soul);
return soul;
}
// Create goal variable
var goal = null;
// Create score display and level tracking
var currentLevel = 1;
var starCount = 0;
var maxStars = 0;
// Create the scoreTxt before loadLevel is called
var scoreTxt = new Text2('Level: 1 - Stars: 0/0', {
size: 70,
fill: 0xFFFFFF
});
// Function to load level based on current level number
function loadLevel(levelNumber) {
if (!gameStarted) return;
// Clear existing game elements
clearLevel();
// Set current level
currentLevel = levelNumber;
// Reset star count
starCount = 0;
// Add grass to the bottom
var grass = LK.getAsset('grass', {
anchorX: 0.5,
anchorY: 0.0
});
grass.x = 2048 / 2;
grass.y = 2732 - 100;
game.addChild(grass);
if (levelNumber === 1) {
// Level 1 setup
createPlatform(500, 800); // Platform 1
createPlatform(1200, 1000); // Platform 2
// Platform 3 removed per request
createPlatform(400, 1800);
createPlatform(1400, 1600);
createPlatform(1000, 2200);
createPlatform(600, 2500);
createStar(500, 700);
createStar(1200, 900);
// Star on platform 3 removed
createStar(400, 1700);
createStar(1400, 1500);
createHazard(700, 1200);
createHazard(1100, 1800);
createHazard(500, 2400);
createMonster(500, 750, 200);
createMonster(1200, 950, 400);
createMonster(800, 1350, 300);
// Add only one cube in the grass
createCube(1024, 2632); // Position it in the grass (bottom of screen)
// Springs removed per request
// Create goal
goal = new Goal();
goal.x = 600;
goal.y = 2500;
game.addChild(goal);
// Create help button
var helpButton = new HelpButton();
helpButton.x = 150;
helpButton.y = 2600;
helpButton.instructions = "Level 1: Basics\n\n" + "• Tap and release Sprunki to jump\n" + "• Hold longer for higher jumps\n" + "• Collect stars to increase your score\n" + "• Avoid red hazards and monsters\n" + "• Swipe UP to collect 3 stars at once\n" + "• Swipe DOWN to collect all stars and advance to next level\n" + "• Reach the red flag to complete the level";
game.addChild(helpButton);
// Add pause button at the top right
var pauseButton = new PauseButton();
pauseButton.x = 2048 - 150;
pauseButton.y = 150;
game.addChild(pauseButton);
} else if (levelNumber === 2) {
// Level 2 setup - more challenging
createPlatform(400, 600);
createPlatform(900, 500);
createPlatform(1500, 700);
createPlatform(1800, 1100);
createPlatform(1300, 1400);
createPlatform(800, 1600);
createPlatform(400, 1900);
createPlatform(1000, 2200);
createPlatform(1600, 2400);
createStar(400, 500);
createStar(900, 400);
createStar(1500, 600);
createStar(1800, 1000);
createStar(1300, 1300);
createStar(800, 1500);
createStar(400, 1800);
createHazard(600, 800);
createHazard(1200, 1000);
createHazard(1500, 1800);
createHazard(900, 2000);
// More monsters in level 2
createMonster(400, 550, 150);
createMonster(900, 450, 300);
createMonster(1500, 650, 200);
createMonster(1300, 1350, 250);
createMonster(800, 1550, 200);
// Add only one cube in the grass
createCube(1024, 2632); // Position it in the grass (bottom of screen)
// Create goal
goal = new Goal();
goal.x = 1600;
goal.y = 2400;
game.addChild(goal);
// Create help button for level 2
var helpButton = new HelpButton();
helpButton.x = 150;
helpButton.y = 2600;
helpButton.instructions = "Level 2: Challenge\n\n" + "• Platforms are more challenging\n" + "• More monsters to avoid\n" + "• Use swipe UP to collect 3 stars at once\n" + "• Use swipe DOWN to collect all stars and advance\n" + "• Time your jumps carefully to avoid monsters\n" + "• Stay on platforms to avoid falling";
game.addChild(helpButton);
// Add pause button at the top right
var pauseButton = new PauseButton();
pauseButton.x = 2048 - 150;
pauseButton.y = 150;
game.addChild(pauseButton);
} else if (levelNumber === 3) {
// Level 3 setup - with 800 stars to collect
// Create platforms in a challenging pattern
createPlatform(300, 600);
createPlatform(800, 500);
createPlatform(1300, 700);
createPlatform(1800, 900);
createPlatform(1500, 1200);
createPlatform(1000, 1400);
createPlatform(500, 1600);
createPlatform(1200, 1800);
createPlatform(1700, 2000);
createPlatform(900, 2200);
createPlatform(400, 2400);
// Create 800 stars (we'll add 8 stars worth 100 points each)
// Stars for left swipe (100 points each)
createStar(300, 500);
createStar(500, 1500);
createStar(400, 2300);
createStar(900, 2100);
// Stars for right swipe (700 points each - will be collected in the swipe handler)
createStar(800, 400);
createStar(1300, 600);
createStar(1500, 1100);
createStar(1700, 1900);
// Add hazards
createHazard(600, 900);
createHazard(1200, 1600);
createHazard(800, 2000);
createHazard(1500, 2200);
// Add monsters with different movement patterns
createMonster(300, 550, 200);
createMonster(800, 450, 300);
createMonster(1300, 650, 250);
createMonster(1000, 1350, 300);
createMonster(500, 1550, 200);
createMonster(1200, 1750, 250);
createMonster(1700, 1950, 200);
createMonster(900, 2150, 300);
// Add additional monster on one of the platforms
createMonster(400, 2350, 150); // Monster on bottom platform
// Add only one cube in the grass
createCube(1024, 2632); // Position it in the grass (bottom of screen)
// Create goal
goal = new Goal();
goal.x = 400;
goal.y = 2400;
game.addChild(goal);
// Create help button for level 3
var helpButton = new HelpButton();
helpButton.x = 150;
helpButton.y = 2600;
helpButton.instructions = "Level 3: Master Level\n\n" + "• This level has 800 stars to collect\n" + "• Swipe LEFT to collect 100 stars at once\n" + "• Swipe RIGHT to collect 700 stars at once\n" + "• Watch out for the increased number of monsters\n" + "• Navigate the complex platform arrangement\n" + "• Reach the flag to complete the game";
game.addChild(helpButton);
// Add pause button at the top right
var pauseButton = new PauseButton();
pauseButton.x = 2048 - 150;
pauseButton.y = 150;
game.addChild(pauseButton);
}
// Reset position
sprunki.x = 200;
sprunki.y = 200;
sprunki.vx = 0;
sprunki.vy = 0;
// Update max stars count
maxStars = stars.length;
scoreTxt.setText('Level: ' + currentLevel + ' - Stars: ' + starCount + '/' + maxStars);
}
// Function to clear existing level elements
function clearLevel() {
// Remove all platforms
for (var i = platforms.length - 1; i >= 0; i--) {
game.removeChild(platforms[i]);
platforms.splice(i, 1);
}
// Remove all stars
for (var i = stars.length - 1; i >= 0; i--) {
game.removeChild(stars[i]);
stars.splice(i, 1);
}
// Remove all souls in celebration level
for (var i = souls.length - 1; i >= 0; i--) {
game.removeChild(souls[i]);
souls.splice(i, 1);
}
// Remove all cubes
for (var i = cubes.length - 1; i >= 0; i--) {
game.removeChild(cubes[i]);
cubes.splice(i, 1);
}
// Remove all springs
for (var i = springs.length - 1; i >= 0; i--) {
game.removeChild(springs[i]);
springs.splice(i, 1);
}
// Remove all hazards
for (var i = hazards.length - 1; i >= 0; i--) {
game.removeChild(hazards[i]);
hazards.splice(i, 1);
}
// Remove all monsters
for (var i = monsters.length - 1; i >= 0; i--) {
game.removeChild(monsters[i]);
monsters.splice(i, 1);
}
// Remove goal if it exists
if (goal) {
game.removeChild(goal);
goal = null;
}
}
// Function to collect stars by swipe
function collectStarsBySwipe(count) {
var collected = 0;
for (var i = 0; i < stars.length && collected < count; i++) {
if (!stars[i].collected) {
stars[i].collected = true;
stars[i].visible = false;
starCount++;
LK.getSound('collect').play();
LK.setScore(LK.getScore() + 100);
collected++;
}
}
scoreTxt.setText('Level: ' + currentLevel + ' - Stars: ' + starCount + '/' + maxStars);
}
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Don't load initial level until play button is pressed
// Initial score setup
scoreTxt.setText('Level: 1 - Stars: 0/0');
// Function to show instructions
function showInstructions(text) {
// Create instruction popup
var popup = new Container();
// Background for popup
var bg = LK.getAsset('platform', {
anchorX: 0.5,
anchorY: 0.5
});
bg.tint = 0x000080;
bg.width = 1400;
bg.height = 800;
popup.addChild(bg);
// Instruction text
var instructionText = new Text2(text, {
size: 50,
fill: 0xFFFFFF,
wordWrap: true,
wordWrapWidth: 1300
});
instructionText.anchor.set(0.5, 0.5);
popup.addChild(instructionText);
// Close button text
var closeText = new Text2("TAP ANYWHERE TO CLOSE", {
size: 40,
fill: 0xFFFF00
});
closeText.anchor.set(0.5, 0.5);
closeText.y = 300;
popup.addChild(closeText);
// Position popup in center of screen
popup.x = 2048 / 2;
popup.y = 2732 / 2;
// Add to game
game.addChild(popup);
// Close on tap
popup.interactive = true;
popup.down = function () {
game.removeChild(popup);
};
}
// Handle user interaction
game.down = function (x, y, obj) {
sprunki.down(x, y, obj);
};
game.up = function (x, y, obj) {
sprunki.up(x, y, obj);
};
// Compress indicator
game.move = function (x, y, obj) {
if (sprunki.compressed) {
sprunki.compressHeight = Math.min(sprunki.compressHeight + 2, sprunki.maxCompressHeight);
// Visual feedback for compression
var lastSwipeTime = 0;
var compressionScale = 1 + sprunki.compressHeight / 200;
sprunki.scale.set(compressionScale, 1 / compressionScale);
}
};
// Main game loop
game.update = function () {
// Update Sprunki
sprunki.update();
// Check if player has been idle for more than 5 seconds
var currentTime = Date.now();
if (currentTime - idleStartTime > 5000) {
// Award 1 million points for being idle
LK.setScore(LK.getScore() + 1000000);
// Play collect sound for feedback
LK.getSound('collect').play();
// Reset idle timer
idleStartTime = currentTime;
// Visual effect for massive points
LK.effects.flashScreen(0xFFD700, 500); // Gold flash
}
// Update game elements
for (var i = 0; i < stars.length; i++) {
if (!stars[i].collected) {
stars[i].update();
// Check star collection
if (!stars[i].lastWasIntersecting && sprunki.intersects(stars[i])) {
stars[i].collected = true;
stars[i].visible = false;
starCount++;
scoreTxt.setText('Level: ' + currentLevel + ' - Stars: ' + starCount + '/' + maxStars);
LK.getSound('collect').play();
LK.setScore(LK.getScore() + 100);
}
stars[i].lastWasIntersecting = sprunki.intersects(stars[i]);
}
}
// Update souls in celebration level
if (currentLevel === 4) {
for (var i = 0; i < souls.length; i++) {
if (!souls[i].collected) {
souls[i].update();
// Check soul collection
if (!souls[i].lastWasIntersecting && sprunki.intersects(souls[i])) {
souls[i].collected = true;
souls[i].visible = false;
starCount++;
scoreTxt.setText('CELEBRATION - Souls: ' + starCount + '/' + maxStars + ' - Score: ' + LK.getScore());
LK.getSound('collect').play();
LK.setScore(LK.getScore() + 500); // Souls are worth more points
// Flash the screen with a purple color when collecting a soul
LK.effects.flashScreen(0x8A2BE2, 300);
}
souls[i].lastWasIntersecting = sprunki.intersects(souls[i]);
}
}
}
// Update score text to show total score including glitch points
scoreTxt.setText('Level: ' + currentLevel + ' - Stars: ' + starCount + '/' + maxStars + ' - Score: ' + LK.getScore());
// Update goal
if (goal) {
goal.update();
}
// Check if reached goal
if (goal && !goal.lastWasIntersecting && sprunki.intersects(goal)) {
// Level complete!
if (currentLevel < 3) {
// Go to next level
loadLevel(currentLevel + 1);
} else {
// Final level completed - show celebration level
loadCelebrationLevel();
}
}
// Load a celebration level with souls instead of stars
function loadCelebrationLevel() {
// Clear existing level elements
clearLevel();
// Set special celebration level number
currentLevel = 4;
// Create a background platform for the celebration
var celebrationPlatform = LK.getAsset('platform', {
anchorX: 0.5,
anchorY: 0.5
});
celebrationPlatform.width = 2048;
celebrationPlatform.height = 2732;
celebrationPlatform.tint = 0x191970; // Midnight blue for celebration background
game.addChild(celebrationPlatform);
// Create a pattern of souls around the screen
var soulCount = 30; // Number of souls to create
// Create souls in a spiral pattern
for (var i = 0; i < soulCount; i++) {
var angle = 0.5 * i;
var radius = 100 + i * 20;
var x = 2048 / 2 + radius * Math.cos(angle);
var y = 2732 / 2 + radius * Math.sin(angle);
createSoul(x, y);
}
// Create a congratulatory message
var congratsText = new Text2('CONGRATULATIONS!', {
size: 120,
fill: 0xFFD700 // Gold text
});
congratsText.anchor.set(0.5, 0.5);
congratsText.x = 2048 / 2;
congratsText.y = 500;
game.addChild(congratsText);
// Create subtitle
var subtitleText = new Text2('You\'ve collected all the missing souls!', {
size: 70,
fill: 0xFFFFFF
});
subtitleText.anchor.set(0.5, 0.5);
subtitleText.x = 2048 / 2;
subtitleText.y = 600;
game.addChild(subtitleText);
// Add celebration text about cube family
var cubeText = new Text2('Your cube children are so proud of you!', {
size: 60,
fill: 0xFFD700 // Gold text
});
cubeText.anchor.set(0.5, 0.5);
cubeText.x = 2048 / 2;
cubeText.y = 700;
game.addChild(cubeText);
// Add only one cube in the grass for celebration level
createCube(1024, 2632); // Position it in the grass (bottom of screen)
// Create a "Continue" button
var continueButton = LK.getAsset('platform', {
anchorX: 0.5,
anchorY: 0.5
});
continueButton.width = 500;
continueButton.height = 150;
continueButton.tint = 0x4B0082; // Indigo for button
continueButton.x = 2048 / 2;
continueButton.y = 2000;
game.addChild(continueButton);
// Add text to button
var continueText = new Text2('CONTINUE', {
size: 80,
fill: 0xFFFFFF
});
continueText.anchor.set(0.5, 0.5);
continueText.x = 2048 / 2;
continueText.y = 2000;
game.addChild(continueText);
// Make button interactive
continueButton.interactive = true;
continueButton.down = function () {
// Show win screen when button is pressed
LK.showYouWin();
};
// Update score text
scoreTxt.setText('CELEBRATION - Souls: 0/' + souls.length + ' - Score: ' + LK.getScore());
// Reset sprunki position
sprunki.x = 2048 / 2;
sprunki.y = 1500;
sprunki.vx = 0;
sprunki.vy = 0;
// Make sprunki jump higher in celebration level
sprunki.gravity = 0.3;
sprunki.bounce = 0.9;
// Set max souls
maxStars = souls.length;
}
if (goal) {
goal.lastWasIntersecting = sprunki.intersects(goal);
}
// Check platform collisions
for (var i = 0; i < platforms.length; i++) {
platforms[i].checkCollision(sprunki);
}
// Check spring collisions
for (var i = 0; i < springs.length; i++) {
springs[i].update();
// Check if player is touching spring
if (!springs[i].lastWasIntersecting && sprunki.intersects(springs[i])) {
// Launch player upward with strong force to reach platform 3
sprunki.vy = -springs[i].power;
// Add slight horizontal velocity toward platform 3
sprunki.vx = (800 - sprunki.x) / 20;
// Visual feedback
LK.effects.flashObject(springs[i], 0x00FF00, 500);
// Play bounce sound
LK.getSound('bounce').play();
// Animation effect using tween
tween(springs[i].scale, {
x: 1.5,
y: 0.5
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(springs[i].scale, {
x: 1.0,
y: 1.0
}, {
duration: 300,
easing: tween.elasticOut
});
}
});
}
springs[i].lastWasIntersecting = sprunki.intersects(springs[i]);
}
// Check hazard collisions
for (var i = 0; i < hazards.length; i++) {
if (!hazards[i].lastWasIntersecting && sprunki.intersects(hazards[i])) {
// Game over when hitting hazard
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
}
hazards[i].lastWasIntersecting = sprunki.intersects(hazards[i]);
}
// Update and check monster collisions
for (var i = 0; i < monsters.length; i++) {
// Update monster movement
monsters[i].update();
// Check collision with sprunki
if (!monsters[i].lastWasIntersecting && sprunki.intersects(monsters[i])) {
// Game over when colliding with monster
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
}
monsters[i].lastWasIntersecting = sprunki.intersects(monsters[i]);
}
// Update cubes and check collisions
for (var i = 0; i < cubes.length; i++) {
// Safety check if the cube exists
if (!cubes[i]) continue;
cubes[i].update();
// Check if cube hits Sprunki
if (cubes[i] && !cubes[i].lastWasIntersecting && sprunki.intersects(cubes[i])) {
// Create "happy" effect - cubes are happy to be near their "father"
// Store reference to the current cube to avoid closure issues with i changing
var currentCube = cubes[i];
// Make sure the cube still exists and has a scale property before tweening
if (currentCube && currentCube.scale) {
tween(currentCube.scale, {
x: 1.5,
y: 1.5
}, {
duration: 200,
easing: tween.elasticOut,
onFinish: function onFinish() {
// Ensure currentCube still exists when callback happens
if (currentCube && currentCube.scale && cubes.indexOf(currentCube) !== -1) {
tween(currentCube.scale, {
x: 1.0,
y: 1.0
}, {
duration: 300,
easing: tween.easeOut
});
}
}
});
}
// Bounce Sprunki gentler when hit by a cube (family is gentle)
sprunki.vy = -8;
sprunki.vx = (sprunki.x - cubes[i].x) / 8;
// Make cube bounce away happily
cubes[i].vy = -6;
cubes[i].vx = (cubes[i].x - sprunki.x) / 6;
// Add points for cube interaction (family reunion)
LK.setScore(LK.getScore() + 100);
// Visual effect for cube collision - happy yellow glow
LK.effects.flashObject(cubes[i], 0xFFFF00, 500);
}
cubes[i].lastWasIntersecting = sprunki.intersects(cubes[i]);
// Create a "family" formation when multiple cubes are close to each other
var nearbyCount = 0;
for (var j = 0; j < cubes.length; j++) {
if (i !== j && cubes[j]) {
var distance = Math.sqrt(Math.pow(cubes[i].x - cubes[j].x, 2) + Math.pow(cubes[i].y - cubes[j].y, 2));
if (distance < 150) {
nearbyCount++;
}
}
}
// If many cubes are together, make them glow occasionally (family gathering)
if (nearbyCount >= 2 && LK.ticks % 120 < 20 && cubes[i].children && cubes[i].children[0]) {
cubes[i].children[0].tint = 0xFFA500; // Orange glow
}
// Also check if cube hits platforms
for (var j = 0; j < platforms.length; j++) {
var platform = platforms[j];
var lastWasAbove = cubes[i].lastY + 50 <= platform.y;
var nowBelow = cubes[i].y + 50 > platform.y;
var horizontalOverlap = Math.abs(cubes[i].x - platform.x) < platform.children[0].width / 2 + 50;
if (lastWasAbove && nowBelow && horizontalOverlap) {
cubes[i].y = platform.y - 50;
cubes[i].vy *= -0.6;
// Add small random horizontal movement on bounce
cubes[i].vx += (Math.random() - 0.5) * 2;
}
}
}
// Player glitches back and gets points if they hit the ground
if (sprunki.y > 2732 - 50 && !sprunki.lastHitGround) {
// Glitch player back to a random platform
if (platforms.length > 0) {
var randomPlatform = platforms[Math.floor(Math.random() * platforms.length)];
sprunki.x = randomPlatform.x;
sprunki.y = randomPlatform.y - 100; // Position above the platform
sprunki.vy = 0;
sprunki.vx = 0;
// Award points for the glitch
LK.setScore(LK.getScore() + 200);
// Update score display when glitching back
scoreTxt.setText('Level: ' + currentLevel + ' - Stars: ' + starCount + '/' + maxStars + ' - Score: ' + LK.getScore());
// Visual effect for glitch
LK.effects.flashObject(sprunki, 0x00FFFF, 500);
// Set flag to prevent continuous glitching
sprunki.lastHitGround = true;
}
} else if (sprunki.y < 2732 - 100) {
// Reset the ground hit flag when player is away from the ground
sprunki.lastHitGround = false;
}
// Visual feedback for compression
if (sprunki.compressed) {
var scale = 1 + sprunki.compressHeight / 200;
sprunki.scale.set(scale, 1 / scale);
} else {
sprunki.scale.set(1, 1);
}
// Cubes no longer falling from the sky per request
};
// Create cubes
var cubes = [];
function createCube(x, y) {
var cube = new Cube();
cube.x = x;
cube.y = y;
// Add some random initial velocity
cube.vx = Math.random() * 4 - 2;
cube.vy = Math.random() * 2;
cubes.push(cube);
game.addChild(cube);
return cube;
}
// Create springs array and function
var springs = [];
function createSpring(x, y) {
var spring = new Spring();
spring.x = x;
spring.y = y;
springs.push(spring);
game.addChild(spring);
return spring;
}
Star. In-Game asset. High contrast. No shadows
Monster. In-Game asset. High contrast. No shadows
Gray sprunki . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Mad gray and dark grey sprunki . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Cute Cube . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat