/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { completedLevels: 1 }); /**** * Classes ****/ var MathBox = Container.expand(function (operation, value, lane) { var self = Container.call(this); self.operation = operation; self.value = value; self.lane = lane; self.speed = gameSpeed; // Create distinct hamburger visuals based on operation type self.hamburgerParts = []; if (operation === 'add') { // ADD (+): Classic cheeseburger - green theme var bottomBun = self.attachAsset('hamburgerBun', { anchorX: 0.5, anchorY: 0.5, y: 40, tint: 0x90EE90 }); var lettuce = self.attachAsset('hamburgerLettuce', { anchorX: 0.5, anchorY: 0.5, y: 25, tint: 0x32FF32 }); var cheese = self.attachAsset('hamburgerCheese', { anchorX: 0.5, anchorY: 0.5, y: 15, tint: 0xFFD700 }); var patty = self.attachAsset('hamburgerPatty', { anchorX: 0.5, anchorY: 0.5, y: 5, tint: 0x90EE90 }); var topBun = self.attachAsset('hamburgerBun', { anchorX: 0.5, anchorY: 0.5, y: -10, tint: 0x90EE90 }); self.hamburgerParts = [bottomBun, lettuce, cheese, patty, topBun]; } else if (operation === 'subtract') { // SUBTRACT (-): Deconstructed burger - red theme var bottomBun = self.attachAsset('hamburgerBun', { anchorX: 0.5, anchorY: 0.5, y: 35, tint: 0xFFB6C1 }); var tomato = self.attachAsset('hamburgerTomato', { anchorX: 0.5, anchorY: 0.5, y: 20, tint: 0xFF4444 }); var patty = self.attachAsset('hamburgerPatty', { anchorX: 0.5, anchorY: 0.5, y: 10, tint: 0xFF6666 }); var topBun = self.attachAsset('hamburgerBun', { anchorX: 0.5, anchorY: 0.5, y: -5, tint: 0xFFB6C1 }); self.hamburgerParts = [bottomBun, tomato, patty, topBun]; } else if (operation === 'multiply') { // MULTIPLY (×): Double stack burger - blue theme var bottomBun = self.attachAsset('hamburgerBun', { anchorX: 0.5, anchorY: 0.5, y: 45, tint: 0x87CEEB }); var patty1 = self.attachAsset('hamburgerPatty', { anchorX: 0.5, anchorY: 0.5, y: 30, tint: 0x6699FF }); var cheese1 = self.attachAsset('hamburgerCheese', { anchorX: 0.5, anchorY: 0.5, y: 20, tint: 0x4488FF }); var patty2 = self.attachAsset('hamburgerPatty', { anchorX: 0.5, anchorY: 0.5, y: 10, tint: 0x6699FF }); var cheese2 = self.attachAsset('hamburgerCheese', { anchorX: 0.5, anchorY: 0.5, y: 0, tint: 0x4488FF }); var topBun = self.attachAsset('hamburgerBun', { anchorX: 0.5, anchorY: 0.5, y: -15, tint: 0x87CEEB }); self.hamburgerParts = [bottomBun, patty1, cheese1, patty2, cheese2, topBun]; } else if (operation === 'divide') { // DIVIDE (÷): Split burger - orange theme var bottomBun = self.attachAsset('hamburgerBun', { anchorX: 0.5, anchorY: 0.5, y: 30, tint: 0xFFE4B5, scaleX: 0.8 }); var lettuce = self.attachAsset('hamburgerLettuce', { anchorX: 0.5, anchorY: 0.5, y: 20, tint: 0xFFAA44, scaleX: 0.7 }); var patty = self.attachAsset('hamburgerPatty', { anchorX: 0.5, anchorY: 0.5, y: 10, tint: 0xFF8844, scaleX: 0.6 }); var topBun = self.attachAsset('hamburgerBun', { anchorX: 0.5, anchorY: 0.5, y: 0, tint: 0xFFE4B5, scaleX: 0.5 }); self.hamburgerParts = [bottomBun, lettuce, patty, topBun]; } // Create operation text with better styling and larger size var operationText = ''; if (operation === 'multiply') operationText = '×' + value;else if (operation === 'add') operationText = '+' + value;else if (operation === 'subtract') operationText = '-' + value;else if (operation === 'divide') operationText = '÷' + value; var textDisplay = new Text2(operationText, { size: 58, fill: 0x000000, stroke: 0xFFFFFF, strokeThickness: 3 }); textDisplay.anchor.set(0.5, 0.5); textDisplay.y = 10; // Position in center of hamburger self.addChild(textDisplay); // Add floating animation self.animationTimer = Math.random() * Math.PI * 2; self.update = function () { self.y += self.speed; // Add gentle floating animation self.animationTimer += 0.1; var floatOffset = Math.sin(self.animationTimer) * 2; for (var i = 0; i < self.hamburgerParts.length; i++) { self.hamburgerParts[i].y += floatOffset * 0.1; // Subtle float for all parts } textDisplay.y = 10 + floatOffset; }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); // Create human character with body parts var legs = self.attachAsset('runnerLegs', { anchorX: 0.5, anchorY: 1.0, y: 0 }); var body = self.attachAsset('runnerBody', { anchorX: 0.5, anchorY: 1.0, y: -60 }); var head = self.attachAsset('runnerHead', { anchorX: 0.5, anchorY: 1.0, y: -140 }); self.targetX = 1024; // Center position self.moveSpeed = 8; self.animationTimer = 0; self.currentScale = 1.0; // Track current scale self.bodyParts = [legs, body, head]; // Store references for scaling // Method to grow character smoothly based on multiplication and addition self.grow = function (value, operation) { var growthFactor = 1; if (operation === 'multiply') { growthFactor = 1 + value * 0.15; // Grow based on multiplication value } else if (operation === 'add') { growthFactor = 1 + value * 0.01; // Small growth based on addition value } var newScale = self.currentScale * growthFactor; newScale = Math.max(0.3, Math.min(newScale, 3.0)); // Clamp between 0.3x and 3x if (newScale !== self.currentScale) { // Calculate growth increase based on actual player number change var growthIncrease = playerNumber * 100; // Use actual player number for growth currentGrowth += Math.round(growthIncrease); // Animate scale change smoothly for (var i = 0; i < self.bodyParts.length; i++) { tween(self.bodyParts[i], { scaleX: newScale, scaleY: newScale }, { duration: 400, easing: tween.easeOut }); } self.currentScale = newScale; } }; // Method to shrink character smoothly based on subtraction and division self.shrink = function (value, operation) { var shrinkFactor = 1; if (operation === 'subtract') { shrinkFactor = 1 + value * 0.01; // Shrink based on subtraction value } else if (operation === 'divide') { shrinkFactor = 1 + value * 0.1; // Shrink based on division value } var newScale = self.currentScale / shrinkFactor; newScale = Math.max(0.3, Math.min(newScale, 3.0)); // Clamp between 0.3x and 3x if (newScale !== self.currentScale) { // Calculate growth decrease based on player number loss var growthDecrease = Math.max(0, playerNumber * 50); // Smaller penalty for shrinking currentGrowth = Math.max(0, currentGrowth - Math.round(growthDecrease)); // Animate scale change smoothly for (var i = 0; i < self.bodyParts.length; i++) { tween(self.bodyParts[i], { scaleX: newScale, scaleY: newScale }, { duration: 400, easing: tween.easeOut }); } self.currentScale = newScale; } }; self.update = function () { // Smooth movement towards target position var diff = self.targetX - self.x; if (Math.abs(diff) > 2) { self.x += diff * 0.15; } else { self.x = self.targetX; } // Simple running animation - bob the character self.animationTimer += 0.3; var bobOffset = Math.sin(self.animationTimer) * 3; head.y = -140 + bobOffset; body.y = -60 + bobOffset * 0.5; legs.y = bobOffset * 0.3; // Leg movement animation legs.rotation = Math.sin(self.animationTimer * 2) * 0.1; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB // Sky blue background }); /**** * Game Code ****/ // Game state management var gameState = 'menu'; // 'menu', 'levelSelect', 'playing', 'paused' var currentLevel = 1; var maxLevel = 100; var completedLevels = storage.completedLevels || 1; // Track highest completed level var pauseMenuButtons = []; // Store pause menu button references // Game variables (moved to global scope) var player; var playerNumber = 1; // Always start with 1 var gameSpeed = 6; var mathBoxes = []; var lanes = [600, 1024, 1448]; // Left, center, right lanes var currentLane = 1; // Start in center var spawnTimer = 0; var spawnInterval = 120; // Spawn every 2 seconds at 60fps var gameDistance = 0; var speedIncreaseTimer = 0; var environmentObjects = []; // Level system variables var levelTimer = 30 * 60; // 30 seconds at 60fps var initialScale = 1.0; // Track initial player scale var currentGrowth = 0; // Track total growth achieved var targetGrowth = 100; // Target growth to win level (reasonable for player number) // UI elements var numberDisplay; var timerDisplay; var growthDisplay; var roadMarkings = []; // Main menu setup function createMainMenu() { game.removeChildren(); // Create menu background var menuBg = game.addChild(LK.getAsset('grass', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, alpha: 0.8 })); // Game title var titleText = new Text2('MATH RUNNER', { size: 120, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 6 }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 600; game.addChild(titleText); // Play button var playButton = game.addChild(LK.getAsset('hamburgerBun', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1200, scaleX: 4, scaleY: 2, tint: 0x00FF00 })); var playText = new Text2('PLAY', { size: 80, fill: 0x000000 }); playText.anchor.set(0.5, 0.5); playText.x = 1024; playText.y = 1200; game.addChild(playText); // Level select button var levelButton = game.addChild(LK.getAsset('hamburgerBun', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1500, scaleX: 4, scaleY: 2, tint: 0x0088FF })); var levelText = new Text2('SELECT LEVEL', { size: 60, fill: 0x000000 }); levelText.anchor.set(0.5, 0.5); levelText.x = 1024; levelText.y = 1500; game.addChild(levelText); // Store button references for interaction game.playButton = playButton; game.levelButton = levelButton; game.playText = playText; game.levelText = levelText; } // Level selection screen with pagination var currentLevelPage = 1; var levelsPerPage = 10; function createLevelSelect() { game.removeChildren(); // Level select background var levelBg = game.addChild(LK.getAsset('grass', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, alpha: 0.6 })); // Level select title var levelTitle = new Text2('SELECT LEVEL - Page ' + currentLevelPage + ' of ' + Math.ceil(maxLevel / levelsPerPage), { size: 80, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 4 }); levelTitle.anchor.set(0.5, 0.5); levelTitle.x = 1024; levelTitle.y = 300; game.addChild(levelTitle); // Create level buttons for current page game.levelButtons = []; var startLevel = (currentLevelPage - 1) * levelsPerPage + 1; var endLevel = Math.min(startLevel + levelsPerPage - 1, maxLevel); // Create 2 rows of 5 levels each for (var i = startLevel; i <= endLevel; i++) { var row = Math.floor((i - startLevel) / 5); var col = (i - startLevel) % 5; var btnX = 300 + col * 300; var btnY = 600 + row * 200; var isLocked = i > completedLevels; // Lock levels that haven't been unlocked yet var levelBtn = game.addChild(LK.getAsset('hamburgerBun', { anchorX: 0.5, anchorY: 0.5, x: btnX, y: btnY, scaleX: 2, scaleY: 2, tint: isLocked ? 0x666666 : i <= 20 ? 0x90EE90 : i <= 50 ? 0xFFD700 : i <= 80 ? 0xFF8C00 : 0xFF4444, alpha: isLocked ? 0.5 : 1.0 })); var levelNumText = new Text2(isLocked ? 'LOCKED' : i.toString(), { size: isLocked ? 30 : 60, fill: isLocked ? 0x999999 : 0x000000 }); levelNumText.anchor.set(0.5, 0.5); levelNumText.x = btnX; levelNumText.y = btnY; game.addChild(levelNumText); levelBtn.levelNumber = i; levelBtn.isLocked = isLocked; game.levelButtons.push(levelBtn); } // Navigation buttons if (currentLevelPage > 1) { var prevButton = game.addChild(LK.getAsset('hamburgerBun', { anchorX: 0.5, anchorY: 0.5, x: 400, y: 1200, scaleX: 2, scaleY: 1.5, tint: 0x4169E1 })); var prevText = new Text2('PREV', { size: 50, fill: 0x000000 }); prevText.anchor.set(0.5, 0.5); prevText.x = 400; prevText.y = 1200; game.addChild(prevText); game.prevButton = prevButton; } if (currentLevelPage < Math.ceil(maxLevel / levelsPerPage)) { var nextButton = game.addChild(LK.getAsset('hamburgerBun', { anchorX: 0.5, anchorY: 0.5, x: 1648, y: 1200, scaleX: 2, scaleY: 1.5, tint: 0x4169E1 })); var nextText = new Text2('NEXT', { size: 50, fill: 0x000000 }); nextText.anchor.set(0.5, 0.5); nextText.x = 1648; nextText.y = 1200; game.addChild(nextText); game.nextButton = nextButton; } // Back button var backButton = game.addChild(LK.getAsset('hamburgerBun', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1500, scaleX: 3, scaleY: 1.5, tint: 0xFF4444 })); var backText = new Text2('BACK', { size: 60, fill: 0x000000 }); backText.anchor.set(0.5, 0.5); backText.x = 1024; backText.y = 1500; game.addChild(backText); game.backButton = backButton; game.backText = backText; } // Start game with selected level function startGame(level) { currentLevel = level; gameState = 'playing'; // Clear menu elements game.removeChildren(); // Progressive difficulty scaling for all 100 levels var baseDifficulty = Math.floor((level - 1) / 10); // Difficulty tier (0-9) var levelInTier = (level - 1) % 10 + 1; // Position within tier (1-10) // Target growth scaling: much higher growth targets for difficulty targetGrowth = Math.floor(150 + (level - 1) * 35 + Math.pow(baseDifficulty, 2) * 100); // Timer scaling: much shorter time limits for increased challenge var baseTime = Math.max(15, 45 - baseDifficulty * 4); // Shorter base time levelTimer = (baseTime + Math.floor(levelInTier / 2)) * 60; // Less bonus time within tier // Speed scaling: progressive increase with level gameSpeed = 6 + baseDifficulty * 0.8 + (levelInTier - 1) * 0.2; // Spawn interval scaling: faster spawning at higher levels spawnInterval = Math.max(30, 120 - baseDifficulty * 8 - levelInTier * 2); // Initialize game world initializeGameWorld(); } // Initialize the game world (moved from main code) function initializeGameWorld() { // Reset game state to ensure fresh start playerNumber = 1; // Explicitly reset to 1 at game initialization mathBoxes = []; currentLane = 1; spawnTimer = 0; spawnInterval = 120; gameDistance = 0; speedIncreaseTimer = 0; environmentObjects = []; currentGrowth = 0; roadMarkings = []; // Create realistic environment layers // Sky background (already set in game initialization) // Create layered sky with gradient var skyGradientTop = game.addChild(LK.getAsset('skyGradient', { anchorX: 0.5, anchorY: 0.0, x: 1024, y: 0, tint: 0x87ceeb, alpha: 1.0 })); var skyGradientBottom = game.addChild(LK.getAsset('skyGradient', { anchorX: 0.5, anchorY: 0.0, x: 1024, y: 400, tint: 0xb0e0e6, alpha: 0.8 })); // Add sun/moon var sunMoon = game.addChild(LK.getAsset('sun', { anchorX: 0.5, anchorY: 0.5, x: 1700, y: 300, alpha: 0.9, tint: 0xffd700 })); environmentObjects.push({ type: 'sun', obj: sunMoon }); // Add distant mountains for (var i = 0; i < 4; i++) { var mountain = game.addChild(LK.getAsset('mountain', { anchorX: 0.5, anchorY: 1.0, x: 200 + i * 500, y: 700, alpha: 0.4, scaleY: 0.6 + Math.random() * 0.4 })); var mountainSnow = game.addChild(LK.getAsset('mountainSnow', { anchorX: 0.5, anchorY: 1.0, x: 200 + i * 500, y: 600 - Math.random() * 50, alpha: 0.6, scaleY: 0.3 + Math.random() * 0.2 })); environmentObjects.push({ type: 'mountain', obj: mountain }); environmentObjects.push({ type: 'mountainSnow', obj: mountainSnow }); } // Add realistic moving clouds with shadows for (var i = 0; i < 5; i++) { // Cloud shadow var cloudShadow = game.addChild(LK.getAsset('cloudShadow', { anchorX: 0.5, anchorY: 0.5, x: Math.random() * 2048, y: 150 + Math.random() * 200, alpha: 0.3 })); // Main cloud var cloud = game.addChild(LK.getAsset('cloud', { anchorX: 0.5, anchorY: 0.5, x: Math.random() * 2048, y: 140 + Math.random() * 200, alpha: 0.85, scaleX: 0.8 + Math.random() * 0.6, scaleY: 0.7 + Math.random() * 0.4 })); cloud.moveSpeed = 0.3 + Math.random() * 0.7; cloudShadow.moveSpeed = cloud.moveSpeed; cloud.shadowRef = cloudShadow; environmentObjects.push({ type: 'cloud', obj: cloud }); environmentObjects.push({ type: 'cloudShadow', obj: cloudShadow }); } // Create grass background with details var grassBackground = game.addChild(LK.getAsset('grass', { anchorX: 0.5, anchorY: 1.0, x: 1024, y: 2732, tint: 0x228b22 })); // Add grass detail patches for (var i = 0; i < 12; i++) { var grassDetail = game.addChild(LK.getAsset('grassDetail', { anchorX: 0.5, anchorY: 1.0, x: Math.random() * 2048, y: 2400 + Math.random() * 300, alpha: 0.6, tint: 0x32cd32 + Math.floor(Math.random() * 0x004400), scaleX: 0.5 + Math.random() * 1.0, scaleY: 0.7 + Math.random() * 0.6, rotation: Math.random() * Math.PI * 2 })); environmentObjects.push({ type: 'grassDetail', obj: grassDetail }); } // Create road surface var road = game.addChild(LK.getAsset('road', { anchorX: 0.5, anchorY: 1.0, x: 1024, y: 2732 })); // Add road lane markings for (var i = 0; i < 8; i++) { var marking = game.addChild(LK.getAsset('roadLine', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 200 + i * 350 })); roadMarkings.push(marking); } // Add trees along the sides of the road - stationary realistic placement for (var i = 0; i < 12; i++) { // Left side trees with varied positioning var leftTreeX = 150 + Math.random() * 200; var leftTreeY = 1600 + Math.random() * 800; var leftTree = game.addChild(LK.getAsset('tree', { anchorX: 0.5, anchorY: 1.0, x: leftTreeX, y: leftTreeY, scaleX: 0.8 + Math.random() * 0.6, scaleY: 0.9 + Math.random() * 0.4 })); var leftFoliage = game.addChild(LK.getAsset('treeFoliage', { anchorX: 0.5, anchorY: 1.0, x: leftTreeX, y: leftTreeY - 100 - Math.random() * 30, scaleX: 0.8 + Math.random() * 0.5, scaleY: 0.8 + Math.random() * 0.4, tint: 0x228b22 + Math.floor(Math.random() * 0x005500) })); // Right side trees with varied positioning var rightTreeX = 1700 + Math.random() * 200; var rightTreeY = 1800 + Math.random() * 700; var rightTree = game.addChild(LK.getAsset('tree', { anchorX: 0.5, anchorY: 1.0, x: rightTreeX, y: rightTreeY, scaleX: 0.8 + Math.random() * 0.6, scaleY: 0.9 + Math.random() * 0.4 })); var rightFoliage = game.addChild(LK.getAsset('treeFoliage', { anchorX: 0.5, anchorY: 1.0, x: rightTreeX, y: rightTreeY - 100 - Math.random() * 30, scaleX: 0.8 + Math.random() * 0.5, scaleY: 0.8 + Math.random() * 0.4, tint: 0x228b22 + Math.floor(Math.random() * 0x005500) })); } // Add city buildings in background with realistic stationary skyline for (var i = 0; i < 15; i++) { var buildingHeight = 0.6 + Math.random() * 1.8; var buildingX = 100 + i * 140 + Math.random() * 80; var building = game.addChild(LK.getAsset('building', { anchorX: 0.5, anchorY: 1.0, x: buildingX, y: 750, alpha: 0.25 + Math.random() * 0.15, tint: 0x555555 + Math.floor(Math.random() * 0x444444), scaleY: buildingHeight, scaleX: 0.8 + Math.random() * 0.6 })); // Add windows to buildings with more realistic patterns var windowRows = Math.floor(buildingHeight * 10); var windowCols = 3 + Math.floor(Math.random() * 4); for (var row = 0; row < windowRows; row++) { for (var col = 0; col < windowCols; col++) { if (Math.random() > 0.4) { // Varied window lighting patterns var window = game.addChild(LK.getAsset('buildingWindow', { anchorX: 0.5, anchorY: 0.5, x: buildingX - 40 + col * 25, y: building.y - 40 - row * 30, alpha: 0.6 + Math.random() * 0.4, tint: Math.random() > 0.7 ? 0xffffff : 0xffff88, scaleX: 0.8 + Math.random() * 0.4, scaleY: 0.9 + Math.random() * 0.3 })); } } } } // Create player player = game.addChild(new Player()); player.x = lanes[currentLane]; player.y = 2400; // UI Elements numberDisplay = new Text2(playerNumber.toString(), { size: 80, fill: 0xFFFFFF }); numberDisplay.anchor.set(0.5, 0.5); numberDisplay.x = 1024; numberDisplay.y = 300; game.addChild(numberDisplay); // Add timer display timerDisplay = new Text2('Time: ' + Math.ceil(levelTimer / 60), { size: 50, fill: 0xFFFF00 }); timerDisplay.anchor.set(0.5, 0); timerDisplay.y = 60; LK.gui.top.addChild(timerDisplay); // Add growth display growthDisplay = new Text2('Growth: 0 / ' + targetGrowth, { size: 40, fill: 0x00FF00 }); growthDisplay.anchor.set(0.5, 0); growthDisplay.y = 120; LK.gui.top.addChild(growthDisplay); // Add pause button var pauseButton = new Text2('||', { size: 60, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 3 }); pauseButton.anchor.set(0.5, 0.5); pauseButton.x = 1900; pauseButton.y = 100; game.addChild(pauseButton); game.pauseButton = pauseButton; } // Create pause menu overlay function createPauseMenu() { // Create semi-transparent overlay var overlay = game.addChild(LK.getAsset('grass', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, alpha: 0.7, tint: 0x000000 })); // Pause title var pauseTitle = new Text2('GAME PAUSED', { size: 100, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 5 }); pauseTitle.anchor.set(0.5, 0.5); pauseTitle.x = 1024; pauseTitle.y = 800; game.addChild(pauseTitle); // Continue button var continueButton = game.addChild(LK.getAsset('hamburgerBun', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1200, scaleX: 4, scaleY: 2, tint: 0x00FF00 })); var continueText = new Text2('CONTINUE', { size: 70, fill: 0x000000 }); continueText.anchor.set(0.5, 0.5); continueText.x = 1024; continueText.y = 1200; game.addChild(continueText); // Main menu button var mainMenuButton = game.addChild(LK.getAsset('hamburgerBun', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1500, scaleX: 4, scaleY: 2, tint: 0xFF4444 })); var mainMenuText = new Text2('MAIN MENU', { size: 70, fill: 0x000000 }); mainMenuText.anchor.set(0.5, 0.5); mainMenuText.x = 1024; mainMenuText.y = 1500; game.addChild(mainMenuText); // Store references for interaction pauseMenuButtons = [overlay, pauseTitle, continueButton, continueText, mainMenuButton, mainMenuText]; game.continueButton = continueButton; game.mainMenuButton = mainMenuButton; } // Remove pause menu elements function removePauseMenu() { for (var i = 0; i < pauseMenuButtons.length; i++) { if (pauseMenuButtons[i].parent) { pauseMenuButtons[i].parent.removeChild(pauseMenuButtons[i]); } } pauseMenuButtons = []; game.continueButton = null; game.mainMenuButton = null; } // Handle pause button press (LK engine automatically calls this when pause is pressed) LK.on('pause', function () { if (gameState === 'playing') { gameState = 'paused'; createPauseMenu(); } }); // Initialize main menu on game start createMainMenu(); // Generate random math operations with progressive difficulty based on current level function generateOperation() { var levelDifficulty = Math.floor(gameDistance / 1200); // Time-based difficulty within level var totalDifficulty = Math.floor((currentLevel - 1) / 5) + levelDifficulty; // Combined difficulty // Equal distribution of all operations at all levels var operations = ['multiply', 'add', 'subtract', 'divide']; var operation = operations[Math.floor(Math.random() * operations.length)]; var value; if (operation === 'multiply') { var baseMultiplier = 2 + Math.floor(currentLevel / 10); value = Math.floor(Math.random() * (1 + totalDifficulty)) + baseMultiplier; value = Math.min(value, Math.max(6, Math.floor(currentLevel / 5))); // Scale cap with level } else if (operation === 'add') { var baseAdd = 5 + Math.floor(currentLevel / 3) + totalDifficulty * 3; value = Math.floor(Math.random() * (10 + currentLevel)) + baseAdd; value = Math.min(value, 50 + currentLevel * 2); // Higher values at higher levels } else if (operation === 'subtract') { var baseSub = 3 + Math.floor(currentLevel / 4) + totalDifficulty * 2; value = Math.floor(Math.random() * (5 + Math.floor(currentLevel / 2))) + baseSub; value = Math.min(value, Math.max(15, Math.floor(currentLevel / 2))); // Increase subtract danger } else if (operation === 'divide') { var baseDiv = 2 + Math.floor(currentLevel / 15); value = Math.floor(Math.random() * (2 + Math.floor(totalDifficulty / 2))) + baseDiv; value = Math.min(value, Math.max(5, Math.floor(currentLevel / 8))); // Scale division difficulty } return { operation: operation, value: value }; } // Spawn math boxes function spawnMathBoxes() { // Spawn 2-3 boxes across different lanes var numBoxes = Math.floor(Math.random() * 2) + 2; // 2 or 3 boxes var usedLanes = []; for (var i = 0; i < numBoxes; i++) { var laneIndex; do { laneIndex = Math.floor(Math.random() * 3); } while (usedLanes.indexOf(laneIndex) !== -1); usedLanes.push(laneIndex); var operation = generateOperation(); var mathBox = new MathBox(operation.operation, operation.value, laneIndex); mathBox.x = lanes[laneIndex]; mathBox.y = -100; mathBoxes.push(mathBox); game.addChild(mathBox); } } // Handle player movement and menu interactions game.down = function (x, y, obj) { if (gameState === 'menu') { // Check if play button was clicked - using coordinate bounds checking if (game.playButton && x >= 824 && x <= 1224 && y >= 1100 && y <= 1300) { startGame(1); // Start level 1 } // Check if level select button was clicked else if (game.levelButton && x >= 824 && x <= 1224 && y >= 1400 && y <= 1600) { gameState = 'levelSelect'; createLevelSelect(); } } else if (gameState === 'levelSelect') { // Check level buttons if (game.levelButtons) { for (var i = 0; i < game.levelButtons.length; i++) { var startLevel = (currentLevelPage - 1) * levelsPerPage + 1; var levelIndex = game.levelButtons[i].levelNumber - startLevel; var row = Math.floor(levelIndex / 5); var col = levelIndex % 5; var btnX = 300 + col * 300; var btnY = 600 + row * 200; if (x >= btnX - 100 && x <= btnX + 100 && y >= btnY - 50 && y <= btnY + 50) { // Only allow playing unlocked levels if (!game.levelButtons[i].isLocked) { startGame(game.levelButtons[i].levelNumber); return; } } } } // Check navigation buttons if (game.prevButton && x >= 300 && x <= 500 && y >= 1150 && y <= 1250) { currentLevelPage--; createLevelSelect(); return; } if (game.nextButton && x >= 1548 && x <= 1748 && y >= 1150 && y <= 1250) { currentLevelPage++; createLevelSelect(); return; } // Check back button if (game.backButton && x >= 724 && x <= 1324 && y >= 1450 && y <= 1550) { gameState = 'menu'; currentLevelPage = 1; // Reset to first page createMainMenu(); } } else if (gameState === 'paused') { // Check if continue button was clicked if (game.continueButton && x >= 824 && x <= 1224 && y >= 1100 && y <= 1300) { gameState = 'playing'; removePauseMenu(); } // Check if main menu button was clicked else if (game.mainMenuButton && x >= 824 && x <= 1224 && y >= 1400 && y <= 1600) { gameState = 'menu'; removePauseMenu(); createMainMenu(); } } else if (gameState === 'levelComplete') { // Check if next level button was clicked if (game.nextLevelButton && x >= 824 && x <= 1224 && y >= 1500 && y <= 1700) { startGame(currentLevel + 1); // Progress to next level } } else if (gameState === 'playing') { // Check if pause button was clicked (top right area) if (x >= 1900 && x <= 2048 && y >= 20 && y <= 120) { gameState = 'paused'; createPauseMenu(); return; } if (x < 1024 && currentLane > 0) { // Move left currentLane--; player.targetX = lanes[currentLane]; } else if (x >= 1024 && currentLane < 2) { // Move right currentLane++; player.targetX = lanes[currentLane]; } } }; // Main game update game.update = function () { // Only update game logic when actually playing if (gameState !== 'playing' && gameState !== 'levelComplete') { return; } // If in level complete state, don't update game mechanics if (gameState === 'levelComplete') { return; } gameDistance++; // Update level timer levelTimer--; var secondsLeft = Math.ceil(levelTimer / 60); timerDisplay.setText('Time: ' + secondsLeft); // Update growth display growthDisplay.setText('Growth: ' + playerNumber + ' / ' + targetGrowth); // Check win condition if (playerNumber >= targetGrowth) { LK.setScore(playerNumber); // Unlock next level if current level is newly completed if (currentLevel >= completedLevels) { completedLevels = currentLevel + 1; storage.completedLevels = completedLevels; // Save progress } // Check if there are more levels to play if (currentLevel < maxLevel) { LK.showYouWin(); // Wait a moment for the you win screen to appear, then add next level button LK.setTimeout(function () { // Create next level button var nextLevelButton = game.addChild(LK.getAsset('hamburgerBun', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1600, scaleX: 4, scaleY: 2, tint: 0x00FF00 })); var nextLevelText = new Text2('NEXT LEVEL', { size: 70, fill: 0x000000 }); nextLevelText.anchor.set(0.5, 0.5); nextLevelText.x = 1024; nextLevelText.y = 1600; game.addChild(nextLevelText); // Store button references for interaction game.nextLevelButton = nextLevelButton; game.nextLevelText = nextLevelText; }, 1000); gameState = 'levelComplete'; return; } else { // Player completed all 100 levels! var finalText = new Text2('CONGRATULATIONS!\nYOU COMPLETED ALL 100 LEVELS!', { size: 80, fill: 0xFFD700, stroke: 0x000000, strokeThickness: 4, align: 'center' }); finalText.anchor.set(0.5, 0.5); finalText.x = 1024; finalText.y = 1366; game.addChild(finalText); LK.showYouWin(); gameState = 'menu'; LK.setTimeout(function () { createMainMenu(); }, 4000); return; } } // Check time up condition if (levelTimer <= 0) { LK.setScore(playerNumber); LK.showGameOver(); gameState = 'menu'; LK.setTimeout(function () { createMainMenu(); }, 2000); return; } // Calculate dynamic speed based on player growth var baseSpeed = 6; // Base speed for level var growthSpeedMultiplier = 1 + (playerNumber - 1) * 0.015; // Speed increases by 1.5% per growth point (reduced from 5%) var currentLevelSpeedBonus = (currentLevel - 1) * 0.2; // Each level adds 20% base speed (reduced from 30%) var dynamicGameSpeed = (baseSpeed + currentLevelSpeedBonus) * growthSpeedMultiplier; // Cap the maximum speed to prevent it from becoming unplayable dynamicGameSpeed = Math.min(dynamicGameSpeed, baseSpeed * 2); // Cap at 2x base speed // Apply smooth speed transition gameSpeed += (dynamicGameSpeed - gameSpeed) * 0.05; // Slower transition for smoother gameplay (reduced from 0.1) // Increase speed and difficulty more aggressively over time speedIncreaseTimer++; if (speedIncreaseTimer >= 300) { // Every 5 seconds instead of 10 var speedIncrease = 0.3 + gameDistance / 6000; // Gradually increase the increment gameSpeed += speedIncrease; speedIncreaseTimer = 0; // Visual feedback for speed increase LK.effects.flashScreen(0x00FF00, 200); // Quick green flash } // Calculate dynamic spawn interval based on player growth var baseSpawnInterval = 120; var growthSpawnReduction = Math.floor(playerNumber / 10) * 5; // Reduce interval by 5 frames every 10 growth points (reduced scaling) var dynamicSpawnInterval = Math.max(60, baseSpawnInterval - growthSpawnReduction); // Minimum 60 frames (increased from 30) // Spawn math boxes spawnTimer++; if (spawnTimer >= Math.min(spawnInterval, dynamicSpawnInterval)) { spawnMathBoxes(); spawnTimer = 0; // Aggressively decrease spawn interval for more difficulty if (spawnInterval > 45) { // Faster decrease rate based on distance traveled var decreaseRate = Math.max(1, Math.floor(gameDistance / 1800)); spawnInterval -= decreaseRate; spawnInterval = Math.max(45, spawnInterval); // Minimum interval of 45 frames } } // Update math boxes and check collisions for (var i = mathBoxes.length - 1; i >= 0; i--) { var box = mathBoxes[i]; if (box.lastY === undefined) box.lastY = box.y; // Remove boxes that are off screen if (box.lastY <= 2732 && box.y > 2732) { box.destroy(); mathBoxes.splice(i, 1); continue; } // Check collision with player if (box.intersects(player)) { // Apply operation var newNumber = playerNumber; if (box.operation === 'multiply') { newNumber = playerNumber * box.value; // Grow character based on multiplication value player.grow(box.value, 'multiply'); } else if (box.operation === 'add') { newNumber = playerNumber + box.value; // Grow character based on addition value player.grow(box.value, 'add'); } else if (box.operation === 'subtract') { newNumber = playerNumber - box.value; // Shrink character based on subtraction value player.shrink(box.value, 'subtract'); } else if (box.operation === 'divide') { newNumber = Math.floor(playerNumber / box.value); // Shrink character based on division value player.shrink(box.value, 'divide'); } // Update player number playerNumber = Math.max(1, newNumber); // Ensure playerNumber stays at least 1 numberDisplay.setText(playerNumber.toString()); // Check game over condition if (playerNumber <= 0) { LK.getSound('gameOver').play(); LK.setScore(Math.floor(gameDistance / 60)); LK.showGameOver(); gameState = 'menu'; LK.setTimeout(function () { createMainMenu(); }, 2000); return; } // Play collect sound and remove box LK.getSound('collect').play(); LK.effects.flashObject(box, 0xFFFFFF, 300); box.destroy(); mathBoxes.splice(i, 1); continue; } box.lastY = box.y; } // Update speed for all boxes for (var j = 0; j < mathBoxes.length; j++) { mathBoxes[j].speed = gameSpeed; } // Animate environment objects with realistic parallax for (var k = 0; k < environmentObjects.length; k++) { var envObj = environmentObjects[k]; if (envObj.type === 'cloud') { envObj.obj.x += envObj.obj.moveSpeed; if (envObj.obj.x > 2200) { envObj.obj.x = -150; envObj.obj.y = 140 + Math.random() * 200; } // Move cloud shadow with cloud if (envObj.obj.shadowRef) { envObj.obj.shadowRef.x = envObj.obj.x + 15; envObj.obj.shadowRef.y = envObj.obj.y + 10; } } else if (envObj.type === 'cloudShadow') { envObj.obj.x += envObj.obj.moveSpeed; if (envObj.obj.x > 2200) { envObj.obj.x = -135; } } else if (envObj.type === 'sun') { // Gentle sun movement envObj.obj.x += 0.1; if (envObj.obj.x > 2100) { envObj.obj.x = -100; } envObj.obj.y += Math.sin(gameDistance * 0.01) * 0.2; } else if (envObj.type === 'mountain' || envObj.type === 'mountainSnow') { envObj.obj.y += gameSpeed * 0.05; // Very slow parallax for distant mountains if (envObj.obj.y > 2800) { envObj.obj.y = -400; } // Trees and buildings remain stationary for realistic cityscape } else if (envObj.type === 'grassDetail') { envObj.obj.y += gameSpeed * 0.8; // Grass details move with foreground if (envObj.obj.y > 2800) { envObj.obj.y = -100; envObj.obj.x = Math.random() * 2048; } } } // Animate road markings for (var m = 0; m < roadMarkings.length; m++) { roadMarkings[m].y += gameSpeed; if (roadMarkings[m].y > 2800) { roadMarkings[m].y = -50; } } // Update number display to show player number numberDisplay.setText(playerNumber.toString()); // Update number display position numberDisplay.y = player.y - 200; // Moved higher to accommodate taller character numberDisplay.x = player.x; };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
completedLevels: 1
});
/****
* Classes
****/
var MathBox = Container.expand(function (operation, value, lane) {
var self = Container.call(this);
self.operation = operation;
self.value = value;
self.lane = lane;
self.speed = gameSpeed;
// Create distinct hamburger visuals based on operation type
self.hamburgerParts = [];
if (operation === 'add') {
// ADD (+): Classic cheeseburger - green theme
var bottomBun = self.attachAsset('hamburgerBun', {
anchorX: 0.5,
anchorY: 0.5,
y: 40,
tint: 0x90EE90
});
var lettuce = self.attachAsset('hamburgerLettuce', {
anchorX: 0.5,
anchorY: 0.5,
y: 25,
tint: 0x32FF32
});
var cheese = self.attachAsset('hamburgerCheese', {
anchorX: 0.5,
anchorY: 0.5,
y: 15,
tint: 0xFFD700
});
var patty = self.attachAsset('hamburgerPatty', {
anchorX: 0.5,
anchorY: 0.5,
y: 5,
tint: 0x90EE90
});
var topBun = self.attachAsset('hamburgerBun', {
anchorX: 0.5,
anchorY: 0.5,
y: -10,
tint: 0x90EE90
});
self.hamburgerParts = [bottomBun, lettuce, cheese, patty, topBun];
} else if (operation === 'subtract') {
// SUBTRACT (-): Deconstructed burger - red theme
var bottomBun = self.attachAsset('hamburgerBun', {
anchorX: 0.5,
anchorY: 0.5,
y: 35,
tint: 0xFFB6C1
});
var tomato = self.attachAsset('hamburgerTomato', {
anchorX: 0.5,
anchorY: 0.5,
y: 20,
tint: 0xFF4444
});
var patty = self.attachAsset('hamburgerPatty', {
anchorX: 0.5,
anchorY: 0.5,
y: 10,
tint: 0xFF6666
});
var topBun = self.attachAsset('hamburgerBun', {
anchorX: 0.5,
anchorY: 0.5,
y: -5,
tint: 0xFFB6C1
});
self.hamburgerParts = [bottomBun, tomato, patty, topBun];
} else if (operation === 'multiply') {
// MULTIPLY (×): Double stack burger - blue theme
var bottomBun = self.attachAsset('hamburgerBun', {
anchorX: 0.5,
anchorY: 0.5,
y: 45,
tint: 0x87CEEB
});
var patty1 = self.attachAsset('hamburgerPatty', {
anchorX: 0.5,
anchorY: 0.5,
y: 30,
tint: 0x6699FF
});
var cheese1 = self.attachAsset('hamburgerCheese', {
anchorX: 0.5,
anchorY: 0.5,
y: 20,
tint: 0x4488FF
});
var patty2 = self.attachAsset('hamburgerPatty', {
anchorX: 0.5,
anchorY: 0.5,
y: 10,
tint: 0x6699FF
});
var cheese2 = self.attachAsset('hamburgerCheese', {
anchorX: 0.5,
anchorY: 0.5,
y: 0,
tint: 0x4488FF
});
var topBun = self.attachAsset('hamburgerBun', {
anchorX: 0.5,
anchorY: 0.5,
y: -15,
tint: 0x87CEEB
});
self.hamburgerParts = [bottomBun, patty1, cheese1, patty2, cheese2, topBun];
} else if (operation === 'divide') {
// DIVIDE (÷): Split burger - orange theme
var bottomBun = self.attachAsset('hamburgerBun', {
anchorX: 0.5,
anchorY: 0.5,
y: 30,
tint: 0xFFE4B5,
scaleX: 0.8
});
var lettuce = self.attachAsset('hamburgerLettuce', {
anchorX: 0.5,
anchorY: 0.5,
y: 20,
tint: 0xFFAA44,
scaleX: 0.7
});
var patty = self.attachAsset('hamburgerPatty', {
anchorX: 0.5,
anchorY: 0.5,
y: 10,
tint: 0xFF8844,
scaleX: 0.6
});
var topBun = self.attachAsset('hamburgerBun', {
anchorX: 0.5,
anchorY: 0.5,
y: 0,
tint: 0xFFE4B5,
scaleX: 0.5
});
self.hamburgerParts = [bottomBun, lettuce, patty, topBun];
}
// Create operation text with better styling and larger size
var operationText = '';
if (operation === 'multiply') operationText = '×' + value;else if (operation === 'add') operationText = '+' + value;else if (operation === 'subtract') operationText = '-' + value;else if (operation === 'divide') operationText = '÷' + value;
var textDisplay = new Text2(operationText, {
size: 58,
fill: 0x000000,
stroke: 0xFFFFFF,
strokeThickness: 3
});
textDisplay.anchor.set(0.5, 0.5);
textDisplay.y = 10; // Position in center of hamburger
self.addChild(textDisplay);
// Add floating animation
self.animationTimer = Math.random() * Math.PI * 2;
self.update = function () {
self.y += self.speed;
// Add gentle floating animation
self.animationTimer += 0.1;
var floatOffset = Math.sin(self.animationTimer) * 2;
for (var i = 0; i < self.hamburgerParts.length; i++) {
self.hamburgerParts[i].y += floatOffset * 0.1; // Subtle float for all parts
}
textDisplay.y = 10 + floatOffset;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
// Create human character with body parts
var legs = self.attachAsset('runnerLegs', {
anchorX: 0.5,
anchorY: 1.0,
y: 0
});
var body = self.attachAsset('runnerBody', {
anchorX: 0.5,
anchorY: 1.0,
y: -60
});
var head = self.attachAsset('runnerHead', {
anchorX: 0.5,
anchorY: 1.0,
y: -140
});
self.targetX = 1024; // Center position
self.moveSpeed = 8;
self.animationTimer = 0;
self.currentScale = 1.0; // Track current scale
self.bodyParts = [legs, body, head]; // Store references for scaling
// Method to grow character smoothly based on multiplication and addition
self.grow = function (value, operation) {
var growthFactor = 1;
if (operation === 'multiply') {
growthFactor = 1 + value * 0.15; // Grow based on multiplication value
} else if (operation === 'add') {
growthFactor = 1 + value * 0.01; // Small growth based on addition value
}
var newScale = self.currentScale * growthFactor;
newScale = Math.max(0.3, Math.min(newScale, 3.0)); // Clamp between 0.3x and 3x
if (newScale !== self.currentScale) {
// Calculate growth increase based on actual player number change
var growthIncrease = playerNumber * 100; // Use actual player number for growth
currentGrowth += Math.round(growthIncrease);
// Animate scale change smoothly
for (var i = 0; i < self.bodyParts.length; i++) {
tween(self.bodyParts[i], {
scaleX: newScale,
scaleY: newScale
}, {
duration: 400,
easing: tween.easeOut
});
}
self.currentScale = newScale;
}
};
// Method to shrink character smoothly based on subtraction and division
self.shrink = function (value, operation) {
var shrinkFactor = 1;
if (operation === 'subtract') {
shrinkFactor = 1 + value * 0.01; // Shrink based on subtraction value
} else if (operation === 'divide') {
shrinkFactor = 1 + value * 0.1; // Shrink based on division value
}
var newScale = self.currentScale / shrinkFactor;
newScale = Math.max(0.3, Math.min(newScale, 3.0)); // Clamp between 0.3x and 3x
if (newScale !== self.currentScale) {
// Calculate growth decrease based on player number loss
var growthDecrease = Math.max(0, playerNumber * 50); // Smaller penalty for shrinking
currentGrowth = Math.max(0, currentGrowth - Math.round(growthDecrease));
// Animate scale change smoothly
for (var i = 0; i < self.bodyParts.length; i++) {
tween(self.bodyParts[i], {
scaleX: newScale,
scaleY: newScale
}, {
duration: 400,
easing: tween.easeOut
});
}
self.currentScale = newScale;
}
};
self.update = function () {
// Smooth movement towards target position
var diff = self.targetX - self.x;
if (Math.abs(diff) > 2) {
self.x += diff * 0.15;
} else {
self.x = self.targetX;
}
// Simple running animation - bob the character
self.animationTimer += 0.3;
var bobOffset = Math.sin(self.animationTimer) * 3;
head.y = -140 + bobOffset;
body.y = -60 + bobOffset * 0.5;
legs.y = bobOffset * 0.3;
// Leg movement animation
legs.rotation = Math.sin(self.animationTimer * 2) * 0.1;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB // Sky blue background
});
/****
* Game Code
****/
// Game state management
var gameState = 'menu'; // 'menu', 'levelSelect', 'playing', 'paused'
var currentLevel = 1;
var maxLevel = 100;
var completedLevels = storage.completedLevels || 1; // Track highest completed level
var pauseMenuButtons = []; // Store pause menu button references
// Game variables (moved to global scope)
var player;
var playerNumber = 1; // Always start with 1
var gameSpeed = 6;
var mathBoxes = [];
var lanes = [600, 1024, 1448]; // Left, center, right lanes
var currentLane = 1; // Start in center
var spawnTimer = 0;
var spawnInterval = 120; // Spawn every 2 seconds at 60fps
var gameDistance = 0;
var speedIncreaseTimer = 0;
var environmentObjects = [];
// Level system variables
var levelTimer = 30 * 60; // 30 seconds at 60fps
var initialScale = 1.0; // Track initial player scale
var currentGrowth = 0; // Track total growth achieved
var targetGrowth = 100; // Target growth to win level (reasonable for player number)
// UI elements
var numberDisplay;
var timerDisplay;
var growthDisplay;
var roadMarkings = [];
// Main menu setup
function createMainMenu() {
game.removeChildren();
// Create menu background
var menuBg = game.addChild(LK.getAsset('grass', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
alpha: 0.8
}));
// Game title
var titleText = new Text2('MATH RUNNER', {
size: 120,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 6
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 600;
game.addChild(titleText);
// Play button
var playButton = game.addChild(LK.getAsset('hamburgerBun', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1200,
scaleX: 4,
scaleY: 2,
tint: 0x00FF00
}));
var playText = new Text2('PLAY', {
size: 80,
fill: 0x000000
});
playText.anchor.set(0.5, 0.5);
playText.x = 1024;
playText.y = 1200;
game.addChild(playText);
// Level select button
var levelButton = game.addChild(LK.getAsset('hamburgerBun', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1500,
scaleX: 4,
scaleY: 2,
tint: 0x0088FF
}));
var levelText = new Text2('SELECT LEVEL', {
size: 60,
fill: 0x000000
});
levelText.anchor.set(0.5, 0.5);
levelText.x = 1024;
levelText.y = 1500;
game.addChild(levelText);
// Store button references for interaction
game.playButton = playButton;
game.levelButton = levelButton;
game.playText = playText;
game.levelText = levelText;
}
// Level selection screen with pagination
var currentLevelPage = 1;
var levelsPerPage = 10;
function createLevelSelect() {
game.removeChildren();
// Level select background
var levelBg = game.addChild(LK.getAsset('grass', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
alpha: 0.6
}));
// Level select title
var levelTitle = new Text2('SELECT LEVEL - Page ' + currentLevelPage + ' of ' + Math.ceil(maxLevel / levelsPerPage), {
size: 80,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4
});
levelTitle.anchor.set(0.5, 0.5);
levelTitle.x = 1024;
levelTitle.y = 300;
game.addChild(levelTitle);
// Create level buttons for current page
game.levelButtons = [];
var startLevel = (currentLevelPage - 1) * levelsPerPage + 1;
var endLevel = Math.min(startLevel + levelsPerPage - 1, maxLevel);
// Create 2 rows of 5 levels each
for (var i = startLevel; i <= endLevel; i++) {
var row = Math.floor((i - startLevel) / 5);
var col = (i - startLevel) % 5;
var btnX = 300 + col * 300;
var btnY = 600 + row * 200;
var isLocked = i > completedLevels; // Lock levels that haven't been unlocked yet
var levelBtn = game.addChild(LK.getAsset('hamburgerBun', {
anchorX: 0.5,
anchorY: 0.5,
x: btnX,
y: btnY,
scaleX: 2,
scaleY: 2,
tint: isLocked ? 0x666666 : i <= 20 ? 0x90EE90 : i <= 50 ? 0xFFD700 : i <= 80 ? 0xFF8C00 : 0xFF4444,
alpha: isLocked ? 0.5 : 1.0
}));
var levelNumText = new Text2(isLocked ? 'LOCKED' : i.toString(), {
size: isLocked ? 30 : 60,
fill: isLocked ? 0x999999 : 0x000000
});
levelNumText.anchor.set(0.5, 0.5);
levelNumText.x = btnX;
levelNumText.y = btnY;
game.addChild(levelNumText);
levelBtn.levelNumber = i;
levelBtn.isLocked = isLocked;
game.levelButtons.push(levelBtn);
}
// Navigation buttons
if (currentLevelPage > 1) {
var prevButton = game.addChild(LK.getAsset('hamburgerBun', {
anchorX: 0.5,
anchorY: 0.5,
x: 400,
y: 1200,
scaleX: 2,
scaleY: 1.5,
tint: 0x4169E1
}));
var prevText = new Text2('PREV', {
size: 50,
fill: 0x000000
});
prevText.anchor.set(0.5, 0.5);
prevText.x = 400;
prevText.y = 1200;
game.addChild(prevText);
game.prevButton = prevButton;
}
if (currentLevelPage < Math.ceil(maxLevel / levelsPerPage)) {
var nextButton = game.addChild(LK.getAsset('hamburgerBun', {
anchorX: 0.5,
anchorY: 0.5,
x: 1648,
y: 1200,
scaleX: 2,
scaleY: 1.5,
tint: 0x4169E1
}));
var nextText = new Text2('NEXT', {
size: 50,
fill: 0x000000
});
nextText.anchor.set(0.5, 0.5);
nextText.x = 1648;
nextText.y = 1200;
game.addChild(nextText);
game.nextButton = nextButton;
}
// Back button
var backButton = game.addChild(LK.getAsset('hamburgerBun', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1500,
scaleX: 3,
scaleY: 1.5,
tint: 0xFF4444
}));
var backText = new Text2('BACK', {
size: 60,
fill: 0x000000
});
backText.anchor.set(0.5, 0.5);
backText.x = 1024;
backText.y = 1500;
game.addChild(backText);
game.backButton = backButton;
game.backText = backText;
}
// Start game with selected level
function startGame(level) {
currentLevel = level;
gameState = 'playing';
// Clear menu elements
game.removeChildren();
// Progressive difficulty scaling for all 100 levels
var baseDifficulty = Math.floor((level - 1) / 10); // Difficulty tier (0-9)
var levelInTier = (level - 1) % 10 + 1; // Position within tier (1-10)
// Target growth scaling: much higher growth targets for difficulty
targetGrowth = Math.floor(150 + (level - 1) * 35 + Math.pow(baseDifficulty, 2) * 100);
// Timer scaling: much shorter time limits for increased challenge
var baseTime = Math.max(15, 45 - baseDifficulty * 4); // Shorter base time
levelTimer = (baseTime + Math.floor(levelInTier / 2)) * 60; // Less bonus time within tier
// Speed scaling: progressive increase with level
gameSpeed = 6 + baseDifficulty * 0.8 + (levelInTier - 1) * 0.2;
// Spawn interval scaling: faster spawning at higher levels
spawnInterval = Math.max(30, 120 - baseDifficulty * 8 - levelInTier * 2);
// Initialize game world
initializeGameWorld();
}
// Initialize the game world (moved from main code)
function initializeGameWorld() {
// Reset game state to ensure fresh start
playerNumber = 1; // Explicitly reset to 1 at game initialization
mathBoxes = [];
currentLane = 1;
spawnTimer = 0;
spawnInterval = 120;
gameDistance = 0;
speedIncreaseTimer = 0;
environmentObjects = [];
currentGrowth = 0;
roadMarkings = [];
// Create realistic environment layers
// Sky background (already set in game initialization)
// Create layered sky with gradient
var skyGradientTop = game.addChild(LK.getAsset('skyGradient', {
anchorX: 0.5,
anchorY: 0.0,
x: 1024,
y: 0,
tint: 0x87ceeb,
alpha: 1.0
}));
var skyGradientBottom = game.addChild(LK.getAsset('skyGradient', {
anchorX: 0.5,
anchorY: 0.0,
x: 1024,
y: 400,
tint: 0xb0e0e6,
alpha: 0.8
}));
// Add sun/moon
var sunMoon = game.addChild(LK.getAsset('sun', {
anchorX: 0.5,
anchorY: 0.5,
x: 1700,
y: 300,
alpha: 0.9,
tint: 0xffd700
}));
environmentObjects.push({
type: 'sun',
obj: sunMoon
});
// Add distant mountains
for (var i = 0; i < 4; i++) {
var mountain = game.addChild(LK.getAsset('mountain', {
anchorX: 0.5,
anchorY: 1.0,
x: 200 + i * 500,
y: 700,
alpha: 0.4,
scaleY: 0.6 + Math.random() * 0.4
}));
var mountainSnow = game.addChild(LK.getAsset('mountainSnow', {
anchorX: 0.5,
anchorY: 1.0,
x: 200 + i * 500,
y: 600 - Math.random() * 50,
alpha: 0.6,
scaleY: 0.3 + Math.random() * 0.2
}));
environmentObjects.push({
type: 'mountain',
obj: mountain
});
environmentObjects.push({
type: 'mountainSnow',
obj: mountainSnow
});
}
// Add realistic moving clouds with shadows
for (var i = 0; i < 5; i++) {
// Cloud shadow
var cloudShadow = game.addChild(LK.getAsset('cloudShadow', {
anchorX: 0.5,
anchorY: 0.5,
x: Math.random() * 2048,
y: 150 + Math.random() * 200,
alpha: 0.3
}));
// Main cloud
var cloud = game.addChild(LK.getAsset('cloud', {
anchorX: 0.5,
anchorY: 0.5,
x: Math.random() * 2048,
y: 140 + Math.random() * 200,
alpha: 0.85,
scaleX: 0.8 + Math.random() * 0.6,
scaleY: 0.7 + Math.random() * 0.4
}));
cloud.moveSpeed = 0.3 + Math.random() * 0.7;
cloudShadow.moveSpeed = cloud.moveSpeed;
cloud.shadowRef = cloudShadow;
environmentObjects.push({
type: 'cloud',
obj: cloud
});
environmentObjects.push({
type: 'cloudShadow',
obj: cloudShadow
});
}
// Create grass background with details
var grassBackground = game.addChild(LK.getAsset('grass', {
anchorX: 0.5,
anchorY: 1.0,
x: 1024,
y: 2732,
tint: 0x228b22
}));
// Add grass detail patches
for (var i = 0; i < 12; i++) {
var grassDetail = game.addChild(LK.getAsset('grassDetail', {
anchorX: 0.5,
anchorY: 1.0,
x: Math.random() * 2048,
y: 2400 + Math.random() * 300,
alpha: 0.6,
tint: 0x32cd32 + Math.floor(Math.random() * 0x004400),
scaleX: 0.5 + Math.random() * 1.0,
scaleY: 0.7 + Math.random() * 0.6,
rotation: Math.random() * Math.PI * 2
}));
environmentObjects.push({
type: 'grassDetail',
obj: grassDetail
});
}
// Create road surface
var road = game.addChild(LK.getAsset('road', {
anchorX: 0.5,
anchorY: 1.0,
x: 1024,
y: 2732
}));
// Add road lane markings
for (var i = 0; i < 8; i++) {
var marking = game.addChild(LK.getAsset('roadLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 200 + i * 350
}));
roadMarkings.push(marking);
}
// Add trees along the sides of the road - stationary realistic placement
for (var i = 0; i < 12; i++) {
// Left side trees with varied positioning
var leftTreeX = 150 + Math.random() * 200;
var leftTreeY = 1600 + Math.random() * 800;
var leftTree = game.addChild(LK.getAsset('tree', {
anchorX: 0.5,
anchorY: 1.0,
x: leftTreeX,
y: leftTreeY,
scaleX: 0.8 + Math.random() * 0.6,
scaleY: 0.9 + Math.random() * 0.4
}));
var leftFoliage = game.addChild(LK.getAsset('treeFoliage', {
anchorX: 0.5,
anchorY: 1.0,
x: leftTreeX,
y: leftTreeY - 100 - Math.random() * 30,
scaleX: 0.8 + Math.random() * 0.5,
scaleY: 0.8 + Math.random() * 0.4,
tint: 0x228b22 + Math.floor(Math.random() * 0x005500)
}));
// Right side trees with varied positioning
var rightTreeX = 1700 + Math.random() * 200;
var rightTreeY = 1800 + Math.random() * 700;
var rightTree = game.addChild(LK.getAsset('tree', {
anchorX: 0.5,
anchorY: 1.0,
x: rightTreeX,
y: rightTreeY,
scaleX: 0.8 + Math.random() * 0.6,
scaleY: 0.9 + Math.random() * 0.4
}));
var rightFoliage = game.addChild(LK.getAsset('treeFoliage', {
anchorX: 0.5,
anchorY: 1.0,
x: rightTreeX,
y: rightTreeY - 100 - Math.random() * 30,
scaleX: 0.8 + Math.random() * 0.5,
scaleY: 0.8 + Math.random() * 0.4,
tint: 0x228b22 + Math.floor(Math.random() * 0x005500)
}));
}
// Add city buildings in background with realistic stationary skyline
for (var i = 0; i < 15; i++) {
var buildingHeight = 0.6 + Math.random() * 1.8;
var buildingX = 100 + i * 140 + Math.random() * 80;
var building = game.addChild(LK.getAsset('building', {
anchorX: 0.5,
anchorY: 1.0,
x: buildingX,
y: 750,
alpha: 0.25 + Math.random() * 0.15,
tint: 0x555555 + Math.floor(Math.random() * 0x444444),
scaleY: buildingHeight,
scaleX: 0.8 + Math.random() * 0.6
}));
// Add windows to buildings with more realistic patterns
var windowRows = Math.floor(buildingHeight * 10);
var windowCols = 3 + Math.floor(Math.random() * 4);
for (var row = 0; row < windowRows; row++) {
for (var col = 0; col < windowCols; col++) {
if (Math.random() > 0.4) {
// Varied window lighting patterns
var window = game.addChild(LK.getAsset('buildingWindow', {
anchorX: 0.5,
anchorY: 0.5,
x: buildingX - 40 + col * 25,
y: building.y - 40 - row * 30,
alpha: 0.6 + Math.random() * 0.4,
tint: Math.random() > 0.7 ? 0xffffff : 0xffff88,
scaleX: 0.8 + Math.random() * 0.4,
scaleY: 0.9 + Math.random() * 0.3
}));
}
}
}
}
// Create player
player = game.addChild(new Player());
player.x = lanes[currentLane];
player.y = 2400;
// UI Elements
numberDisplay = new Text2(playerNumber.toString(), {
size: 80,
fill: 0xFFFFFF
});
numberDisplay.anchor.set(0.5, 0.5);
numberDisplay.x = 1024;
numberDisplay.y = 300;
game.addChild(numberDisplay);
// Add timer display
timerDisplay = new Text2('Time: ' + Math.ceil(levelTimer / 60), {
size: 50,
fill: 0xFFFF00
});
timerDisplay.anchor.set(0.5, 0);
timerDisplay.y = 60;
LK.gui.top.addChild(timerDisplay);
// Add growth display
growthDisplay = new Text2('Growth: 0 / ' + targetGrowth, {
size: 40,
fill: 0x00FF00
});
growthDisplay.anchor.set(0.5, 0);
growthDisplay.y = 120;
LK.gui.top.addChild(growthDisplay);
// Add pause button
var pauseButton = new Text2('||', {
size: 60,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3
});
pauseButton.anchor.set(0.5, 0.5);
pauseButton.x = 1900;
pauseButton.y = 100;
game.addChild(pauseButton);
game.pauseButton = pauseButton;
}
// Create pause menu overlay
function createPauseMenu() {
// Create semi-transparent overlay
var overlay = game.addChild(LK.getAsset('grass', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
alpha: 0.7,
tint: 0x000000
}));
// Pause title
var pauseTitle = new Text2('GAME PAUSED', {
size: 100,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 5
});
pauseTitle.anchor.set(0.5, 0.5);
pauseTitle.x = 1024;
pauseTitle.y = 800;
game.addChild(pauseTitle);
// Continue button
var continueButton = game.addChild(LK.getAsset('hamburgerBun', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1200,
scaleX: 4,
scaleY: 2,
tint: 0x00FF00
}));
var continueText = new Text2('CONTINUE', {
size: 70,
fill: 0x000000
});
continueText.anchor.set(0.5, 0.5);
continueText.x = 1024;
continueText.y = 1200;
game.addChild(continueText);
// Main menu button
var mainMenuButton = game.addChild(LK.getAsset('hamburgerBun', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1500,
scaleX: 4,
scaleY: 2,
tint: 0xFF4444
}));
var mainMenuText = new Text2('MAIN MENU', {
size: 70,
fill: 0x000000
});
mainMenuText.anchor.set(0.5, 0.5);
mainMenuText.x = 1024;
mainMenuText.y = 1500;
game.addChild(mainMenuText);
// Store references for interaction
pauseMenuButtons = [overlay, pauseTitle, continueButton, continueText, mainMenuButton, mainMenuText];
game.continueButton = continueButton;
game.mainMenuButton = mainMenuButton;
}
// Remove pause menu elements
function removePauseMenu() {
for (var i = 0; i < pauseMenuButtons.length; i++) {
if (pauseMenuButtons[i].parent) {
pauseMenuButtons[i].parent.removeChild(pauseMenuButtons[i]);
}
}
pauseMenuButtons = [];
game.continueButton = null;
game.mainMenuButton = null;
}
// Handle pause button press (LK engine automatically calls this when pause is pressed)
LK.on('pause', function () {
if (gameState === 'playing') {
gameState = 'paused';
createPauseMenu();
}
});
// Initialize main menu on game start
createMainMenu();
// Generate random math operations with progressive difficulty based on current level
function generateOperation() {
var levelDifficulty = Math.floor(gameDistance / 1200); // Time-based difficulty within level
var totalDifficulty = Math.floor((currentLevel - 1) / 5) + levelDifficulty; // Combined difficulty
// Equal distribution of all operations at all levels
var operations = ['multiply', 'add', 'subtract', 'divide'];
var operation = operations[Math.floor(Math.random() * operations.length)];
var value;
if (operation === 'multiply') {
var baseMultiplier = 2 + Math.floor(currentLevel / 10);
value = Math.floor(Math.random() * (1 + totalDifficulty)) + baseMultiplier;
value = Math.min(value, Math.max(6, Math.floor(currentLevel / 5))); // Scale cap with level
} else if (operation === 'add') {
var baseAdd = 5 + Math.floor(currentLevel / 3) + totalDifficulty * 3;
value = Math.floor(Math.random() * (10 + currentLevel)) + baseAdd;
value = Math.min(value, 50 + currentLevel * 2); // Higher values at higher levels
} else if (operation === 'subtract') {
var baseSub = 3 + Math.floor(currentLevel / 4) + totalDifficulty * 2;
value = Math.floor(Math.random() * (5 + Math.floor(currentLevel / 2))) + baseSub;
value = Math.min(value, Math.max(15, Math.floor(currentLevel / 2))); // Increase subtract danger
} else if (operation === 'divide') {
var baseDiv = 2 + Math.floor(currentLevel / 15);
value = Math.floor(Math.random() * (2 + Math.floor(totalDifficulty / 2))) + baseDiv;
value = Math.min(value, Math.max(5, Math.floor(currentLevel / 8))); // Scale division difficulty
}
return {
operation: operation,
value: value
};
}
// Spawn math boxes
function spawnMathBoxes() {
// Spawn 2-3 boxes across different lanes
var numBoxes = Math.floor(Math.random() * 2) + 2; // 2 or 3 boxes
var usedLanes = [];
for (var i = 0; i < numBoxes; i++) {
var laneIndex;
do {
laneIndex = Math.floor(Math.random() * 3);
} while (usedLanes.indexOf(laneIndex) !== -1);
usedLanes.push(laneIndex);
var operation = generateOperation();
var mathBox = new MathBox(operation.operation, operation.value, laneIndex);
mathBox.x = lanes[laneIndex];
mathBox.y = -100;
mathBoxes.push(mathBox);
game.addChild(mathBox);
}
}
// Handle player movement and menu interactions
game.down = function (x, y, obj) {
if (gameState === 'menu') {
// Check if play button was clicked - using coordinate bounds checking
if (game.playButton && x >= 824 && x <= 1224 && y >= 1100 && y <= 1300) {
startGame(1); // Start level 1
}
// Check if level select button was clicked
else if (game.levelButton && x >= 824 && x <= 1224 && y >= 1400 && y <= 1600) {
gameState = 'levelSelect';
createLevelSelect();
}
} else if (gameState === 'levelSelect') {
// Check level buttons
if (game.levelButtons) {
for (var i = 0; i < game.levelButtons.length; i++) {
var startLevel = (currentLevelPage - 1) * levelsPerPage + 1;
var levelIndex = game.levelButtons[i].levelNumber - startLevel;
var row = Math.floor(levelIndex / 5);
var col = levelIndex % 5;
var btnX = 300 + col * 300;
var btnY = 600 + row * 200;
if (x >= btnX - 100 && x <= btnX + 100 && y >= btnY - 50 && y <= btnY + 50) {
// Only allow playing unlocked levels
if (!game.levelButtons[i].isLocked) {
startGame(game.levelButtons[i].levelNumber);
return;
}
}
}
}
// Check navigation buttons
if (game.prevButton && x >= 300 && x <= 500 && y >= 1150 && y <= 1250) {
currentLevelPage--;
createLevelSelect();
return;
}
if (game.nextButton && x >= 1548 && x <= 1748 && y >= 1150 && y <= 1250) {
currentLevelPage++;
createLevelSelect();
return;
}
// Check back button
if (game.backButton && x >= 724 && x <= 1324 && y >= 1450 && y <= 1550) {
gameState = 'menu';
currentLevelPage = 1; // Reset to first page
createMainMenu();
}
} else if (gameState === 'paused') {
// Check if continue button was clicked
if (game.continueButton && x >= 824 && x <= 1224 && y >= 1100 && y <= 1300) {
gameState = 'playing';
removePauseMenu();
}
// Check if main menu button was clicked
else if (game.mainMenuButton && x >= 824 && x <= 1224 && y >= 1400 && y <= 1600) {
gameState = 'menu';
removePauseMenu();
createMainMenu();
}
} else if (gameState === 'levelComplete') {
// Check if next level button was clicked
if (game.nextLevelButton && x >= 824 && x <= 1224 && y >= 1500 && y <= 1700) {
startGame(currentLevel + 1); // Progress to next level
}
} else if (gameState === 'playing') {
// Check if pause button was clicked (top right area)
if (x >= 1900 && x <= 2048 && y >= 20 && y <= 120) {
gameState = 'paused';
createPauseMenu();
return;
}
if (x < 1024 && currentLane > 0) {
// Move left
currentLane--;
player.targetX = lanes[currentLane];
} else if (x >= 1024 && currentLane < 2) {
// Move right
currentLane++;
player.targetX = lanes[currentLane];
}
}
};
// Main game update
game.update = function () {
// Only update game logic when actually playing
if (gameState !== 'playing' && gameState !== 'levelComplete') {
return;
}
// If in level complete state, don't update game mechanics
if (gameState === 'levelComplete') {
return;
}
gameDistance++;
// Update level timer
levelTimer--;
var secondsLeft = Math.ceil(levelTimer / 60);
timerDisplay.setText('Time: ' + secondsLeft);
// Update growth display
growthDisplay.setText('Growth: ' + playerNumber + ' / ' + targetGrowth);
// Check win condition
if (playerNumber >= targetGrowth) {
LK.setScore(playerNumber);
// Unlock next level if current level is newly completed
if (currentLevel >= completedLevels) {
completedLevels = currentLevel + 1;
storage.completedLevels = completedLevels; // Save progress
}
// Check if there are more levels to play
if (currentLevel < maxLevel) {
LK.showYouWin();
// Wait a moment for the you win screen to appear, then add next level button
LK.setTimeout(function () {
// Create next level button
var nextLevelButton = game.addChild(LK.getAsset('hamburgerBun', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1600,
scaleX: 4,
scaleY: 2,
tint: 0x00FF00
}));
var nextLevelText = new Text2('NEXT LEVEL', {
size: 70,
fill: 0x000000
});
nextLevelText.anchor.set(0.5, 0.5);
nextLevelText.x = 1024;
nextLevelText.y = 1600;
game.addChild(nextLevelText);
// Store button references for interaction
game.nextLevelButton = nextLevelButton;
game.nextLevelText = nextLevelText;
}, 1000);
gameState = 'levelComplete';
return;
} else {
// Player completed all 100 levels!
var finalText = new Text2('CONGRATULATIONS!\nYOU COMPLETED ALL 100 LEVELS!', {
size: 80,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 4,
align: 'center'
});
finalText.anchor.set(0.5, 0.5);
finalText.x = 1024;
finalText.y = 1366;
game.addChild(finalText);
LK.showYouWin();
gameState = 'menu';
LK.setTimeout(function () {
createMainMenu();
}, 4000);
return;
}
}
// Check time up condition
if (levelTimer <= 0) {
LK.setScore(playerNumber);
LK.showGameOver();
gameState = 'menu';
LK.setTimeout(function () {
createMainMenu();
}, 2000);
return;
}
// Calculate dynamic speed based on player growth
var baseSpeed = 6; // Base speed for level
var growthSpeedMultiplier = 1 + (playerNumber - 1) * 0.015; // Speed increases by 1.5% per growth point (reduced from 5%)
var currentLevelSpeedBonus = (currentLevel - 1) * 0.2; // Each level adds 20% base speed (reduced from 30%)
var dynamicGameSpeed = (baseSpeed + currentLevelSpeedBonus) * growthSpeedMultiplier;
// Cap the maximum speed to prevent it from becoming unplayable
dynamicGameSpeed = Math.min(dynamicGameSpeed, baseSpeed * 2); // Cap at 2x base speed
// Apply smooth speed transition
gameSpeed += (dynamicGameSpeed - gameSpeed) * 0.05; // Slower transition for smoother gameplay (reduced from 0.1)
// Increase speed and difficulty more aggressively over time
speedIncreaseTimer++;
if (speedIncreaseTimer >= 300) {
// Every 5 seconds instead of 10
var speedIncrease = 0.3 + gameDistance / 6000; // Gradually increase the increment
gameSpeed += speedIncrease;
speedIncreaseTimer = 0;
// Visual feedback for speed increase
LK.effects.flashScreen(0x00FF00, 200); // Quick green flash
}
// Calculate dynamic spawn interval based on player growth
var baseSpawnInterval = 120;
var growthSpawnReduction = Math.floor(playerNumber / 10) * 5; // Reduce interval by 5 frames every 10 growth points (reduced scaling)
var dynamicSpawnInterval = Math.max(60, baseSpawnInterval - growthSpawnReduction); // Minimum 60 frames (increased from 30)
// Spawn math boxes
spawnTimer++;
if (spawnTimer >= Math.min(spawnInterval, dynamicSpawnInterval)) {
spawnMathBoxes();
spawnTimer = 0;
// Aggressively decrease spawn interval for more difficulty
if (spawnInterval > 45) {
// Faster decrease rate based on distance traveled
var decreaseRate = Math.max(1, Math.floor(gameDistance / 1800));
spawnInterval -= decreaseRate;
spawnInterval = Math.max(45, spawnInterval); // Minimum interval of 45 frames
}
}
// Update math boxes and check collisions
for (var i = mathBoxes.length - 1; i >= 0; i--) {
var box = mathBoxes[i];
if (box.lastY === undefined) box.lastY = box.y;
// Remove boxes that are off screen
if (box.lastY <= 2732 && box.y > 2732) {
box.destroy();
mathBoxes.splice(i, 1);
continue;
}
// Check collision with player
if (box.intersects(player)) {
// Apply operation
var newNumber = playerNumber;
if (box.operation === 'multiply') {
newNumber = playerNumber * box.value;
// Grow character based on multiplication value
player.grow(box.value, 'multiply');
} else if (box.operation === 'add') {
newNumber = playerNumber + box.value;
// Grow character based on addition value
player.grow(box.value, 'add');
} else if (box.operation === 'subtract') {
newNumber = playerNumber - box.value;
// Shrink character based on subtraction value
player.shrink(box.value, 'subtract');
} else if (box.operation === 'divide') {
newNumber = Math.floor(playerNumber / box.value);
// Shrink character based on division value
player.shrink(box.value, 'divide');
}
// Update player number
playerNumber = Math.max(1, newNumber); // Ensure playerNumber stays at least 1
numberDisplay.setText(playerNumber.toString());
// Check game over condition
if (playerNumber <= 0) {
LK.getSound('gameOver').play();
LK.setScore(Math.floor(gameDistance / 60));
LK.showGameOver();
gameState = 'menu';
LK.setTimeout(function () {
createMainMenu();
}, 2000);
return;
}
// Play collect sound and remove box
LK.getSound('collect').play();
LK.effects.flashObject(box, 0xFFFFFF, 300);
box.destroy();
mathBoxes.splice(i, 1);
continue;
}
box.lastY = box.y;
}
// Update speed for all boxes
for (var j = 0; j < mathBoxes.length; j++) {
mathBoxes[j].speed = gameSpeed;
}
// Animate environment objects with realistic parallax
for (var k = 0; k < environmentObjects.length; k++) {
var envObj = environmentObjects[k];
if (envObj.type === 'cloud') {
envObj.obj.x += envObj.obj.moveSpeed;
if (envObj.obj.x > 2200) {
envObj.obj.x = -150;
envObj.obj.y = 140 + Math.random() * 200;
}
// Move cloud shadow with cloud
if (envObj.obj.shadowRef) {
envObj.obj.shadowRef.x = envObj.obj.x + 15;
envObj.obj.shadowRef.y = envObj.obj.y + 10;
}
} else if (envObj.type === 'cloudShadow') {
envObj.obj.x += envObj.obj.moveSpeed;
if (envObj.obj.x > 2200) {
envObj.obj.x = -135;
}
} else if (envObj.type === 'sun') {
// Gentle sun movement
envObj.obj.x += 0.1;
if (envObj.obj.x > 2100) {
envObj.obj.x = -100;
}
envObj.obj.y += Math.sin(gameDistance * 0.01) * 0.2;
} else if (envObj.type === 'mountain' || envObj.type === 'mountainSnow') {
envObj.obj.y += gameSpeed * 0.05; // Very slow parallax for distant mountains
if (envObj.obj.y > 2800) {
envObj.obj.y = -400;
}
// Trees and buildings remain stationary for realistic cityscape
} else if (envObj.type === 'grassDetail') {
envObj.obj.y += gameSpeed * 0.8; // Grass details move with foreground
if (envObj.obj.y > 2800) {
envObj.obj.y = -100;
envObj.obj.x = Math.random() * 2048;
}
}
}
// Animate road markings
for (var m = 0; m < roadMarkings.length; m++) {
roadMarkings[m].y += gameSpeed;
if (roadMarkings[m].y > 2800) {
roadMarkings[m].y = -50;
}
}
// Update number display to show player number
numberDisplay.setText(playerNumber.toString());
// Update number display position
numberDisplay.y = player.y - 200; // Moved higher to accommodate taller character
numberDisplay.x = player.x;
};