/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var BombObstacle = Container.expand(function () {
var self = Container.call(this);
var bombGraphics = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
// Add pulsing effect for bomb
bombGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.3) * 0.2;
bombGraphics.scaleY = 1 + Math.sin(LK.ticks * 0.3) * 0.2;
// Change color between red and orange for danger effect
var pulseValue = Math.sin(LK.ticks * 0.4);
if (pulseValue > 0) {
bombGraphics.tint = 0xFF4500; // Orange
} else {
bombGraphics.tint = 0x8B0000; // Dark red
}
};
return self;
});
var CenterLine = Container.expand(function () {
var self = Container.call(this);
var centerGraphics = self.attachAsset('centerLine', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
};
return self;
});
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
// Add slight rotation for visual effect
coinGraphics.rotation += 0.1;
};
return self;
});
var ExplosionEffect = Container.expand(function () {
var self = Container.call(this);
var explosionParts = [];
// Create main explosion circle
var mainExplosion = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5
});
explosionParts.push(mainExplosion);
// Create spark particles around the explosion
for (var i = 0; i < 8; i++) {
var spark = self.attachAsset('explosionSpark', {
anchorX: 0.5,
anchorY: 0.5
});
var angle = i / 8 * Math.PI * 2;
spark.x = Math.cos(angle) * 30;
spark.y = Math.sin(angle) * 30;
explosionParts.push(spark);
}
self.startExplosion = function () {
// Animate main explosion - scale up and fade out
tween(mainExplosion, {
scaleX: 3,
scaleY: 3,
alpha: 0
}, {
duration: 800,
easing: tween.easeOut
});
// Animate sparks - fly outward and fade
for (var i = 1; i < explosionParts.length; i++) {
var spark = explosionParts[i];
var angle = (i - 1) / 8 * Math.PI * 2;
var targetX = Math.cos(angle) * 120;
var targetY = Math.sin(angle) * 120;
tween(spark, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 600,
easing: tween.easeOut
});
}
// Remove explosion after animation
LK.setTimeout(function () {
self.destroy();
}, 900);
};
return self;
});
var ExtraHeart = Container.expand(function () {
var self = Container.call(this);
var heartGraphics = self.attachAsset('extraHeart', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
// Add pulsing effect for heart
heartGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.2) * 0.1;
heartGraphics.scaleY = 1 + Math.sin(LK.ticks * 0.2) * 0.1;
// Add gentle glow effect
var glowValue = Math.sin(LK.ticks * 0.15);
if (glowValue > 0) {
heartGraphics.tint = 0xFFB6C1; // Light pink
} else {
heartGraphics.tint = 0xFF69B4; // Hot pink
}
};
return self;
});
var ObstacleCar = Container.expand(function () {
var self = Container.call(this);
var carGraphics = self.attachAsset('obstacleCar', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 7;
self.update = function () {
self.y += self.speed;
};
return self;
});
var PlayerCar = Container.expand(function () {
var self = Container.call(this);
var carGraphics = self.attachAsset('playerCar', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
// Keep car within road bounds
if (self.x < 100) {
self.x = 100;
}
if (self.x > 1948) {
self.x = 1948;
}
};
return self;
});
var RoadLine = Container.expand(function () {
var self = Container.call(this);
var lineGraphics = self.attachAsset('roadLine', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
};
return self;
});
var SlowPotion = Container.expand(function () {
var self = Container.call(this);
var potionGraphics = self.attachAsset('slowPotion', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
// Add rotation for visual effect
potionGraphics.rotation += 0.08;
};
return self;
});
var SpeedPotion = Container.expand(function () {
var self = Container.call(this);
var potionGraphics = self.attachAsset('speedPotion', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
// Add rotation for visual effect
potionGraphics.rotation += 0.08;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228b22
});
/****
* Game Code
****/
// Game variables
var playerCar;
var obstacles = [];
var coins = [];
var roadLines = [];
var laneDividers = [];
var centerLines = [];
var explosions = [];
var speedPotions = [];
var slowPotions = [];
var bombObstacles = [];
var extraHearts = [];
var gameSpeed = 20;
var baseGameSpeed = 20;
var speedBoostActive = false;
var speedBoostTimer = 0;
var slowEffectActive = false;
var slowEffectTimer = 0;
var spawnTimer = 0;
var coinSpawnTimer = 0;
var speedPotionSpawnTimer = 0;
var slowPotionSpawnTimer = 0;
var bombSpawnTimer = 0;
var extraHeartSpawnTimer = 0;
var roadLineSpawnTimer = 0;
var laneDividerSpawnTimer = 0;
var centerLineSpawnTimer = 0;
var distanceScore = 0;
var coinScore = 0;
var coinCount = 0;
var dragNode = null;
var gameStarted = false;
var lives = 3;
var effectBar;
var effectBarFill;
var effectBarVisible = false;
// Create forest background with multiple tiles for seamless scrolling
var forestBackgrounds = [];
for (var i = 0; i < 4; i++) {
var forestBg = game.addChild(LK.getAsset('forest', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: i * 400 - 200
}));
forestBackgrounds.push(forestBg);
}
// Add trees on the sides for forest atmosphere
var trees = [];
for (var i = 0; i < 8; i++) {
// Left side trees
var leftTree = game.addChild(LK.getAsset('tree', {
anchorX: 0.5,
anchorY: 1,
x: 200,
y: i * 350 - 100
}));
var leftTreeTop = game.addChild(LK.getAsset('treeTop', {
anchorX: 0.5,
anchorY: 1,
x: 200,
y: i * 350 - 140
}));
trees.push(leftTree);
trees.push(leftTreeTop);
// Right side trees
var rightTree = game.addChild(LK.getAsset('tree', {
anchorX: 0.5,
anchorY: 1,
x: 1848,
y: i * 350 - 100
}));
var rightTreeTop = game.addChild(LK.getAsset('treeTop', {
anchorX: 0.5,
anchorY: 1,
x: 1848,
y: i * 350 - 140
}));
trees.push(rightTree);
trees.push(rightTreeTop);
}
// Create player car
playerCar = game.addChild(new PlayerCar());
playerCar.x = 1024;
playerCar.y = 2000;
// Create UI elements
var scoreText = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
var coinCountText = new Text2('Coins: 0', {
size: 40,
fill: 0xFFD700
});
coinCountText.anchor.set(0, 0);
coinCountText.x = 150;
coinCountText.y = 100;
LK.gui.topLeft.addChild(coinCountText);
var remainingText = new Text2('Remaining: 10000m', {
size: 40,
fill: 0x00FF00
});
remainingText.anchor.set(0, 0);
remainingText.x = 150;
remainingText.y = 170;
LK.gui.topLeft.addChild(remainingText);
var speedCounterText = new Text2('Speed: 20', {
size: 40,
fill: 0xFF6600
});
speedCounterText.anchor.set(0, 0);
speedCounterText.x = 150;
speedCounterText.y = 240;
LK.gui.topLeft.addChild(speedCounterText);
// Create heart display for lives
var hearts = [];
for (var h = 0; h < 3; h++) {
var heart = LK.getAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
heart.scaleX = 1.7; // Increase size by 70%
heart.scaleY = 1.7; // Increase size by 70%
heart.x = -60 - h * 50; // Position hearts from right to left
heart.y = 30;
hearts.push(heart);
LK.gui.topRight.addChild(heart);
}
// Create high score display
var highScoreText = new Text2('High Score: ' + (storage.highScore || 0), {
size: 35,
fill: 0xFFD700
});
highScoreText.anchor.set(1, 0);
highScoreText.x = -20;
highScoreText.y = 100;
LK.gui.topRight.addChild(highScoreText);
// Create effect bar (hidden initially) - positioned at bottom-left with speedometer style
effectBar = LK.getAsset('effectBarBg', {
anchorX: 0,
anchorY: 1
});
effectBar.x = 50;
effectBar.y = -50;
effectBar.alpha = 0;
effectBar.scaleX = 2.002; // Increased by 10% from 1.82 (1.82 * 1.1 = 2.002)
effectBar.scaleY = 2.002; // Increased by 10% from 1.82 (1.82 * 1.1 = 2.002)
effectBar.tint = 0xff0000; // Make effect bar red
LK.gui.bottomLeft.addChild(effectBar);
effectBarFill = LK.getAsset('effectBarFill', {
anchorX: 0,
anchorY: 0.5
});
effectBarFill.x = 0;
effectBarFill.y = 0;
effectBarFill.tint = 0xff0000; // Make effect bar fill red
effectBar.addChild(effectBarFill);
// Touch controls
function handleMove(x, y, obj) {
if (dragNode && gameStarted) {
// Constrain car to road bounds (between 324 and 1724)
var roadLeftBound = 324;
var roadRightBound = 1724;
if (x < roadLeftBound) {
playerCar.x = roadLeftBound;
} else if (x > roadRightBound) {
playerCar.x = roadRightBound;
} else {
playerCar.x = x;
}
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
if (!gameStarted) {
gameStarted = true;
LK.stopMusic();
LK.playMusic('bgMusic');
}
dragNode = playerCar;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Spawn functions
function spawnObstacle() {
var obstacle = new ObstacleCar();
obstacle.x = Math.random() * 1400 + 324; // Random X position within safe road bounds (324 to 1724)
obstacle.y = -100;
obstacle.speed = gameSpeed;
obstacles.push(obstacle);
game.addChild(obstacle);
}
function spawnCoin() {
var coin = new Coin();
var attempts = 0;
var maxAttempts = 10;
var validPosition = false;
var minDistance = 200; // Minimum distance between coins and obstacles
var coinX,
coinY = -100;
// Try to find a valid position away from obstacles
while (!validPosition && attempts < maxAttempts) {
coinX = Math.random() * 1400 + 324; // Random X position within safe road bounds (324 to 1724)
validPosition = true;
// Check distance from all existing obstacles
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
// Only check obstacles that are close vertically (within spawn range)
if (obstacle.y > -500 && obstacle.y < 500) {
var distance = Math.sqrt(Math.pow(coinX - obstacle.x, 2) + Math.pow(coinY - obstacle.y, 2));
if (distance < minDistance) {
validPosition = false;
break;
}
}
}
attempts++;
}
coin.x = coinX;
coin.y = coinY;
coin.speed = gameSpeed;
coins.push(coin);
game.addChild(coin);
}
function spawnRoadLine() {
var roadLine = new RoadLine();
roadLine.x = 1024;
roadLine.y = -100;
roadLine.speed = gameSpeed;
roadLines.push(roadLine);
game.addChild(roadLine);
}
function spawnLaneDivider() {
// Create lane dividers for left and right lanes
var leftDivider = game.addChild(LK.getAsset('laneDivider', {
anchorX: 0.5,
anchorY: 0.5,
x: 700,
y: -100
}));
leftDivider.speed = gameSpeed;
laneDividers.push(leftDivider);
var rightDivider = game.addChild(LK.getAsset('laneDivider', {
anchorX: 0.5,
anchorY: 0.5,
x: 1348,
y: -100
}));
rightDivider.speed = gameSpeed;
laneDividers.push(rightDivider);
}
function spawnCenterLine() {
var centerLine = new CenterLine();
centerLine.x = 1024;
centerLine.y = -100;
centerLine.speed = gameSpeed;
centerLines.push(centerLine);
game.addChild(centerLine);
}
function spawnSpeedPotion() {
var potion = new SpeedPotion();
var attempts = 0;
var maxAttempts = 10;
var validPosition = false;
var minDistance = 200;
var potionX,
potionY = -100;
// Try to find a valid position away from obstacles
while (!validPosition && attempts < maxAttempts) {
potionX = Math.random() * 1400 + 324;
validPosition = true;
// Check distance from all existing obstacles
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (obstacle.y > -500 && obstacle.y < 500) {
var distance = Math.sqrt(Math.pow(potionX - obstacle.x, 2) + Math.pow(potionY - obstacle.y, 2));
if (distance < minDistance) {
validPosition = false;
break;
}
}
}
attempts++;
}
potion.x = potionX;
potion.y = potionY;
potion.speed = gameSpeed;
speedPotions.push(potion);
game.addChild(potion);
}
function spawnSlowPotion() {
var potion = new SlowPotion();
var attempts = 0;
var maxAttempts = 10;
var validPosition = false;
var minDistance = 200;
var potionX,
potionY = -100;
// Try to find a valid position away from obstacles
while (!validPosition && attempts < maxAttempts) {
potionX = Math.random() * 1400 + 324;
validPosition = true;
// Check distance from all existing obstacles
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (obstacle.y > -500 && obstacle.y < 500) {
var distance = Math.sqrt(Math.pow(potionX - obstacle.x, 2) + Math.pow(potionY - obstacle.y, 2));
if (distance < minDistance) {
validPosition = false;
break;
}
}
}
attempts++;
}
potion.x = potionX;
potion.y = potionY;
potion.speed = gameSpeed;
slowPotions.push(potion);
game.addChild(potion);
}
function spawnBombObstacle() {
var bomb = new BombObstacle();
var attempts = 0;
var maxAttempts = 10;
var validPosition = false;
var minDistance = 250;
var bombX,
bombY = -100;
// Try to find a valid position away from obstacles and other items
while (!validPosition && attempts < maxAttempts) {
bombX = Math.random() * 1400 + 324;
validPosition = true;
// Check distance from all existing obstacles
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (obstacle.y > -500 && obstacle.y < 500) {
var distance = Math.sqrt(Math.pow(bombX - obstacle.x, 2) + Math.pow(bombY - obstacle.y, 2));
if (distance < minDistance) {
validPosition = false;
break;
}
}
}
attempts++;
}
bomb.x = bombX;
bomb.y = bombY;
bomb.speed = gameSpeed;
bombObstacles.push(bomb);
game.addChild(bomb);
}
function spawnExtraHeart() {
var heart = new ExtraHeart();
var attempts = 0;
var maxAttempts = 10;
var validPosition = false;
var minDistance = 250;
var heartX,
heartY = -100;
// Try to find a valid position away from obstacles
while (!validPosition && attempts < maxAttempts) {
heartX = Math.random() * 1400 + 324;
validPosition = true;
// Check distance from all existing obstacles
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (obstacle.y > -500 && obstacle.y < 500) {
var distance = Math.sqrt(Math.pow(heartX - obstacle.x, 2) + Math.pow(heartY - obstacle.y, 2));
if (distance < minDistance) {
validPosition = false;
break;
}
}
}
attempts++;
}
heart.x = heartX;
heart.y = heartY;
heart.speed = gameSpeed;
extraHearts.push(heart);
game.addChild(heart);
}
// Main game loop
game.update = function () {
if (!gameStarted) {
return;
}
// Update distance score at a slower rate
distanceScore += gameSpeed / 60;
// Increase game speed gradually with time-based acceleration
if (LK.ticks % 180 === 0) {
// More aggressive acceleration that increases with time
var timeInSeconds = LK.ticks / 60;
var acceleration = 0.8 + timeInSeconds / 30 * 0.4;
gameSpeed += acceleration;
}
// Spawn obstacles more frequently as speed increases
spawnTimer++;
var obstacleSpawnRate = Math.max(18.76, 56.27 - gameSpeed * 4.51); // Reduced by 5% from previous (increased spawn timer)
// Reduce spawn rate by 5% after score reaches 5000
var currentScore = Math.floor(distanceScore) + coinScore;
if (currentScore >= 5000) {
obstacleSpawnRate = obstacleSpawnRate * 1.0526; // 5% decrease in spawn rate (increase timer by ~5.26%)
}
if (spawnTimer > obstacleSpawnRate) {
spawnObstacle();
spawnTimer = 0;
}
// Spawn coins
coinSpawnTimer++;
if (coinSpawnTimer > 100.8) {
// Reduced spawn rate by 5% from previous (96 * 1.05 ≈ 100.8)
spawnCoin();
coinSpawnTimer = 0;
}
// Spawn speed potions (less frequently than coins)
speedPotionSpawnTimer++;
if (speedPotionSpawnTimer > 310) {
// Increased spawn rate by 20% from previous (387 * 0.8 ≈ 310)
spawnSpeedPotion();
speedPotionSpawnTimer = 0;
}
// Spawn slow potions (same frequency as speed potions)
slowPotionSpawnTimer++;
if (slowPotionSpawnTimer > 350) {
spawnSlowPotion();
slowPotionSpawnTimer = 0;
}
// Spawn bomb obstacles (less frequently than other obstacles)
bombSpawnTimer++;
if (bombSpawnTimer > 600) {
// Spawn every 10 seconds at 60 FPS
spawnBombObstacle();
bombSpawnTimer = 0;
}
// Spawn extra hearts (very rarely)
extraHeartSpawnTimer++;
if (extraHeartSpawnTimer > 1140) {
// Spawn every 19 seconds at 60 FPS (5% increase in spawn rate)
spawnExtraHeart();
extraHeartSpawnTimer = 0;
}
// Handle speed boost timer
if (speedBoostActive) {
speedBoostTimer--;
if (speedBoostTimer <= 0) {
speedBoostActive = false;
gameSpeed = baseGameSpeed;
// Hide effect bar
effectBarVisible = false;
tween(effectBar, {
alpha: 0
}, {
duration: 300
});
// Visual feedback that boost ended
tween(playerCar, {
tint: 0xFFFFFF
}, {
duration: 500
});
}
}
// Handle slow effect timer
if (slowEffectActive) {
slowEffectTimer--;
if (slowEffectTimer <= 0) {
slowEffectActive = false;
gameSpeed = baseGameSpeed;
// Hide effect bar
effectBarVisible = false;
tween(effectBar, {
alpha: 0
}, {
duration: 300
});
// Visual feedback that slow effect ended
tween(playerCar, {
tint: 0xFFFFFF
}, {
duration: 500
});
}
}
// Spawn road lines
roadLineSpawnTimer++;
if (roadLineSpawnTimer > 40) {
spawnRoadLine();
roadLineSpawnTimer = 0;
}
// Spawn lane dividers more frequently as speed increases
laneDividerSpawnTimer++;
var laneDividerSpawnRate = Math.max(15, 30 - gameSpeed * 1.5);
if (laneDividerSpawnTimer > laneDividerSpawnRate) {
spawnLaneDivider();
laneDividerSpawnTimer = 0;
}
// Spawn center lines more frequently as speed increases
centerLineSpawnTimer++;
var centerLineSpawnRate = Math.max(12, 25 - gameSpeed * 1.2);
if (centerLineSpawnTimer > centerLineSpawnRate) {
spawnCenterLine();
centerLineSpawnTimer = 0;
}
// Update and check obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
if (obstacle.lastY === undefined) {
obstacle.lastY = obstacle.y;
}
// Remove off-screen obstacles
if (obstacle.lastY < 2800 && obstacle.y >= 2800) {
obstacle.destroy();
obstacles.splice(i, 1);
continue;
}
// Check collision with player
if (obstacle.intersects(playerCar)) {
// Create explosion effect at collision point
var explosion = new ExplosionEffect();
explosion.x = playerCar.x;
explosion.y = playerCar.y;
explosions.push(explosion);
game.addChild(explosion);
explosion.startExplosion();
LK.getSound('crash').play();
LK.effects.flashScreen(0xff0000, 1000);
// Decrease lives
lives--;
// Update heart display - show hearts based on remaining lives
for (var h = 0; h < hearts.length; h++) {
if (h < lives) {
hearts[h].alpha = 1.0; // Show heart if player has this life
} else {
hearts[h].alpha = 0.3; // Hide heart if player doesn't have this life
}
}
// Remove the obstacle that caused collision
obstacle.destroy();
obstacles.splice(i, 1);
// Check if game over
if (lives <= 0) {
// Ensure final score is set correctly before game over
var finalScore = Math.floor(distanceScore) + coinScore;
LK.setScore(finalScore);
// Update high score if current score is better
if (finalScore > (storage.highScore || 0)) {
storage.highScore = finalScore;
}
LK.showGameOver();
return;
}
continue;
}
obstacle.lastY = obstacle.y;
}
// Update and check coins
for (var j = coins.length - 1; j >= 0; j--) {
var coin = coins[j];
if (coin.lastY === undefined) {
coin.lastY = coin.y;
}
if (coin.lastCollected === undefined) {
coin.lastCollected = false;
}
// Remove off-screen coins
if (coin.lastY < 2800 && coin.y >= 2800) {
coin.destroy();
coins.splice(j, 1);
continue;
}
// Check collection
var currentCollected = coin.intersects(playerCar);
if (!coin.lastCollected && currentCollected) {
LK.getSound('collectCoin').play();
coinCount += 1;
coinScore += 100;
LK.setScore(Math.floor(distanceScore) + coinScore);
coin.destroy();
coins.splice(j, 1);
continue;
}
coin.lastY = coin.y;
coin.lastCollected = currentCollected;
}
// Update and check speed potions
for (var sp = speedPotions.length - 1; sp >= 0; sp--) {
var potion = speedPotions[sp];
if (potion.lastY === undefined) {
potion.lastY = potion.y;
}
if (potion.lastCollected === undefined) {
potion.lastCollected = false;
}
// Remove off-screen potions
if (potion.lastY < 2800 && potion.y >= 2800) {
potion.destroy();
speedPotions.splice(sp, 1);
continue;
}
// Check collection
var currentPotionCollected = potion.intersects(playerCar);
if (!potion.lastCollected && currentPotionCollected) {
// Deactivate slow effect if active
if (slowEffectActive) {
slowEffectActive = false;
slowEffectTimer = 0;
} //{6D_new}
// Activate speed boost
speedBoostActive = true;
speedBoostTimer = 300; // 5 seconds at 60 FPS
baseGameSpeed = gameSpeed;
gameSpeed = gameSpeed * 1.5; // 50% speed increase
// Show effect bar
effectBarVisible = true;
effectBarFill.tint = 0x00FFFF; // Blue for speed boost
tween(effectBar, {
alpha: 1
}, {
duration: 200
});
// Visual feedback for speed boost
tween(playerCar, {
tint: 0x00FFFF
}, {
duration: 300
});
potion.destroy();
speedPotions.splice(sp, 1);
continue;
}
potion.lastY = potion.y;
potion.lastCollected = currentPotionCollected;
}
// Update and check slow potions
for (var slp = slowPotions.length - 1; slp >= 0; slp--) {
var slowPotion = slowPotions[slp];
if (slowPotion.lastY === undefined) {
slowPotion.lastY = slowPotion.y;
}
if (slowPotion.lastCollected === undefined) {
slowPotion.lastCollected = false;
}
// Remove off-screen potions
if (slowPotion.lastY < 2800 && slowPotion.y >= 2800) {
slowPotion.destroy();
slowPotions.splice(slp, 1);
continue;
}
// Check collection
var currentSlowPotionCollected = slowPotion.intersects(playerCar);
if (!slowPotion.lastCollected && currentSlowPotionCollected) {
// Deactivate speed effect if active
if (speedBoostActive) {
speedBoostActive = false;
speedBoostTimer = 0;
} //{74_new}
// Activate slow effect
slowEffectActive = true;
slowEffectTimer = 300; // 5 seconds at 60 FPS
baseGameSpeed = gameSpeed;
gameSpeed = gameSpeed * 0.5; // 50% speed decrease
// Show effect bar
effectBarVisible = true;
effectBarFill.tint = 0xFF6600; // Orange for slow effect
tween(effectBar, {
alpha: 1
}, {
duration: 200
});
// Visual feedback for slow effect
tween(playerCar, {
tint: 0x0000FF
}, {
duration: 300
});
slowPotion.destroy();
slowPotions.splice(slp, 1);
continue;
}
slowPotion.lastY = slowPotion.y;
slowPotion.lastCollected = currentSlowPotionCollected;
}
// Update and check bomb obstacles
for (var bo = bombObstacles.length - 1; bo >= 0; bo--) {
var bomb = bombObstacles[bo];
if (bomb.lastY === undefined) {
bomb.lastY = bomb.y;
}
if (bomb.lastCollected === undefined) {
bomb.lastCollected = false;
}
// Remove off-screen bombs
if (bomb.lastY < 2800 && bomb.y >= 2800) {
bomb.destroy();
bombObstacles.splice(bo, 1);
continue;
}
// Check collision with player
var currentBombCollected = bomb.intersects(playerCar);
if (!bomb.lastCollected && currentBombCollected) {
// Play bomb explosion sound immediately - continues even after game over
LK.getSound('bombExplosion').play();
// Create massive explosion effect with much larger scale
var explosion = new ExplosionEffect();
explosion.x = playerCar.x;
explosion.y = playerCar.y;
explosions.push(explosion);
game.addChild(explosion);
// Scale up the explosion to make it massive
explosion.scaleX = 3;
explosion.scaleY = 3;
// Create additional large explosion effects around the impact
for (var ex = 0; ex < 5; ex++) {
var additionalExplosion = new ExplosionEffect();
additionalExplosion.x = playerCar.x + (Math.random() - 0.5) * 400;
additionalExplosion.y = playerCar.y + (Math.random() - 0.5) * 400;
additionalExplosion.scaleX = 2 + Math.random();
additionalExplosion.scaleY = 2 + Math.random();
explosions.push(additionalExplosion);
game.addChild(additionalExplosion);
additionalExplosion.startExplosion();
}
explosion.startExplosion();
// Screen shake effect using tween
tween(game, {
x: game.x + 20
}, {
duration: 50,
onFinish: function onFinish() {
tween(game, {
x: game.x - 40
}, {
duration: 50,
onFinish: function onFinish() {
tween(game, {
x: game.x + 40
}, {
duration: 50,
onFinish: function onFinish() {
tween(game, {
x: game.x - 20
}, {
duration: 50
});
}
});
}
});
}
});
// Remove ALL hearts (set lives to 0)
lives = 0;
// Hide all hearts
for (var h = 0; h < hearts.length; h++) {
hearts[h].alpha = 0.3;
}
// Remove the bomb that caused collision
bomb.destroy();
bombObstacles.splice(bo, 1);
// Game over immediately
var finalScore = Math.floor(distanceScore) + coinScore;
LK.setScore(finalScore);
// Update high score if current score is better
if (finalScore > (storage.highScore || 0)) {
storage.highScore = finalScore;
}
// Create dramatic flash sequence for atomic bomb explosion - 50% more intense
LK.effects.flashScreen(0xffffff, 3450); // Intense white flash first (+50% duration)
LK.setTimeout(function () {
LK.effects.flashScreen(0xff0000, 5250); // Red explosion flash (+50% duration)
}, 50); // Faster transition for intensity
LK.setTimeout(function () {
LK.effects.flashScreen(0xffffff, 2000); // Additional white flash for intensity
}, 150);
LK.setTimeout(function () {
LK.effects.flashScreen(0xff4500, 4500); // Orange explosion flash (+50% duration)
}, 300); // Faster transition
LK.setTimeout(function () {
LK.effects.flashScreen(0xff0000, 2500); // Additional red flash
}, 600);
LK.setTimeout(function () {
LK.effects.flashScreen(0xffff00, 4200); // Yellow afterglow (+50% duration)
}, 900);
LK.setTimeout(function () {
LK.effects.flashScreen(0xff4500, 2000); // Final orange flash for intensity
}, 1200);
LK.showGameOver();
return;
}
bomb.lastY = bomb.y;
bomb.lastCollected = currentBombCollected;
}
// Update and check extra hearts
for (var eh = extraHearts.length - 1; eh >= 0; eh--) {
var extraHeart = extraHearts[eh];
if (extraHeart.lastY === undefined) {
extraHeart.lastY = extraHeart.y;
}
if (extraHeart.lastCollected === undefined) {
extraHeart.lastCollected = false;
}
// Remove off-screen hearts
if (extraHeart.lastY < 2800 && extraHeart.y >= 2800) {
extraHeart.destroy();
extraHearts.splice(eh, 1);
continue;
}
// Check collection
var currentHeartCollected = extraHeart.intersects(playerCar);
if (!extraHeart.lastCollected && currentHeartCollected) {
// Increase lives by 1
lives += 1;
// Update heart display - show hearts based on current lives
for (var h = 0; h < hearts.length; h++) {
if (h < lives) {
hearts[h].alpha = 1.0; // Show heart if player has this life
} else {
hearts[h].alpha = 0.3; // Hide heart if player doesn't have this life
}
}
// Flash all hearts to show extra life gained
for (var h = 0; h < hearts.length; h++) {
(function (heart) {
tween(heart, {
tint: 0x00FF00
}, {
duration: 300,
onFinish: function onFinish() {
tween(heart, {
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
})(hearts[h]);
}
// Visual feedback for gaining extra life
LK.effects.flashScreen(0x00FF00, 500);
tween(playerCar, {
tint: 0x00FF00
}, {
duration: 500,
onFinish: function onFinish() {
tween(playerCar, {
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
extraHeart.destroy();
extraHearts.splice(eh, 1);
continue;
}
extraHeart.lastY = extraHeart.y;
extraHeart.lastCollected = currentHeartCollected;
}
// Update and remove road lines
for (var k = roadLines.length - 1; k >= 0; k--) {
var roadLine = roadLines[k];
if (roadLine.lastY === undefined) {
roadLine.lastY = roadLine.y;
}
if (roadLine.lastY < 2800 && roadLine.y >= 2800) {
roadLine.destroy();
roadLines.splice(k, 1);
continue;
}
roadLine.lastY = roadLine.y;
}
// Update and remove lane dividers
for (var d = laneDividers.length - 1; d >= 0; d--) {
var divider = laneDividers[d];
if (divider.lastY === undefined) {
divider.lastY = divider.y;
}
divider.y += divider.speed;
if (divider.lastY < 2800 && divider.y >= 2800) {
divider.destroy();
laneDividers.splice(d, 1);
continue;
}
divider.lastY = divider.y;
}
// Update and remove center lines
for (var c = centerLines.length - 1; c >= 0; c--) {
var centerLine = centerLines[c];
if (centerLine.lastY === undefined) {
centerLine.lastY = centerLine.y;
}
if (centerLine.lastY < 2800 && centerLine.y >= 2800) {
centerLine.destroy();
centerLines.splice(c, 1);
continue;
}
centerLine.lastY = centerLine.y;
}
// Check win condition
var totalScore = Math.floor(distanceScore) + coinScore;
if (totalScore >= 10000) {
// Ensure final score is set correctly before showing win
LK.setScore(totalScore);
// Update high score if current score is better
if (totalScore > (storage.highScore || 0)) {
storage.highScore = totalScore;
}
LK.showYouWin();
return;
}
// Update UI
scoreText.setText('Score: ' + totalScore);
var remainingDistance = Math.max(0, 10000 - totalScore);
remainingText.setText('Remaining: ' + remainingDistance + 'm');
coinCountText.setText('Coins: ' + coinCount);
highScoreText.setText('High Score: ' + (storage.highScore || 0));
// Scroll forest backgrounds for realistic movement
for (var bg = 0; bg < forestBackgrounds.length; bg++) {
forestBackgrounds[bg].y += gameSpeed;
// Reset position when tile moves off screen
if (forestBackgrounds[bg].y > 2932) {
forestBackgrounds[bg].y = forestBackgrounds[bg].y - 4 * 400;
}
}
// Scroll trees for realistic movement
for (var t = 0; t < trees.length; t++) {
trees[t].y += gameSpeed;
// Reset position when tree moves off screen
if (trees[t].y > 2932) {
trees[t].y = trees[t].y - 8 * 350;
}
}
// Update speeds for all moving objects
for (var l = 0; l < obstacles.length; l++) {
obstacles[l].speed = gameSpeed;
}
for (var m = 0; m < coins.length; m++) {
coins[m].speed = gameSpeed;
}
for (var n = 0; n < roadLines.length; n++) {
roadLines[n].speed = gameSpeed;
}
for (var d = 0; d < laneDividers.length; d++) {
laneDividers[d].speed = gameSpeed;
}
for (var cl = 0; cl < centerLines.length; cl++) {
centerLines[cl].speed = gameSpeed;
}
for (var sp = 0; sp < speedPotions.length; sp++) {
speedPotions[sp].speed = gameSpeed;
}
for (var slp = 0; slp < slowPotions.length; slp++) {
slowPotions[slp].speed = gameSpeed;
}
for (var bo = 0; bo < bombObstacles.length; bo++) {
bombObstacles[bo].speed = gameSpeed;
}
for (var eh = 0; eh < extraHearts.length; eh++) {
extraHearts[eh].speed = gameSpeed;
}
// Update effect bar
if (effectBarVisible) {
var currentTimer = 0;
var maxTimer = 300;
if (speedBoostActive) {
currentTimer = speedBoostTimer;
} else if (slowEffectActive) {
currentTimer = slowEffectTimer;
}
// Update bar width based on remaining time
var fillWidth = currentTimer / maxTimer * 348;
effectBarFill.scaleX = Math.max(0, fillWidth / 348);
}
// Update speed counter
speedCounterText.setText('Speed: ' + Math.floor(gameSpeed));
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var BombObstacle = Container.expand(function () {
var self = Container.call(this);
var bombGraphics = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
// Add pulsing effect for bomb
bombGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.3) * 0.2;
bombGraphics.scaleY = 1 + Math.sin(LK.ticks * 0.3) * 0.2;
// Change color between red and orange for danger effect
var pulseValue = Math.sin(LK.ticks * 0.4);
if (pulseValue > 0) {
bombGraphics.tint = 0xFF4500; // Orange
} else {
bombGraphics.tint = 0x8B0000; // Dark red
}
};
return self;
});
var CenterLine = Container.expand(function () {
var self = Container.call(this);
var centerGraphics = self.attachAsset('centerLine', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
};
return self;
});
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
// Add slight rotation for visual effect
coinGraphics.rotation += 0.1;
};
return self;
});
var ExplosionEffect = Container.expand(function () {
var self = Container.call(this);
var explosionParts = [];
// Create main explosion circle
var mainExplosion = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5
});
explosionParts.push(mainExplosion);
// Create spark particles around the explosion
for (var i = 0; i < 8; i++) {
var spark = self.attachAsset('explosionSpark', {
anchorX: 0.5,
anchorY: 0.5
});
var angle = i / 8 * Math.PI * 2;
spark.x = Math.cos(angle) * 30;
spark.y = Math.sin(angle) * 30;
explosionParts.push(spark);
}
self.startExplosion = function () {
// Animate main explosion - scale up and fade out
tween(mainExplosion, {
scaleX: 3,
scaleY: 3,
alpha: 0
}, {
duration: 800,
easing: tween.easeOut
});
// Animate sparks - fly outward and fade
for (var i = 1; i < explosionParts.length; i++) {
var spark = explosionParts[i];
var angle = (i - 1) / 8 * Math.PI * 2;
var targetX = Math.cos(angle) * 120;
var targetY = Math.sin(angle) * 120;
tween(spark, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 600,
easing: tween.easeOut
});
}
// Remove explosion after animation
LK.setTimeout(function () {
self.destroy();
}, 900);
};
return self;
});
var ExtraHeart = Container.expand(function () {
var self = Container.call(this);
var heartGraphics = self.attachAsset('extraHeart', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
// Add pulsing effect for heart
heartGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.2) * 0.1;
heartGraphics.scaleY = 1 + Math.sin(LK.ticks * 0.2) * 0.1;
// Add gentle glow effect
var glowValue = Math.sin(LK.ticks * 0.15);
if (glowValue > 0) {
heartGraphics.tint = 0xFFB6C1; // Light pink
} else {
heartGraphics.tint = 0xFF69B4; // Hot pink
}
};
return self;
});
var ObstacleCar = Container.expand(function () {
var self = Container.call(this);
var carGraphics = self.attachAsset('obstacleCar', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 7;
self.update = function () {
self.y += self.speed;
};
return self;
});
var PlayerCar = Container.expand(function () {
var self = Container.call(this);
var carGraphics = self.attachAsset('playerCar', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
// Keep car within road bounds
if (self.x < 100) {
self.x = 100;
}
if (self.x > 1948) {
self.x = 1948;
}
};
return self;
});
var RoadLine = Container.expand(function () {
var self = Container.call(this);
var lineGraphics = self.attachAsset('roadLine', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
};
return self;
});
var SlowPotion = Container.expand(function () {
var self = Container.call(this);
var potionGraphics = self.attachAsset('slowPotion', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
// Add rotation for visual effect
potionGraphics.rotation += 0.08;
};
return self;
});
var SpeedPotion = Container.expand(function () {
var self = Container.call(this);
var potionGraphics = self.attachAsset('speedPotion', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed;
// Add rotation for visual effect
potionGraphics.rotation += 0.08;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228b22
});
/****
* Game Code
****/
// Game variables
var playerCar;
var obstacles = [];
var coins = [];
var roadLines = [];
var laneDividers = [];
var centerLines = [];
var explosions = [];
var speedPotions = [];
var slowPotions = [];
var bombObstacles = [];
var extraHearts = [];
var gameSpeed = 20;
var baseGameSpeed = 20;
var speedBoostActive = false;
var speedBoostTimer = 0;
var slowEffectActive = false;
var slowEffectTimer = 0;
var spawnTimer = 0;
var coinSpawnTimer = 0;
var speedPotionSpawnTimer = 0;
var slowPotionSpawnTimer = 0;
var bombSpawnTimer = 0;
var extraHeartSpawnTimer = 0;
var roadLineSpawnTimer = 0;
var laneDividerSpawnTimer = 0;
var centerLineSpawnTimer = 0;
var distanceScore = 0;
var coinScore = 0;
var coinCount = 0;
var dragNode = null;
var gameStarted = false;
var lives = 3;
var effectBar;
var effectBarFill;
var effectBarVisible = false;
// Create forest background with multiple tiles for seamless scrolling
var forestBackgrounds = [];
for (var i = 0; i < 4; i++) {
var forestBg = game.addChild(LK.getAsset('forest', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: i * 400 - 200
}));
forestBackgrounds.push(forestBg);
}
// Add trees on the sides for forest atmosphere
var trees = [];
for (var i = 0; i < 8; i++) {
// Left side trees
var leftTree = game.addChild(LK.getAsset('tree', {
anchorX: 0.5,
anchorY: 1,
x: 200,
y: i * 350 - 100
}));
var leftTreeTop = game.addChild(LK.getAsset('treeTop', {
anchorX: 0.5,
anchorY: 1,
x: 200,
y: i * 350 - 140
}));
trees.push(leftTree);
trees.push(leftTreeTop);
// Right side trees
var rightTree = game.addChild(LK.getAsset('tree', {
anchorX: 0.5,
anchorY: 1,
x: 1848,
y: i * 350 - 100
}));
var rightTreeTop = game.addChild(LK.getAsset('treeTop', {
anchorX: 0.5,
anchorY: 1,
x: 1848,
y: i * 350 - 140
}));
trees.push(rightTree);
trees.push(rightTreeTop);
}
// Create player car
playerCar = game.addChild(new PlayerCar());
playerCar.x = 1024;
playerCar.y = 2000;
// Create UI elements
var scoreText = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
var coinCountText = new Text2('Coins: 0', {
size: 40,
fill: 0xFFD700
});
coinCountText.anchor.set(0, 0);
coinCountText.x = 150;
coinCountText.y = 100;
LK.gui.topLeft.addChild(coinCountText);
var remainingText = new Text2('Remaining: 10000m', {
size: 40,
fill: 0x00FF00
});
remainingText.anchor.set(0, 0);
remainingText.x = 150;
remainingText.y = 170;
LK.gui.topLeft.addChild(remainingText);
var speedCounterText = new Text2('Speed: 20', {
size: 40,
fill: 0xFF6600
});
speedCounterText.anchor.set(0, 0);
speedCounterText.x = 150;
speedCounterText.y = 240;
LK.gui.topLeft.addChild(speedCounterText);
// Create heart display for lives
var hearts = [];
for (var h = 0; h < 3; h++) {
var heart = LK.getAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
heart.scaleX = 1.7; // Increase size by 70%
heart.scaleY = 1.7; // Increase size by 70%
heart.x = -60 - h * 50; // Position hearts from right to left
heart.y = 30;
hearts.push(heart);
LK.gui.topRight.addChild(heart);
}
// Create high score display
var highScoreText = new Text2('High Score: ' + (storage.highScore || 0), {
size: 35,
fill: 0xFFD700
});
highScoreText.anchor.set(1, 0);
highScoreText.x = -20;
highScoreText.y = 100;
LK.gui.topRight.addChild(highScoreText);
// Create effect bar (hidden initially) - positioned at bottom-left with speedometer style
effectBar = LK.getAsset('effectBarBg', {
anchorX: 0,
anchorY: 1
});
effectBar.x = 50;
effectBar.y = -50;
effectBar.alpha = 0;
effectBar.scaleX = 2.002; // Increased by 10% from 1.82 (1.82 * 1.1 = 2.002)
effectBar.scaleY = 2.002; // Increased by 10% from 1.82 (1.82 * 1.1 = 2.002)
effectBar.tint = 0xff0000; // Make effect bar red
LK.gui.bottomLeft.addChild(effectBar);
effectBarFill = LK.getAsset('effectBarFill', {
anchorX: 0,
anchorY: 0.5
});
effectBarFill.x = 0;
effectBarFill.y = 0;
effectBarFill.tint = 0xff0000; // Make effect bar fill red
effectBar.addChild(effectBarFill);
// Touch controls
function handleMove(x, y, obj) {
if (dragNode && gameStarted) {
// Constrain car to road bounds (between 324 and 1724)
var roadLeftBound = 324;
var roadRightBound = 1724;
if (x < roadLeftBound) {
playerCar.x = roadLeftBound;
} else if (x > roadRightBound) {
playerCar.x = roadRightBound;
} else {
playerCar.x = x;
}
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
if (!gameStarted) {
gameStarted = true;
LK.stopMusic();
LK.playMusic('bgMusic');
}
dragNode = playerCar;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Spawn functions
function spawnObstacle() {
var obstacle = new ObstacleCar();
obstacle.x = Math.random() * 1400 + 324; // Random X position within safe road bounds (324 to 1724)
obstacle.y = -100;
obstacle.speed = gameSpeed;
obstacles.push(obstacle);
game.addChild(obstacle);
}
function spawnCoin() {
var coin = new Coin();
var attempts = 0;
var maxAttempts = 10;
var validPosition = false;
var minDistance = 200; // Minimum distance between coins and obstacles
var coinX,
coinY = -100;
// Try to find a valid position away from obstacles
while (!validPosition && attempts < maxAttempts) {
coinX = Math.random() * 1400 + 324; // Random X position within safe road bounds (324 to 1724)
validPosition = true;
// Check distance from all existing obstacles
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
// Only check obstacles that are close vertically (within spawn range)
if (obstacle.y > -500 && obstacle.y < 500) {
var distance = Math.sqrt(Math.pow(coinX - obstacle.x, 2) + Math.pow(coinY - obstacle.y, 2));
if (distance < minDistance) {
validPosition = false;
break;
}
}
}
attempts++;
}
coin.x = coinX;
coin.y = coinY;
coin.speed = gameSpeed;
coins.push(coin);
game.addChild(coin);
}
function spawnRoadLine() {
var roadLine = new RoadLine();
roadLine.x = 1024;
roadLine.y = -100;
roadLine.speed = gameSpeed;
roadLines.push(roadLine);
game.addChild(roadLine);
}
function spawnLaneDivider() {
// Create lane dividers for left and right lanes
var leftDivider = game.addChild(LK.getAsset('laneDivider', {
anchorX: 0.5,
anchorY: 0.5,
x: 700,
y: -100
}));
leftDivider.speed = gameSpeed;
laneDividers.push(leftDivider);
var rightDivider = game.addChild(LK.getAsset('laneDivider', {
anchorX: 0.5,
anchorY: 0.5,
x: 1348,
y: -100
}));
rightDivider.speed = gameSpeed;
laneDividers.push(rightDivider);
}
function spawnCenterLine() {
var centerLine = new CenterLine();
centerLine.x = 1024;
centerLine.y = -100;
centerLine.speed = gameSpeed;
centerLines.push(centerLine);
game.addChild(centerLine);
}
function spawnSpeedPotion() {
var potion = new SpeedPotion();
var attempts = 0;
var maxAttempts = 10;
var validPosition = false;
var minDistance = 200;
var potionX,
potionY = -100;
// Try to find a valid position away from obstacles
while (!validPosition && attempts < maxAttempts) {
potionX = Math.random() * 1400 + 324;
validPosition = true;
// Check distance from all existing obstacles
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (obstacle.y > -500 && obstacle.y < 500) {
var distance = Math.sqrt(Math.pow(potionX - obstacle.x, 2) + Math.pow(potionY - obstacle.y, 2));
if (distance < minDistance) {
validPosition = false;
break;
}
}
}
attempts++;
}
potion.x = potionX;
potion.y = potionY;
potion.speed = gameSpeed;
speedPotions.push(potion);
game.addChild(potion);
}
function spawnSlowPotion() {
var potion = new SlowPotion();
var attempts = 0;
var maxAttempts = 10;
var validPosition = false;
var minDistance = 200;
var potionX,
potionY = -100;
// Try to find a valid position away from obstacles
while (!validPosition && attempts < maxAttempts) {
potionX = Math.random() * 1400 + 324;
validPosition = true;
// Check distance from all existing obstacles
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (obstacle.y > -500 && obstacle.y < 500) {
var distance = Math.sqrt(Math.pow(potionX - obstacle.x, 2) + Math.pow(potionY - obstacle.y, 2));
if (distance < minDistance) {
validPosition = false;
break;
}
}
}
attempts++;
}
potion.x = potionX;
potion.y = potionY;
potion.speed = gameSpeed;
slowPotions.push(potion);
game.addChild(potion);
}
function spawnBombObstacle() {
var bomb = new BombObstacle();
var attempts = 0;
var maxAttempts = 10;
var validPosition = false;
var minDistance = 250;
var bombX,
bombY = -100;
// Try to find a valid position away from obstacles and other items
while (!validPosition && attempts < maxAttempts) {
bombX = Math.random() * 1400 + 324;
validPosition = true;
// Check distance from all existing obstacles
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (obstacle.y > -500 && obstacle.y < 500) {
var distance = Math.sqrt(Math.pow(bombX - obstacle.x, 2) + Math.pow(bombY - obstacle.y, 2));
if (distance < minDistance) {
validPosition = false;
break;
}
}
}
attempts++;
}
bomb.x = bombX;
bomb.y = bombY;
bomb.speed = gameSpeed;
bombObstacles.push(bomb);
game.addChild(bomb);
}
function spawnExtraHeart() {
var heart = new ExtraHeart();
var attempts = 0;
var maxAttempts = 10;
var validPosition = false;
var minDistance = 250;
var heartX,
heartY = -100;
// Try to find a valid position away from obstacles
while (!validPosition && attempts < maxAttempts) {
heartX = Math.random() * 1400 + 324;
validPosition = true;
// Check distance from all existing obstacles
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (obstacle.y > -500 && obstacle.y < 500) {
var distance = Math.sqrt(Math.pow(heartX - obstacle.x, 2) + Math.pow(heartY - obstacle.y, 2));
if (distance < minDistance) {
validPosition = false;
break;
}
}
}
attempts++;
}
heart.x = heartX;
heart.y = heartY;
heart.speed = gameSpeed;
extraHearts.push(heart);
game.addChild(heart);
}
// Main game loop
game.update = function () {
if (!gameStarted) {
return;
}
// Update distance score at a slower rate
distanceScore += gameSpeed / 60;
// Increase game speed gradually with time-based acceleration
if (LK.ticks % 180 === 0) {
// More aggressive acceleration that increases with time
var timeInSeconds = LK.ticks / 60;
var acceleration = 0.8 + timeInSeconds / 30 * 0.4;
gameSpeed += acceleration;
}
// Spawn obstacles more frequently as speed increases
spawnTimer++;
var obstacleSpawnRate = Math.max(18.76, 56.27 - gameSpeed * 4.51); // Reduced by 5% from previous (increased spawn timer)
// Reduce spawn rate by 5% after score reaches 5000
var currentScore = Math.floor(distanceScore) + coinScore;
if (currentScore >= 5000) {
obstacleSpawnRate = obstacleSpawnRate * 1.0526; // 5% decrease in spawn rate (increase timer by ~5.26%)
}
if (spawnTimer > obstacleSpawnRate) {
spawnObstacle();
spawnTimer = 0;
}
// Spawn coins
coinSpawnTimer++;
if (coinSpawnTimer > 100.8) {
// Reduced spawn rate by 5% from previous (96 * 1.05 ≈ 100.8)
spawnCoin();
coinSpawnTimer = 0;
}
// Spawn speed potions (less frequently than coins)
speedPotionSpawnTimer++;
if (speedPotionSpawnTimer > 310) {
// Increased spawn rate by 20% from previous (387 * 0.8 ≈ 310)
spawnSpeedPotion();
speedPotionSpawnTimer = 0;
}
// Spawn slow potions (same frequency as speed potions)
slowPotionSpawnTimer++;
if (slowPotionSpawnTimer > 350) {
spawnSlowPotion();
slowPotionSpawnTimer = 0;
}
// Spawn bomb obstacles (less frequently than other obstacles)
bombSpawnTimer++;
if (bombSpawnTimer > 600) {
// Spawn every 10 seconds at 60 FPS
spawnBombObstacle();
bombSpawnTimer = 0;
}
// Spawn extra hearts (very rarely)
extraHeartSpawnTimer++;
if (extraHeartSpawnTimer > 1140) {
// Spawn every 19 seconds at 60 FPS (5% increase in spawn rate)
spawnExtraHeart();
extraHeartSpawnTimer = 0;
}
// Handle speed boost timer
if (speedBoostActive) {
speedBoostTimer--;
if (speedBoostTimer <= 0) {
speedBoostActive = false;
gameSpeed = baseGameSpeed;
// Hide effect bar
effectBarVisible = false;
tween(effectBar, {
alpha: 0
}, {
duration: 300
});
// Visual feedback that boost ended
tween(playerCar, {
tint: 0xFFFFFF
}, {
duration: 500
});
}
}
// Handle slow effect timer
if (slowEffectActive) {
slowEffectTimer--;
if (slowEffectTimer <= 0) {
slowEffectActive = false;
gameSpeed = baseGameSpeed;
// Hide effect bar
effectBarVisible = false;
tween(effectBar, {
alpha: 0
}, {
duration: 300
});
// Visual feedback that slow effect ended
tween(playerCar, {
tint: 0xFFFFFF
}, {
duration: 500
});
}
}
// Spawn road lines
roadLineSpawnTimer++;
if (roadLineSpawnTimer > 40) {
spawnRoadLine();
roadLineSpawnTimer = 0;
}
// Spawn lane dividers more frequently as speed increases
laneDividerSpawnTimer++;
var laneDividerSpawnRate = Math.max(15, 30 - gameSpeed * 1.5);
if (laneDividerSpawnTimer > laneDividerSpawnRate) {
spawnLaneDivider();
laneDividerSpawnTimer = 0;
}
// Spawn center lines more frequently as speed increases
centerLineSpawnTimer++;
var centerLineSpawnRate = Math.max(12, 25 - gameSpeed * 1.2);
if (centerLineSpawnTimer > centerLineSpawnRate) {
spawnCenterLine();
centerLineSpawnTimer = 0;
}
// Update and check obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
if (obstacle.lastY === undefined) {
obstacle.lastY = obstacle.y;
}
// Remove off-screen obstacles
if (obstacle.lastY < 2800 && obstacle.y >= 2800) {
obstacle.destroy();
obstacles.splice(i, 1);
continue;
}
// Check collision with player
if (obstacle.intersects(playerCar)) {
// Create explosion effect at collision point
var explosion = new ExplosionEffect();
explosion.x = playerCar.x;
explosion.y = playerCar.y;
explosions.push(explosion);
game.addChild(explosion);
explosion.startExplosion();
LK.getSound('crash').play();
LK.effects.flashScreen(0xff0000, 1000);
// Decrease lives
lives--;
// Update heart display - show hearts based on remaining lives
for (var h = 0; h < hearts.length; h++) {
if (h < lives) {
hearts[h].alpha = 1.0; // Show heart if player has this life
} else {
hearts[h].alpha = 0.3; // Hide heart if player doesn't have this life
}
}
// Remove the obstacle that caused collision
obstacle.destroy();
obstacles.splice(i, 1);
// Check if game over
if (lives <= 0) {
// Ensure final score is set correctly before game over
var finalScore = Math.floor(distanceScore) + coinScore;
LK.setScore(finalScore);
// Update high score if current score is better
if (finalScore > (storage.highScore || 0)) {
storage.highScore = finalScore;
}
LK.showGameOver();
return;
}
continue;
}
obstacle.lastY = obstacle.y;
}
// Update and check coins
for (var j = coins.length - 1; j >= 0; j--) {
var coin = coins[j];
if (coin.lastY === undefined) {
coin.lastY = coin.y;
}
if (coin.lastCollected === undefined) {
coin.lastCollected = false;
}
// Remove off-screen coins
if (coin.lastY < 2800 && coin.y >= 2800) {
coin.destroy();
coins.splice(j, 1);
continue;
}
// Check collection
var currentCollected = coin.intersects(playerCar);
if (!coin.lastCollected && currentCollected) {
LK.getSound('collectCoin').play();
coinCount += 1;
coinScore += 100;
LK.setScore(Math.floor(distanceScore) + coinScore);
coin.destroy();
coins.splice(j, 1);
continue;
}
coin.lastY = coin.y;
coin.lastCollected = currentCollected;
}
// Update and check speed potions
for (var sp = speedPotions.length - 1; sp >= 0; sp--) {
var potion = speedPotions[sp];
if (potion.lastY === undefined) {
potion.lastY = potion.y;
}
if (potion.lastCollected === undefined) {
potion.lastCollected = false;
}
// Remove off-screen potions
if (potion.lastY < 2800 && potion.y >= 2800) {
potion.destroy();
speedPotions.splice(sp, 1);
continue;
}
// Check collection
var currentPotionCollected = potion.intersects(playerCar);
if (!potion.lastCollected && currentPotionCollected) {
// Deactivate slow effect if active
if (slowEffectActive) {
slowEffectActive = false;
slowEffectTimer = 0;
} //{6D_new}
// Activate speed boost
speedBoostActive = true;
speedBoostTimer = 300; // 5 seconds at 60 FPS
baseGameSpeed = gameSpeed;
gameSpeed = gameSpeed * 1.5; // 50% speed increase
// Show effect bar
effectBarVisible = true;
effectBarFill.tint = 0x00FFFF; // Blue for speed boost
tween(effectBar, {
alpha: 1
}, {
duration: 200
});
// Visual feedback for speed boost
tween(playerCar, {
tint: 0x00FFFF
}, {
duration: 300
});
potion.destroy();
speedPotions.splice(sp, 1);
continue;
}
potion.lastY = potion.y;
potion.lastCollected = currentPotionCollected;
}
// Update and check slow potions
for (var slp = slowPotions.length - 1; slp >= 0; slp--) {
var slowPotion = slowPotions[slp];
if (slowPotion.lastY === undefined) {
slowPotion.lastY = slowPotion.y;
}
if (slowPotion.lastCollected === undefined) {
slowPotion.lastCollected = false;
}
// Remove off-screen potions
if (slowPotion.lastY < 2800 && slowPotion.y >= 2800) {
slowPotion.destroy();
slowPotions.splice(slp, 1);
continue;
}
// Check collection
var currentSlowPotionCollected = slowPotion.intersects(playerCar);
if (!slowPotion.lastCollected && currentSlowPotionCollected) {
// Deactivate speed effect if active
if (speedBoostActive) {
speedBoostActive = false;
speedBoostTimer = 0;
} //{74_new}
// Activate slow effect
slowEffectActive = true;
slowEffectTimer = 300; // 5 seconds at 60 FPS
baseGameSpeed = gameSpeed;
gameSpeed = gameSpeed * 0.5; // 50% speed decrease
// Show effect bar
effectBarVisible = true;
effectBarFill.tint = 0xFF6600; // Orange for slow effect
tween(effectBar, {
alpha: 1
}, {
duration: 200
});
// Visual feedback for slow effect
tween(playerCar, {
tint: 0x0000FF
}, {
duration: 300
});
slowPotion.destroy();
slowPotions.splice(slp, 1);
continue;
}
slowPotion.lastY = slowPotion.y;
slowPotion.lastCollected = currentSlowPotionCollected;
}
// Update and check bomb obstacles
for (var bo = bombObstacles.length - 1; bo >= 0; bo--) {
var bomb = bombObstacles[bo];
if (bomb.lastY === undefined) {
bomb.lastY = bomb.y;
}
if (bomb.lastCollected === undefined) {
bomb.lastCollected = false;
}
// Remove off-screen bombs
if (bomb.lastY < 2800 && bomb.y >= 2800) {
bomb.destroy();
bombObstacles.splice(bo, 1);
continue;
}
// Check collision with player
var currentBombCollected = bomb.intersects(playerCar);
if (!bomb.lastCollected && currentBombCollected) {
// Play bomb explosion sound immediately - continues even after game over
LK.getSound('bombExplosion').play();
// Create massive explosion effect with much larger scale
var explosion = new ExplosionEffect();
explosion.x = playerCar.x;
explosion.y = playerCar.y;
explosions.push(explosion);
game.addChild(explosion);
// Scale up the explosion to make it massive
explosion.scaleX = 3;
explosion.scaleY = 3;
// Create additional large explosion effects around the impact
for (var ex = 0; ex < 5; ex++) {
var additionalExplosion = new ExplosionEffect();
additionalExplosion.x = playerCar.x + (Math.random() - 0.5) * 400;
additionalExplosion.y = playerCar.y + (Math.random() - 0.5) * 400;
additionalExplosion.scaleX = 2 + Math.random();
additionalExplosion.scaleY = 2 + Math.random();
explosions.push(additionalExplosion);
game.addChild(additionalExplosion);
additionalExplosion.startExplosion();
}
explosion.startExplosion();
// Screen shake effect using tween
tween(game, {
x: game.x + 20
}, {
duration: 50,
onFinish: function onFinish() {
tween(game, {
x: game.x - 40
}, {
duration: 50,
onFinish: function onFinish() {
tween(game, {
x: game.x + 40
}, {
duration: 50,
onFinish: function onFinish() {
tween(game, {
x: game.x - 20
}, {
duration: 50
});
}
});
}
});
}
});
// Remove ALL hearts (set lives to 0)
lives = 0;
// Hide all hearts
for (var h = 0; h < hearts.length; h++) {
hearts[h].alpha = 0.3;
}
// Remove the bomb that caused collision
bomb.destroy();
bombObstacles.splice(bo, 1);
// Game over immediately
var finalScore = Math.floor(distanceScore) + coinScore;
LK.setScore(finalScore);
// Update high score if current score is better
if (finalScore > (storage.highScore || 0)) {
storage.highScore = finalScore;
}
// Create dramatic flash sequence for atomic bomb explosion - 50% more intense
LK.effects.flashScreen(0xffffff, 3450); // Intense white flash first (+50% duration)
LK.setTimeout(function () {
LK.effects.flashScreen(0xff0000, 5250); // Red explosion flash (+50% duration)
}, 50); // Faster transition for intensity
LK.setTimeout(function () {
LK.effects.flashScreen(0xffffff, 2000); // Additional white flash for intensity
}, 150);
LK.setTimeout(function () {
LK.effects.flashScreen(0xff4500, 4500); // Orange explosion flash (+50% duration)
}, 300); // Faster transition
LK.setTimeout(function () {
LK.effects.flashScreen(0xff0000, 2500); // Additional red flash
}, 600);
LK.setTimeout(function () {
LK.effects.flashScreen(0xffff00, 4200); // Yellow afterglow (+50% duration)
}, 900);
LK.setTimeout(function () {
LK.effects.flashScreen(0xff4500, 2000); // Final orange flash for intensity
}, 1200);
LK.showGameOver();
return;
}
bomb.lastY = bomb.y;
bomb.lastCollected = currentBombCollected;
}
// Update and check extra hearts
for (var eh = extraHearts.length - 1; eh >= 0; eh--) {
var extraHeart = extraHearts[eh];
if (extraHeart.lastY === undefined) {
extraHeart.lastY = extraHeart.y;
}
if (extraHeart.lastCollected === undefined) {
extraHeart.lastCollected = false;
}
// Remove off-screen hearts
if (extraHeart.lastY < 2800 && extraHeart.y >= 2800) {
extraHeart.destroy();
extraHearts.splice(eh, 1);
continue;
}
// Check collection
var currentHeartCollected = extraHeart.intersects(playerCar);
if (!extraHeart.lastCollected && currentHeartCollected) {
// Increase lives by 1
lives += 1;
// Update heart display - show hearts based on current lives
for (var h = 0; h < hearts.length; h++) {
if (h < lives) {
hearts[h].alpha = 1.0; // Show heart if player has this life
} else {
hearts[h].alpha = 0.3; // Hide heart if player doesn't have this life
}
}
// Flash all hearts to show extra life gained
for (var h = 0; h < hearts.length; h++) {
(function (heart) {
tween(heart, {
tint: 0x00FF00
}, {
duration: 300,
onFinish: function onFinish() {
tween(heart, {
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
})(hearts[h]);
}
// Visual feedback for gaining extra life
LK.effects.flashScreen(0x00FF00, 500);
tween(playerCar, {
tint: 0x00FF00
}, {
duration: 500,
onFinish: function onFinish() {
tween(playerCar, {
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
extraHeart.destroy();
extraHearts.splice(eh, 1);
continue;
}
extraHeart.lastY = extraHeart.y;
extraHeart.lastCollected = currentHeartCollected;
}
// Update and remove road lines
for (var k = roadLines.length - 1; k >= 0; k--) {
var roadLine = roadLines[k];
if (roadLine.lastY === undefined) {
roadLine.lastY = roadLine.y;
}
if (roadLine.lastY < 2800 && roadLine.y >= 2800) {
roadLine.destroy();
roadLines.splice(k, 1);
continue;
}
roadLine.lastY = roadLine.y;
}
// Update and remove lane dividers
for (var d = laneDividers.length - 1; d >= 0; d--) {
var divider = laneDividers[d];
if (divider.lastY === undefined) {
divider.lastY = divider.y;
}
divider.y += divider.speed;
if (divider.lastY < 2800 && divider.y >= 2800) {
divider.destroy();
laneDividers.splice(d, 1);
continue;
}
divider.lastY = divider.y;
}
// Update and remove center lines
for (var c = centerLines.length - 1; c >= 0; c--) {
var centerLine = centerLines[c];
if (centerLine.lastY === undefined) {
centerLine.lastY = centerLine.y;
}
if (centerLine.lastY < 2800 && centerLine.y >= 2800) {
centerLine.destroy();
centerLines.splice(c, 1);
continue;
}
centerLine.lastY = centerLine.y;
}
// Check win condition
var totalScore = Math.floor(distanceScore) + coinScore;
if (totalScore >= 10000) {
// Ensure final score is set correctly before showing win
LK.setScore(totalScore);
// Update high score if current score is better
if (totalScore > (storage.highScore || 0)) {
storage.highScore = totalScore;
}
LK.showYouWin();
return;
}
// Update UI
scoreText.setText('Score: ' + totalScore);
var remainingDistance = Math.max(0, 10000 - totalScore);
remainingText.setText('Remaining: ' + remainingDistance + 'm');
coinCountText.setText('Coins: ' + coinCount);
highScoreText.setText('High Score: ' + (storage.highScore || 0));
// Scroll forest backgrounds for realistic movement
for (var bg = 0; bg < forestBackgrounds.length; bg++) {
forestBackgrounds[bg].y += gameSpeed;
// Reset position when tile moves off screen
if (forestBackgrounds[bg].y > 2932) {
forestBackgrounds[bg].y = forestBackgrounds[bg].y - 4 * 400;
}
}
// Scroll trees for realistic movement
for (var t = 0; t < trees.length; t++) {
trees[t].y += gameSpeed;
// Reset position when tree moves off screen
if (trees[t].y > 2932) {
trees[t].y = trees[t].y - 8 * 350;
}
}
// Update speeds for all moving objects
for (var l = 0; l < obstacles.length; l++) {
obstacles[l].speed = gameSpeed;
}
for (var m = 0; m < coins.length; m++) {
coins[m].speed = gameSpeed;
}
for (var n = 0; n < roadLines.length; n++) {
roadLines[n].speed = gameSpeed;
}
for (var d = 0; d < laneDividers.length; d++) {
laneDividers[d].speed = gameSpeed;
}
for (var cl = 0; cl < centerLines.length; cl++) {
centerLines[cl].speed = gameSpeed;
}
for (var sp = 0; sp < speedPotions.length; sp++) {
speedPotions[sp].speed = gameSpeed;
}
for (var slp = 0; slp < slowPotions.length; slp++) {
slowPotions[slp].speed = gameSpeed;
}
for (var bo = 0; bo < bombObstacles.length; bo++) {
bombObstacles[bo].speed = gameSpeed;
}
for (var eh = 0; eh < extraHearts.length; eh++) {
extraHearts[eh].speed = gameSpeed;
}
// Update effect bar
if (effectBarVisible) {
var currentTimer = 0;
var maxTimer = 300;
if (speedBoostActive) {
currentTimer = speedBoostTimer;
} else if (slowEffectActive) {
currentTimer = slowEffectTimer;
}
// Update bar width based on remaining time
var fillWidth = currentTimer / maxTimer * 348;
effectBarFill.scaleX = Math.max(0, fillWidth / 348);
}
// Update speed counter
speedCounterText.setText('Speed: ' + Math.floor(gameSpeed));
};
Ford mustang. High contrast. No shadows
Gold coins. In-Game asset. 2d. High contrast. No shadows
Minecraf hardcore heart. In-Game asset. 2d. High contrast. No shadows
Arkadan fotoğraflı araba. In-Game asset. 2d. High contrast. No shadows
İksir. In-Game asset. 2d. High contrast. No shadows
Atom bomb. In-Game asset. 2d. High contrast. No shadows