/****
* 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;
};