User prompt
her coin 10 puan kazandırsın ve her karakter her coin aldığında puanı toplanarak artsın. sağ üst köşede puanı yazsın.
Code edit (1 edits merged)
Please save this source code
User prompt
Math Runner Academy
Initial prompt
Create a 3-lane endless runner game similar to Subway Surfers. The main character is a 10-year-old boy viewed from behind, running forward in a colorful school-themed environment. The player controls the character using left and right arrow keys on the keyboard to switch between the three lanes. On the path, there are obstacles like backpacks, desks, and fences that must be avoided by switching lanes. There are also collectible gamepad-shaped icons scattered along the way that work like coins and give points when collected. If the player hits an obstacle, the game immediately pauses and displays a multiplication question (e.g., “7 x 8 = ?”) with three answer options. The player must choose the correct answer within 9 seconds. If the answer is correct, the character continues running and the score multiplier increases (1.1x, 1.2x, 1.3x, etc.). If the answer is incorrect or time runs out, the multiplier resets to 1.0 and the character receives a short penalty like slowing down or stumbling. The game should feature child-friendly cartoon-style graphics, smooth animations, and fun background music. Over time, the running speed should gradually increase to make the game more challenging. The main objective is to collect as many points as possible by dodging obstacles, collecting gamepad coins, and solving multiplication questions correctly.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Coin = Container.expand(function () { var self = Container.call(this); var coinGraphics = self.attachAsset('coin', { anchorX: 0.5, anchorY: 0.5 }); self.speed = gameSpeed; self.update = function () { self.y += self.speed; }; return self; }); var Obstacle = Container.expand(function (type) { var self = Container.call(this); var assetName = 'obstacle' + (type || 1); var obstacleGraphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 1.0 }); self.speed = gameSpeed; self.update = function () { self.y += self.speed; }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 1.0 }); self.lane = 1; // 0=left, 1=center, 2=right self.targetX = 0; self.update = function () { // Smooth movement to target lane var diff = self.targetX - self.x; if (Math.abs(diff) > 5) { self.x += diff * 0.15; } else { self.x = self.targetX; } }; return self; }); var QuestionPanel = Container.expand(function () { var self = Container.call(this); var background = self.attachAsset('questionBg', { anchorX: 0.5, anchorY: 0.5 }); self.questionText = new Text2('', { size: 110, // Increased by 20% from 92 fill: 0x000000, font: "'Arial Black'" }); self.questionText.anchor.set(0.5, 0.5); self.questionText.y = -150; self.addChild(self.questionText); self.timerText = new Text2('9', { size: 90, fill: 0xFF0000, font: "'Arial Black'" }); self.timerText.anchor.set(0.5, 0.5); self.timerText.y = -250; self.addChild(self.timerText); self.answerButtons = []; for (var i = 0; i < 3; i++) { var button = self.addChild(LK.getAsset('answerButton', { anchorX: 0.5, anchorY: 0.5 })); button.x = (i - 1) * 450; button.y = 50; var buttonText = new Text2('', { size: 86, // Increased by 20% from 72 fill: 0x000000, font: "'Arial Black'" }); buttonText.anchor.set(0.5, 0.5); button.addChild(buttonText); button.text = buttonText; button.answerValue = 0; self.answerButtons.push(button); } self.setupQuestion = function () { var num1 = Math.floor(Math.random() * 9) + 2; var num2 = Math.floor(Math.random() * 9) + 2; var correctAnswer = num1 * num2; self.questionText.setText(num1 + ' × ' + num2 + ' = ?'); self.correctAnswer = correctAnswer; // Generate wrong answers var wrongAnswers = []; while (wrongAnswers.length < 2) { var wrong = correctAnswer + Math.floor(Math.random() * 20) - 10; if (wrong !== correctAnswer && wrong > 0 && wrongAnswers.indexOf(wrong) === -1) { wrongAnswers.push(wrong); } } // Randomize button positions var answers = [correctAnswer, wrongAnswers[0], wrongAnswers[1]]; for (var i = 0; i < 3; i++) { var randomIndex = Math.floor(Math.random() * answers.length); var answer = answers.splice(randomIndex, 1)[0]; self.answerButtons[i].answerValue = answer; self.answerButtons[i].text.setText(answer.toString()); } questionTimer = 9; self.timerText.setText(questionTimer.toString()); }; return self; }); var SpecialCoin = Container.expand(function () { var self = Container.call(this); var coinGraphics = self.attachAsset('specialCoin', { anchorX: 0.5, anchorY: 0.5 }); self.speed = gameSpeed; self.pointValue = 500; self.update = function () { self.y += self.speed; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB }); /**** * Game Code ****/ // fence // desk // backpack // Game variables var gameStarted = false; var startScreen = null; var gameSpeed = 8; var initialGameSpeed = 8; var speedIncrement = 0.005; var maxSpeed = 20; var lanePositions = [2048 / 2 - 300, 2048 / 2, 2048 / 2 + 300]; var groundY = 2732 - 100; var obstacles = []; var coins = []; var specialCoins = []; var obstacleSpawnTimer = 0; var coinSpawnTimer = 0; var specialCoinSpawnTimer = 0; var scoreMultiplier = 1.0; var questionActive = false; var questionTimer = 0; var questionPanel = null; var firstCoinCollected = false; var lastBonus5xTriggered = false; var forcedQuestionTimer = 0; var forcedQuestionInterval = 1800; // 30 seconds at 60fps var obstaclesPassed = 0; var maxObstaclesBeforeForced = Math.floor(Math.random() * 11) + 10; // Random between 10-20 var player = game.addChild(new Player()); player.x = lanePositions[1]; player.y = groundY; player.targetX = lanePositions[1]; // Ground var ground = game.addChild(LK.getAsset('ground', { anchorX: 0, anchorY: 0 })); ground.x = 0; ground.y = groundY; // UI Elements var scoreText = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF, font: "'Arial Black'" }); scoreText.anchor.set(1, 0); scoreText.x = -20; scoreText.y = 20; scoreText.visible = false; // Hide initially LK.gui.topRight.addChild(scoreText); var multiplierText = new Text2('BONUS: 1.0x', { size: 50, fill: 0xFFFF00, font: "'Arial Black'" }); multiplierText.anchor.set(1, 0); multiplierText.x = -20; multiplierText.y = 80; multiplierText.visible = false; // Hide initially LK.gui.topRight.addChild(multiplierText); // Create start screen startScreen = game.addChild(LK.getAsset('startImage', { anchorX: 0.5, anchorY: 0.5, scaleX: 2048 / 1400, // Scale to full width scaleY: 2732 / 2100 // Scale to full height })); startScreen.x = 2048 / 2; startScreen.y = 2732 / 2; // Hide game elements until game starts player.visible = false; ground.visible = false; function updateUI() { scoreText.setText('Score: ' + LK.getScore()); multiplierText.setText('BONUS: ' + scoreMultiplier.toFixed(1) + 'x'); // Add flaming animation when bonus is 5x or higher if (scoreMultiplier >= 5.0) { // Play special sound when reaching 5x bonus for the first time if (!lastBonus5xTriggered) { LK.getSound('bonus5x').play(); lastBonus5xTriggered = true; } // Stop any existing tweens tween.stop(multiplierText); // Create more intense flame-like movement var flameIntensity = Math.min(scoreMultiplier / 5, 3); // Scale with multiplier, max 3x tween(multiplierText, { x: multiplierText.x + (Math.random() * 12 - 6) * flameIntensity, y: multiplierText.y + (Math.random() * 8 - 4) * flameIntensity, scaleX: 1 + (Math.random() * 0.3 - 0.15) * flameIntensity, scaleY: 1 + (Math.random() * 0.3 - 0.15) * flameIntensity }, { duration: 80, easing: tween.easeInOut, onFinish: function onFinish() { tween(multiplierText, { x: -20, y: 80, scaleX: 1, scaleY: 1 }, { duration: 80, easing: tween.easeOut }); } }); // More dramatic color transitions with multiple flame colors var flameColors = [0xFF0000, 0xFF4500, 0xFF6600, 0xFF8800, 0xFFAA00, 0xFFCC00, 0xFFFF00]; var randomColor1 = flameColors[Math.floor(Math.random() * flameColors.length)]; var randomColor2 = flameColors[Math.floor(Math.random() * flameColors.length)]; tween(multiplierText, { tint: randomColor1 }, { duration: 120, easing: tween.easeInOut, onFinish: function onFinish() { tween(multiplierText, { tint: randomColor2 }, { duration: 120, easing: tween.easeInOut, onFinish: function onFinish() { tween(multiplierText, { tint: 0xFFFF00 }, { duration: 120, easing: tween.easeOut }); } }); } }); // Add subtle rotation for more flame effect tween(multiplierText, { rotation: (Math.random() * 0.2 - 0.1) * flameIntensity }, { duration: 150, easing: tween.easeInOut, onFinish: function onFinish() { tween(multiplierText, { rotation: 0 }, { duration: 150, easing: tween.easeOut }); } }); } else { // Reset to normal appearance tween.stop(multiplierText); multiplierText.tint = 0xFFFF00; multiplierText.x = -20; multiplierText.y = 80; multiplierText.scaleX = 1; multiplierText.scaleY = 1; multiplierText.rotation = 0; // Reset 5x bonus sound trigger when below 5x lastBonus5xTriggered = false; } } function switchLane(direction) { if (questionActive) return; player.lane += direction; if (player.lane < 0) player.lane = 0; if (player.lane > 2) player.lane = 2; player.targetX = lanePositions[player.lane]; } function spawnObstacle() { var lane = Math.floor(Math.random() * 3); var type = Math.floor(Math.random() * 3) + 1; var spawnY = -100; var minDistance = 560; // 2 character widths (280 * 2) var canSpawn = true; // Check distance from other obstacles for (var i = 0; i < obstacles.length; i++) { var existingObstacle = obstacles[i]; if (existingObstacle.lane === lane && Math.abs(existingObstacle.y - spawnY) < minDistance) { canSpawn = false; break; } } // Check distance from coins if (canSpawn) { for (var i = 0; i < coins.length; i++) { var coin = coins[i]; if (coin.lane === lane && Math.abs(coin.y - spawnY) < minDistance) { canSpawn = false; break; } } } // Check distance from special coins if (canSpawn) { for (var i = 0; i < specialCoins.length; i++) { var specialCoin = specialCoins[i]; if (specialCoin.lane === lane && Math.abs(specialCoin.y - spawnY) < minDistance) { canSpawn = false; break; } } } if (canSpawn) { var obstacle = game.addChild(new Obstacle(type)); obstacle.x = lanePositions[lane]; obstacle.y = spawnY; obstacle.lane = lane; obstacles.push(obstacle); } } function spawnCoin() { var lane = Math.floor(Math.random() * 3); var spawnY = -50; var minDistance = 560; // 2 character widths (280 * 2) var canSpawn = true; // Check distance from obstacles for (var i = 0; i < obstacles.length; i++) { var obstacle = obstacles[i]; if (obstacle.lane === lane && Math.abs(obstacle.y - spawnY) < minDistance) { canSpawn = false; break; } } // Check distance from other coins if (canSpawn) { for (var i = 0; i < coins.length; i++) { var existingCoin = coins[i]; if (existingCoin.lane === lane && Math.abs(existingCoin.y - spawnY) < minDistance) { canSpawn = false; break; } } } // Check distance from special coins if (canSpawn) { for (var i = 0; i < specialCoins.length; i++) { var specialCoin = specialCoins[i]; if (specialCoin.lane === lane && Math.abs(specialCoin.y - spawnY) < minDistance) { canSpawn = false; break; } } } if (canSpawn) { var coin = game.addChild(new Coin()); coin.x = lanePositions[lane]; coin.y = spawnY; coin.lane = lane; coins.push(coin); } } function spawnSpecialCoin() { var lane = Math.floor(Math.random() * 3); var spawnY = -50; var minDistance = 560; // 2 character widths (280 * 2) var canSpawn = true; // Check distance from obstacles for (var i = 0; i < obstacles.length; i++) { var obstacle = obstacles[i]; if (obstacle.lane === lane && Math.abs(obstacle.y - spawnY) < minDistance) { canSpawn = false; break; } } // Check distance from coins if (canSpawn) { for (var i = 0; i < coins.length; i++) { var coin = coins[i]; if (coin.lane === lane && Math.abs(coin.y - spawnY) < minDistance) { canSpawn = false; break; } } } // Check distance from other special coins if (canSpawn) { for (var i = 0; i < specialCoins.length; i++) { var existingSpecialCoin = specialCoins[i]; if (existingSpecialCoin.lane === lane && Math.abs(existingSpecialCoin.y - spawnY) < minDistance) { canSpawn = false; break; } } } if (canSpawn) { var specialCoin = game.addChild(new SpecialCoin()); specialCoin.x = lanePositions[lane]; specialCoin.y = spawnY; specialCoin.lane = lane; specialCoins.push(specialCoin); } } function spawnForcedQuestionObstacles() { // Clear existing obstacles to prevent conflicts for (var i = obstacles.length - 1; i >= 0; i--) { obstacles[i].destroy(); obstacles.splice(i, 1); } // Clear existing coins and special coins for (var i = coins.length - 1; i >= 0; i--) { coins[i].destroy(); coins.splice(i, 1); } for (var i = specialCoins.length - 1; i >= 0; i--) { specialCoins[i].destroy(); specialCoins.splice(i, 1); } // Spawn obstacles in all 3 lanes at the same Y position var spawnY = -100; for (var lane = 0; lane < 3; lane++) { var type = Math.floor(Math.random() * 3) + 1; var obstacle = game.addChild(new Obstacle(type)); obstacle.x = lanePositions[lane]; obstacle.y = spawnY; obstacle.lane = lane; obstacles.push(obstacle); } } function showQuestion() { questionActive = true; // Clear all obstacles for (var i = obstacles.length - 1; i >= 0; i--) { obstacles[i].destroy(); obstacles.splice(i, 1); } // Clear all coins for (var i = coins.length - 1; i >= 0; i--) { coins[i].destroy(); coins.splice(i, 1); } // Clear all special coins for (var i = specialCoins.length - 1; i >= 0; i--) { specialCoins[i].destroy(); specialCoins.splice(i, 1); } questionPanel = game.addChild(new QuestionPanel()); questionPanel.x = 2048 / 2; questionPanel.y = 2732 / 2; questionPanel.setupQuestion(); } function hideQuestion() { if (questionPanel) { questionPanel.destroy(); questionPanel = null; } questionActive = false; } function answerQuestion(selectedAnswer) { if (!questionActive) return; var correct = selectedAnswer === questionPanel.correctAnswer; if (correct) { scoreMultiplier += 0.5; // Cap bonus multiplier at 5x if (scoreMultiplier > 5.0) { scoreMultiplier = 5.0; } gameSpeed *= 1.01; // Increase speed by 1% for correct answers LK.getSound('correct').play(); } else { scoreMultiplier = 1.0; var speedLimit = initialGameSpeed * 1.6; // 60% more than initial speed if (gameSpeed < speedLimit) { gameSpeed *= 1.03; // Increase speed by 3% } else { // At speed limit, give 6x score bonus instead var bonusPoints = Math.round(60 * scoreMultiplier); // 6x the base 10 points LK.setScore(LK.getScore() + bonusPoints); } LK.getSound('wrong').play(); } hideQuestion(); updateUI(); } // Touch controls game.down = function (x, y, obj) { if (!gameStarted) { // Start the game when clicking on start screen gameStarted = true; if (startScreen) { startScreen.destroy(); startScreen = null; } player.visible = true; ground.visible = true; scoreText.visible = true; // Show score when game starts multiplierText.visible = true; // Show bonus when game starts return; } if (questionActive && questionPanel) { // Check if clicking on answer buttons for (var i = 0; i < questionPanel.answerButtons.length; i++) { var button = questionPanel.answerButtons[i]; var buttonPos = game.toLocal(button.parent.toGlobal(button.position)); if (Math.abs(x - buttonPos.x) < 200 && Math.abs(y - buttonPos.y) < 50) { answerQuestion(button.answerValue); return; } } } else { // Lane switching if (x < 2048 / 2) { switchLane(-1); // Move left } else { switchLane(1); // Move right } } }; game.update = function () { if (!gameStarted) { return; // Don't update game until started } if (questionActive) { // Handle question timer if (LK.ticks % 60 === 0) { // Every second questionTimer--; if (questionPanel) { questionPanel.timerText.setText(questionTimer.toString()); } if (questionTimer <= 0) { // Time's up - wrong answer scoreMultiplier = 1.0; hideQuestion(); updateUI(); } } return; // Don't update game objects during question } // Increase speed gradually if (gameSpeed < maxSpeed) { gameSpeed += speedIncrement; } // Spawn obstacles obstacleSpawnTimer++; if (obstacleSpawnTimer > 120 - gameSpeed * 2) { // Spawn more frequently as speed increases spawnObstacle(); obstacleSpawnTimer = 0; } // Spawn coins coinSpawnTimer++; if (coinSpawnTimer > 180) { spawnCoin(); coinSpawnTimer = 0; } // Spawn special coins (less frequently) specialCoinSpawnTimer++; if (specialCoinSpawnTimer > 600) { spawnSpecialCoin(); specialCoinSpawnTimer = 0; } // Handle forced question timer forcedQuestionTimer++; if (forcedQuestionTimer > forcedQuestionInterval) { spawnForcedQuestionObstacles(); forcedQuestionTimer = 0; obstaclesPassed = 0; // Reset obstacle counter when across-lane spawn occurs maxObstaclesBeforeForced = Math.floor(Math.random() * 11) + 10; // New random threshold between 10-20 // Increase interval slightly each time to make it less predictable forcedQuestionInterval = Math.floor(forcedQuestionInterval * 1.1); if (forcedQuestionInterval > 3600) { // Cap at 60 seconds forcedQuestionInterval = 3600; } } // Update obstacles for (var i = obstacles.length - 1; i >= 0; i--) { var obstacle = obstacles[i]; if (!obstacle) continue; // Skip if obstacle is undefined obstacle.speed = gameSpeed; // Check collision with player if (obstacle.lane === player.lane && Math.abs(obstacle.y - player.y) < 80 && Math.abs(obstacle.x - player.x) < 60) { LK.getSound('hit').play(); showQuestion(); obstacle.destroy(); obstacles.splice(i, 1); continue; } // Remove off-screen obstacles if (obstacle.y > 2732 + 100) { obstaclesPassed++; obstacle.destroy(); obstacles.splice(i, 1); // Force across-lane obstacles after random number of obstacles passed if (obstaclesPassed >= maxObstaclesBeforeForced) { spawnForcedQuestionObstacles(); obstaclesPassed = 0; // Reset counter maxObstaclesBeforeForced = Math.floor(Math.random() * 11) + 10; // New random threshold between 10-20 } } } // Update coins for (var i = coins.length - 1; i >= 0; i--) { var coin = coins[i]; if (!coin) continue; // Skip if coin is undefined coin.speed = gameSpeed; // Check collection if (coin.lane === player.lane && Math.abs(coin.y - player.y) < 60 && Math.abs(coin.x - player.x) < 50) { var coinPoints = Math.round(10 * scoreMultiplier); LK.setScore(LK.getScore() + coinPoints); LK.getSound('collect').play(); // Check if score exceeded 5000 if (LK.getScore() > 5000) { // Stop background music LK.stopMusic(); // Hide UI elements scoreText.visible = false; multiplierText.visible = false; // Show victory visual var completionImage = game.addChild(LK.getAsset('completionImage', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, scaleX: 0.1, scaleY: 0.1 })); // Play completion sound LK.getSound('completionSound').play(); // Add victory animation tween(completionImage, { scaleX: 1.5, scaleY: 1.5, rotation: Math.PI * 2 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { // Wait for completion sound to finish before showing play again button LK.setTimeout(function () { LK.showYouWin(); }, 1000); // 1 second delay to allow sound to complete } }); } // Start background music on first coin collection if (!firstCoinCollected) { firstCoinCollected = true; LK.playMusic('onepiecesoundtrack', { loop: true }); } updateUI(); // Add coin disappearing animation tween(coin, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { coin.destroy(); } }); coins.splice(i, 1); continue; } // Remove off-screen coins if (coin.y > 2732 + 50) { coin.destroy(); coins.splice(i, 1); } } // Update special coins for (var i = specialCoins.length - 1; i >= 0; i--) { var specialCoin = specialCoins[i]; if (!specialCoin) continue; // Skip if specialCoin is undefined specialCoin.speed = gameSpeed; // Check collection if (specialCoin.lane === player.lane && Math.abs(specialCoin.y - player.y) < 60 && Math.abs(specialCoin.x - player.x) < 50) { var specialCoinPoints = Math.round(specialCoin.pointValue * scoreMultiplier); LK.setScore(LK.getScore() + specialCoinPoints); // Check if score exceeded 5000 if (LK.getScore() > 5000) { // Stop background music LK.stopMusic(); // Hide UI elements scoreText.visible = false; multiplierText.visible = false; // Show victory visual var completionImage = game.addChild(LK.getAsset('completionImage', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, scaleX: 0.1, scaleY: 0.1 })); // Play completion sound LK.getSound('completionSound').play(); // Add victory animation tween(completionImage, { scaleX: 1.5, scaleY: 1.5, rotation: Math.PI * 2 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { // Wait for completion sound to finish before showing play again button LK.setTimeout(function () { LK.showYouWin(); }, 1000); // 1 second delay to allow sound to complete } }); } var specialSound = LK.getSound('specialCollect'); specialSound.play(); // Create a temporary object to tween the music volume var musicVolume = { volume: 0.3 }; // Mute background music volume when special collect sound plays tween(musicVolume, { volume: 0 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { // Update the actual music volume LK.playMusic('onepiecesoundtrack', { volume: musicVolume.volume }); } }); // Fade back to normal volume when special collect sound finishes LK.setTimeout(function () { tween(musicVolume, { volume: 0.3 }, { duration: 100, easing: tween.easeIn, onFinish: function onFinish() { // Update the actual music volume back to normal LK.playMusic('onepiecesoundtrack', { volume: musicVolume.volume }); } }); }, 630); // Duration of specialCollect sound (0.898 - 0.835) * 1000 = 63ms, but adding buffer // Start background music on first coin collection if (!firstCoinCollected) { firstCoinCollected = true; LK.playMusic('onepiecesoundtrack', { loop: true }); } updateUI(); // Add special coin disappearing animation tween(specialCoin, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { specialCoin.destroy(); } }); specialCoins.splice(i, 1); continue; } // Remove off-screen special coins if (specialCoin.y > 2732 + 50) { specialCoin.destroy(); specialCoins.splice(i, 1); } } }; updateUI();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = gameSpeed;
self.update = function () {
self.y += self.speed;
};
return self;
});
var Obstacle = Container.expand(function (type) {
var self = Container.call(this);
var assetName = 'obstacle' + (type || 1);
var obstacleGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 1.0
});
self.speed = gameSpeed;
self.update = function () {
self.y += self.speed;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 1.0
});
self.lane = 1; // 0=left, 1=center, 2=right
self.targetX = 0;
self.update = function () {
// Smooth movement to target lane
var diff = self.targetX - self.x;
if (Math.abs(diff) > 5) {
self.x += diff * 0.15;
} else {
self.x = self.targetX;
}
};
return self;
});
var QuestionPanel = Container.expand(function () {
var self = Container.call(this);
var background = self.attachAsset('questionBg', {
anchorX: 0.5,
anchorY: 0.5
});
self.questionText = new Text2('', {
size: 110,
// Increased by 20% from 92
fill: 0x000000,
font: "'Arial Black'"
});
self.questionText.anchor.set(0.5, 0.5);
self.questionText.y = -150;
self.addChild(self.questionText);
self.timerText = new Text2('9', {
size: 90,
fill: 0xFF0000,
font: "'Arial Black'"
});
self.timerText.anchor.set(0.5, 0.5);
self.timerText.y = -250;
self.addChild(self.timerText);
self.answerButtons = [];
for (var i = 0; i < 3; i++) {
var button = self.addChild(LK.getAsset('answerButton', {
anchorX: 0.5,
anchorY: 0.5
}));
button.x = (i - 1) * 450;
button.y = 50;
var buttonText = new Text2('', {
size: 86,
// Increased by 20% from 72
fill: 0x000000,
font: "'Arial Black'"
});
buttonText.anchor.set(0.5, 0.5);
button.addChild(buttonText);
button.text = buttonText;
button.answerValue = 0;
self.answerButtons.push(button);
}
self.setupQuestion = function () {
var num1 = Math.floor(Math.random() * 9) + 2;
var num2 = Math.floor(Math.random() * 9) + 2;
var correctAnswer = num1 * num2;
self.questionText.setText(num1 + ' × ' + num2 + ' = ?');
self.correctAnswer = correctAnswer;
// Generate wrong answers
var wrongAnswers = [];
while (wrongAnswers.length < 2) {
var wrong = correctAnswer + Math.floor(Math.random() * 20) - 10;
if (wrong !== correctAnswer && wrong > 0 && wrongAnswers.indexOf(wrong) === -1) {
wrongAnswers.push(wrong);
}
}
// Randomize button positions
var answers = [correctAnswer, wrongAnswers[0], wrongAnswers[1]];
for (var i = 0; i < 3; i++) {
var randomIndex = Math.floor(Math.random() * answers.length);
var answer = answers.splice(randomIndex, 1)[0];
self.answerButtons[i].answerValue = answer;
self.answerButtons[i].text.setText(answer.toString());
}
questionTimer = 9;
self.timerText.setText(questionTimer.toString());
};
return self;
});
var SpecialCoin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('specialCoin', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = gameSpeed;
self.pointValue = 500;
self.update = function () {
self.y += self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// fence
// desk
// backpack
// Game variables
var gameStarted = false;
var startScreen = null;
var gameSpeed = 8;
var initialGameSpeed = 8;
var speedIncrement = 0.005;
var maxSpeed = 20;
var lanePositions = [2048 / 2 - 300, 2048 / 2, 2048 / 2 + 300];
var groundY = 2732 - 100;
var obstacles = [];
var coins = [];
var specialCoins = [];
var obstacleSpawnTimer = 0;
var coinSpawnTimer = 0;
var specialCoinSpawnTimer = 0;
var scoreMultiplier = 1.0;
var questionActive = false;
var questionTimer = 0;
var questionPanel = null;
var firstCoinCollected = false;
var lastBonus5xTriggered = false;
var forcedQuestionTimer = 0;
var forcedQuestionInterval = 1800; // 30 seconds at 60fps
var obstaclesPassed = 0;
var maxObstaclesBeforeForced = Math.floor(Math.random() * 11) + 10; // Random between 10-20
var player = game.addChild(new Player());
player.x = lanePositions[1];
player.y = groundY;
player.targetX = lanePositions[1];
// Ground
var ground = game.addChild(LK.getAsset('ground', {
anchorX: 0,
anchorY: 0
}));
ground.x = 0;
ground.y = groundY;
// UI Elements
var scoreText = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF,
font: "'Arial Black'"
});
scoreText.anchor.set(1, 0);
scoreText.x = -20;
scoreText.y = 20;
scoreText.visible = false; // Hide initially
LK.gui.topRight.addChild(scoreText);
var multiplierText = new Text2('BONUS: 1.0x', {
size: 50,
fill: 0xFFFF00,
font: "'Arial Black'"
});
multiplierText.anchor.set(1, 0);
multiplierText.x = -20;
multiplierText.y = 80;
multiplierText.visible = false; // Hide initially
LK.gui.topRight.addChild(multiplierText);
// Create start screen
startScreen = game.addChild(LK.getAsset('startImage', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2048 / 1400,
// Scale to full width
scaleY: 2732 / 2100 // Scale to full height
}));
startScreen.x = 2048 / 2;
startScreen.y = 2732 / 2;
// Hide game elements until game starts
player.visible = false;
ground.visible = false;
function updateUI() {
scoreText.setText('Score: ' + LK.getScore());
multiplierText.setText('BONUS: ' + scoreMultiplier.toFixed(1) + 'x');
// Add flaming animation when bonus is 5x or higher
if (scoreMultiplier >= 5.0) {
// Play special sound when reaching 5x bonus for the first time
if (!lastBonus5xTriggered) {
LK.getSound('bonus5x').play();
lastBonus5xTriggered = true;
}
// Stop any existing tweens
tween.stop(multiplierText);
// Create more intense flame-like movement
var flameIntensity = Math.min(scoreMultiplier / 5, 3); // Scale with multiplier, max 3x
tween(multiplierText, {
x: multiplierText.x + (Math.random() * 12 - 6) * flameIntensity,
y: multiplierText.y + (Math.random() * 8 - 4) * flameIntensity,
scaleX: 1 + (Math.random() * 0.3 - 0.15) * flameIntensity,
scaleY: 1 + (Math.random() * 0.3 - 0.15) * flameIntensity
}, {
duration: 80,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(multiplierText, {
x: -20,
y: 80,
scaleX: 1,
scaleY: 1
}, {
duration: 80,
easing: tween.easeOut
});
}
});
// More dramatic color transitions with multiple flame colors
var flameColors = [0xFF0000, 0xFF4500, 0xFF6600, 0xFF8800, 0xFFAA00, 0xFFCC00, 0xFFFF00];
var randomColor1 = flameColors[Math.floor(Math.random() * flameColors.length)];
var randomColor2 = flameColors[Math.floor(Math.random() * flameColors.length)];
tween(multiplierText, {
tint: randomColor1
}, {
duration: 120,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(multiplierText, {
tint: randomColor2
}, {
duration: 120,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(multiplierText, {
tint: 0xFFFF00
}, {
duration: 120,
easing: tween.easeOut
});
}
});
}
});
// Add subtle rotation for more flame effect
tween(multiplierText, {
rotation: (Math.random() * 0.2 - 0.1) * flameIntensity
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(multiplierText, {
rotation: 0
}, {
duration: 150,
easing: tween.easeOut
});
}
});
} else {
// Reset to normal appearance
tween.stop(multiplierText);
multiplierText.tint = 0xFFFF00;
multiplierText.x = -20;
multiplierText.y = 80;
multiplierText.scaleX = 1;
multiplierText.scaleY = 1;
multiplierText.rotation = 0;
// Reset 5x bonus sound trigger when below 5x
lastBonus5xTriggered = false;
}
}
function switchLane(direction) {
if (questionActive) return;
player.lane += direction;
if (player.lane < 0) player.lane = 0;
if (player.lane > 2) player.lane = 2;
player.targetX = lanePositions[player.lane];
}
function spawnObstacle() {
var lane = Math.floor(Math.random() * 3);
var type = Math.floor(Math.random() * 3) + 1;
var spawnY = -100;
var minDistance = 560; // 2 character widths (280 * 2)
var canSpawn = true;
// Check distance from other obstacles
for (var i = 0; i < obstacles.length; i++) {
var existingObstacle = obstacles[i];
if (existingObstacle.lane === lane && Math.abs(existingObstacle.y - spawnY) < minDistance) {
canSpawn = false;
break;
}
}
// Check distance from coins
if (canSpawn) {
for (var i = 0; i < coins.length; i++) {
var coin = coins[i];
if (coin.lane === lane && Math.abs(coin.y - spawnY) < minDistance) {
canSpawn = false;
break;
}
}
}
// Check distance from special coins
if (canSpawn) {
for (var i = 0; i < specialCoins.length; i++) {
var specialCoin = specialCoins[i];
if (specialCoin.lane === lane && Math.abs(specialCoin.y - spawnY) < minDistance) {
canSpawn = false;
break;
}
}
}
if (canSpawn) {
var obstacle = game.addChild(new Obstacle(type));
obstacle.x = lanePositions[lane];
obstacle.y = spawnY;
obstacle.lane = lane;
obstacles.push(obstacle);
}
}
function spawnCoin() {
var lane = Math.floor(Math.random() * 3);
var spawnY = -50;
var minDistance = 560; // 2 character widths (280 * 2)
var canSpawn = true;
// Check distance from obstacles
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (obstacle.lane === lane && Math.abs(obstacle.y - spawnY) < minDistance) {
canSpawn = false;
break;
}
}
// Check distance from other coins
if (canSpawn) {
for (var i = 0; i < coins.length; i++) {
var existingCoin = coins[i];
if (existingCoin.lane === lane && Math.abs(existingCoin.y - spawnY) < minDistance) {
canSpawn = false;
break;
}
}
}
// Check distance from special coins
if (canSpawn) {
for (var i = 0; i < specialCoins.length; i++) {
var specialCoin = specialCoins[i];
if (specialCoin.lane === lane && Math.abs(specialCoin.y - spawnY) < minDistance) {
canSpawn = false;
break;
}
}
}
if (canSpawn) {
var coin = game.addChild(new Coin());
coin.x = lanePositions[lane];
coin.y = spawnY;
coin.lane = lane;
coins.push(coin);
}
}
function spawnSpecialCoin() {
var lane = Math.floor(Math.random() * 3);
var spawnY = -50;
var minDistance = 560; // 2 character widths (280 * 2)
var canSpawn = true;
// Check distance from obstacles
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (obstacle.lane === lane && Math.abs(obstacle.y - spawnY) < minDistance) {
canSpawn = false;
break;
}
}
// Check distance from coins
if (canSpawn) {
for (var i = 0; i < coins.length; i++) {
var coin = coins[i];
if (coin.lane === lane && Math.abs(coin.y - spawnY) < minDistance) {
canSpawn = false;
break;
}
}
}
// Check distance from other special coins
if (canSpawn) {
for (var i = 0; i < specialCoins.length; i++) {
var existingSpecialCoin = specialCoins[i];
if (existingSpecialCoin.lane === lane && Math.abs(existingSpecialCoin.y - spawnY) < minDistance) {
canSpawn = false;
break;
}
}
}
if (canSpawn) {
var specialCoin = game.addChild(new SpecialCoin());
specialCoin.x = lanePositions[lane];
specialCoin.y = spawnY;
specialCoin.lane = lane;
specialCoins.push(specialCoin);
}
}
function spawnForcedQuestionObstacles() {
// Clear existing obstacles to prevent conflicts
for (var i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
// Clear existing coins and special coins
for (var i = coins.length - 1; i >= 0; i--) {
coins[i].destroy();
coins.splice(i, 1);
}
for (var i = specialCoins.length - 1; i >= 0; i--) {
specialCoins[i].destroy();
specialCoins.splice(i, 1);
}
// Spawn obstacles in all 3 lanes at the same Y position
var spawnY = -100;
for (var lane = 0; lane < 3; lane++) {
var type = Math.floor(Math.random() * 3) + 1;
var obstacle = game.addChild(new Obstacle(type));
obstacle.x = lanePositions[lane];
obstacle.y = spawnY;
obstacle.lane = lane;
obstacles.push(obstacle);
}
}
function showQuestion() {
questionActive = true;
// Clear all obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
// Clear all coins
for (var i = coins.length - 1; i >= 0; i--) {
coins[i].destroy();
coins.splice(i, 1);
}
// Clear all special coins
for (var i = specialCoins.length - 1; i >= 0; i--) {
specialCoins[i].destroy();
specialCoins.splice(i, 1);
}
questionPanel = game.addChild(new QuestionPanel());
questionPanel.x = 2048 / 2;
questionPanel.y = 2732 / 2;
questionPanel.setupQuestion();
}
function hideQuestion() {
if (questionPanel) {
questionPanel.destroy();
questionPanel = null;
}
questionActive = false;
}
function answerQuestion(selectedAnswer) {
if (!questionActive) return;
var correct = selectedAnswer === questionPanel.correctAnswer;
if (correct) {
scoreMultiplier += 0.5;
// Cap bonus multiplier at 5x
if (scoreMultiplier > 5.0) {
scoreMultiplier = 5.0;
}
gameSpeed *= 1.01; // Increase speed by 1% for correct answers
LK.getSound('correct').play();
} else {
scoreMultiplier = 1.0;
var speedLimit = initialGameSpeed * 1.6; // 60% more than initial speed
if (gameSpeed < speedLimit) {
gameSpeed *= 1.03; // Increase speed by 3%
} else {
// At speed limit, give 6x score bonus instead
var bonusPoints = Math.round(60 * scoreMultiplier); // 6x the base 10 points
LK.setScore(LK.getScore() + bonusPoints);
}
LK.getSound('wrong').play();
}
hideQuestion();
updateUI();
}
// Touch controls
game.down = function (x, y, obj) {
if (!gameStarted) {
// Start the game when clicking on start screen
gameStarted = true;
if (startScreen) {
startScreen.destroy();
startScreen = null;
}
player.visible = true;
ground.visible = true;
scoreText.visible = true; // Show score when game starts
multiplierText.visible = true; // Show bonus when game starts
return;
}
if (questionActive && questionPanel) {
// Check if clicking on answer buttons
for (var i = 0; i < questionPanel.answerButtons.length; i++) {
var button = questionPanel.answerButtons[i];
var buttonPos = game.toLocal(button.parent.toGlobal(button.position));
if (Math.abs(x - buttonPos.x) < 200 && Math.abs(y - buttonPos.y) < 50) {
answerQuestion(button.answerValue);
return;
}
}
} else {
// Lane switching
if (x < 2048 / 2) {
switchLane(-1); // Move left
} else {
switchLane(1); // Move right
}
}
};
game.update = function () {
if (!gameStarted) {
return; // Don't update game until started
}
if (questionActive) {
// Handle question timer
if (LK.ticks % 60 === 0) {
// Every second
questionTimer--;
if (questionPanel) {
questionPanel.timerText.setText(questionTimer.toString());
}
if (questionTimer <= 0) {
// Time's up - wrong answer
scoreMultiplier = 1.0;
hideQuestion();
updateUI();
}
}
return; // Don't update game objects during question
}
// Increase speed gradually
if (gameSpeed < maxSpeed) {
gameSpeed += speedIncrement;
}
// Spawn obstacles
obstacleSpawnTimer++;
if (obstacleSpawnTimer > 120 - gameSpeed * 2) {
// Spawn more frequently as speed increases
spawnObstacle();
obstacleSpawnTimer = 0;
}
// Spawn coins
coinSpawnTimer++;
if (coinSpawnTimer > 180) {
spawnCoin();
coinSpawnTimer = 0;
}
// Spawn special coins (less frequently)
specialCoinSpawnTimer++;
if (specialCoinSpawnTimer > 600) {
spawnSpecialCoin();
specialCoinSpawnTimer = 0;
}
// Handle forced question timer
forcedQuestionTimer++;
if (forcedQuestionTimer > forcedQuestionInterval) {
spawnForcedQuestionObstacles();
forcedQuestionTimer = 0;
obstaclesPassed = 0; // Reset obstacle counter when across-lane spawn occurs
maxObstaclesBeforeForced = Math.floor(Math.random() * 11) + 10; // New random threshold between 10-20
// Increase interval slightly each time to make it less predictable
forcedQuestionInterval = Math.floor(forcedQuestionInterval * 1.1);
if (forcedQuestionInterval > 3600) {
// Cap at 60 seconds
forcedQuestionInterval = 3600;
}
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
if (!obstacle) continue; // Skip if obstacle is undefined
obstacle.speed = gameSpeed;
// Check collision with player
if (obstacle.lane === player.lane && Math.abs(obstacle.y - player.y) < 80 && Math.abs(obstacle.x - player.x) < 60) {
LK.getSound('hit').play();
showQuestion();
obstacle.destroy();
obstacles.splice(i, 1);
continue;
}
// Remove off-screen obstacles
if (obstacle.y > 2732 + 100) {
obstaclesPassed++;
obstacle.destroy();
obstacles.splice(i, 1);
// Force across-lane obstacles after random number of obstacles passed
if (obstaclesPassed >= maxObstaclesBeforeForced) {
spawnForcedQuestionObstacles();
obstaclesPassed = 0; // Reset counter
maxObstaclesBeforeForced = Math.floor(Math.random() * 11) + 10; // New random threshold between 10-20
}
}
}
// Update coins
for (var i = coins.length - 1; i >= 0; i--) {
var coin = coins[i];
if (!coin) continue; // Skip if coin is undefined
coin.speed = gameSpeed;
// Check collection
if (coin.lane === player.lane && Math.abs(coin.y - player.y) < 60 && Math.abs(coin.x - player.x) < 50) {
var coinPoints = Math.round(10 * scoreMultiplier);
LK.setScore(LK.getScore() + coinPoints);
LK.getSound('collect').play();
// Check if score exceeded 5000
if (LK.getScore() > 5000) {
// Stop background music
LK.stopMusic();
// Hide UI elements
scoreText.visible = false;
multiplierText.visible = false;
// Show victory visual
var completionImage = game.addChild(LK.getAsset('completionImage', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 0.1,
scaleY: 0.1
}));
// Play completion sound
LK.getSound('completionSound').play();
// Add victory animation
tween(completionImage, {
scaleX: 1.5,
scaleY: 1.5,
rotation: Math.PI * 2
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Wait for completion sound to finish before showing play again button
LK.setTimeout(function () {
LK.showYouWin();
}, 1000); // 1 second delay to allow sound to complete
}
});
}
// Start background music on first coin collection
if (!firstCoinCollected) {
firstCoinCollected = true;
LK.playMusic('onepiecesoundtrack', {
loop: true
});
}
updateUI();
// Add coin disappearing animation
tween(coin, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
coin.destroy();
}
});
coins.splice(i, 1);
continue;
}
// Remove off-screen coins
if (coin.y > 2732 + 50) {
coin.destroy();
coins.splice(i, 1);
}
}
// Update special coins
for (var i = specialCoins.length - 1; i >= 0; i--) {
var specialCoin = specialCoins[i];
if (!specialCoin) continue; // Skip if specialCoin is undefined
specialCoin.speed = gameSpeed;
// Check collection
if (specialCoin.lane === player.lane && Math.abs(specialCoin.y - player.y) < 60 && Math.abs(specialCoin.x - player.x) < 50) {
var specialCoinPoints = Math.round(specialCoin.pointValue * scoreMultiplier);
LK.setScore(LK.getScore() + specialCoinPoints);
// Check if score exceeded 5000
if (LK.getScore() > 5000) {
// Stop background music
LK.stopMusic();
// Hide UI elements
scoreText.visible = false;
multiplierText.visible = false;
// Show victory visual
var completionImage = game.addChild(LK.getAsset('completionImage', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 0.1,
scaleY: 0.1
}));
// Play completion sound
LK.getSound('completionSound').play();
// Add victory animation
tween(completionImage, {
scaleX: 1.5,
scaleY: 1.5,
rotation: Math.PI * 2
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Wait for completion sound to finish before showing play again button
LK.setTimeout(function () {
LK.showYouWin();
}, 1000); // 1 second delay to allow sound to complete
}
});
}
var specialSound = LK.getSound('specialCollect');
specialSound.play();
// Create a temporary object to tween the music volume
var musicVolume = {
volume: 0.3
};
// Mute background music volume when special collect sound plays
tween(musicVolume, {
volume: 0
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
// Update the actual music volume
LK.playMusic('onepiecesoundtrack', {
volume: musicVolume.volume
});
}
});
// Fade back to normal volume when special collect sound finishes
LK.setTimeout(function () {
tween(musicVolume, {
volume: 0.3
}, {
duration: 100,
easing: tween.easeIn,
onFinish: function onFinish() {
// Update the actual music volume back to normal
LK.playMusic('onepiecesoundtrack', {
volume: musicVolume.volume
});
}
});
}, 630); // Duration of specialCollect sound (0.898 - 0.835) * 1000 = 63ms, but adding buffer
// Start background music on first coin collection
if (!firstCoinCollected) {
firstCoinCollected = true;
LK.playMusic('onepiecesoundtrack', {
loop: true
});
}
updateUI();
// Add special coin disappearing animation
tween(specialCoin, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
specialCoin.destroy();
}
});
specialCoins.splice(i, 1);
continue;
}
// Remove off-screen special coins
if (specialCoin.y > 2732 + 50) {
specialCoin.destroy();
specialCoins.splice(i, 1);
}
}
};
updateUI();