/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BlurBackground = Container.expand(function (options) {
var self = Container.call(this);
self.graphics = new Graphics();
self.addChild(self.graphics);
self.options = options || {};
self.options.blur = self.options.blur || 10;
self.options.width = self.options.width || 2048;
self.options.height = self.options.height || 2732;
self.updateGraphics = function () {
self.graphics.clear();
self.graphics.beginFill(0x0a0a0a, 0.95); // Very dark background for modern look
self.graphics.drawRect(0, 0, self.options.width, self.options.height);
self.graphics.endFill();
};
self.applyBlur = function () {
var blurFilter = new filters.BlurFilter();
blurFilter.blur = self.options.blur;
// Filters not supported in LK engine - commenting out assignment
// self.filters = [blurFilter];
};
self.updateGraphics();
self.applyBlur();
return self;
});
var Car = Container.expand(function () {
var self = Container.call(this);
self.projectMovement = function (vector) {
var angle = -Math.PI / 4;
var cosAngle = Math.cos(angle);
var sinAngle = Math.sin(angle);
return {
x: vector.x * cosAngle - vector.y * sinAngle,
y: vector.x * sinAngle + vector.y * cosAngle
};
};
var carGraphics = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
// Create headlight beam effect (right beam)
self.headlightBeam = self.attachAsset('beamR', {
anchorX: 0.5,
anchorY: 1.0,
width: 1400,
height: 1200,
alpha: 0.3,
tint: 0xFFFF88
});
self.headlightBeam.x = 753;
self.headlightBeam.y = -50;
// Create second headlight beam next to the first one
self.headlightBeam2 = self.attachAsset('beamR', {
anchorX: 0.5,
anchorY: 1.0,
width: 1400,
height: 1200,
alpha: 0.3,
tint: 0xFFFF88
});
self.headlightBeam2.x = 838;
self.headlightBeam2.y = -15;
// Create left headlight beam effect
self.leftBeam = self.attachAsset('beamL', {
anchorX: 0.5,
anchorY: 1.0,
width: 1400,
height: 1200,
alpha: 0.3,
tint: 0xFFFF88
});
self.leftBeam.x = -753;
self.leftBeam.y = -50;
self.leftBeam.visible = false;
// Create second left headlight beam next to the first one
self.leftBeam2 = self.attachAsset('beamL', {
anchorX: 0.5,
anchorY: 1.0,
width: 1400,
height: 1200,
alpha: 0.3,
tint: 0xFFFF88
});
self.leftBeam2.x = -838;
self.leftBeam2.y = -15;
self.leftBeam2.visible = false;
self.speed = 5;
self.direction = 0;
self.momentum = {
x: 0,
y: 0
};
self._move_migrated = function () {
var momentumModifier = 0.1;
if (self.direction === 0) {
self.momentum.x += self.speed * momentumModifier;
} else {
self.momentum.y -= self.speed * momentumModifier;
}
var projectedMovement = self.projectMovement(self.momentum);
// Check if car reaches the center of the screen on the X coordinate
if (self.lastX <= 2048 / 2 && self.x > 2048 / 2) {
console.log("Car reached the center of the screen on the X coordinate");
}
self.lastX = self.x;
self.x += projectedMovement.x;
self.y += projectedMovement.y;
// Check if car arrives at a specific X, Y coordinate
if (self.lastY <= 1500 && self.y > 1500 && self.lastX <= 1000 && self.x > 1000) {
console.log("Car arrived at the specific X, Y coordinate");
}
self.lastY = self.y;
self.lastX = self.x;
// Check if car comes close to the bottom of the screen on the Y coordinate
if (self.lastY <= 2732 - 100 && self.y > 2732 - 100) {
console.log("Car is close to the bottom of the screen");
}
self.lastY = self.y;
var nonTravelMomentum;
if (self.direction === 0) {
self.momentum.x *= 0.98;
self.momentum.y *= 0.95;
nonTravelMomentum = self.momentum.y;
} else {
self.momentum.x *= 0.95;
self.momentum.y *= 0.98;
nonTravelMomentum = self.momentum.x;
}
self.nonTravelMomentum = nonTravelMomentum;
// Show beamR only when car is turned to the right (direction === 0)
var shouldShowRightBeam = self.direction === 0;
self.headlightBeam.visible = shouldShowRightBeam;
self.headlightBeam2.visible = shouldShowRightBeam;
// Show beamL only when car is turned to the left (direction === 1)
var shouldShowLeftBeam = self.direction === 1;
if (self.leftBeam) {
self.leftBeam.visible = shouldShowLeftBeam;
}
if (self.leftBeam2) {
self.leftBeam2.visible = shouldShowLeftBeam;
}
if (shouldShowRightBeam) {
// Update headlight beam intensity based on speed
var totalSpeed = Math.sqrt(self.momentum.x * self.momentum.x + self.momentum.y * self.momentum.y);
var beamIntensity = Math.min(0.6, Math.max(0.2, totalSpeed * 0.05));
// Animate headlight beam flickering
tween(self.headlightBeam, {
alpha: beamIntensity
}, {
duration: 100 + Math.random() * 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Create slight flickering effect
var flickerAlpha = beamIntensity + (Math.random() - 0.5) * 0.1;
tween(self.headlightBeam, {
alpha: Math.max(0.1, flickerAlpha)
}, {
duration: 50,
easing: tween.linear
});
}
});
// Animate second beam with slight variation
tween(self.headlightBeam2, {
alpha: beamIntensity
}, {
duration: 120 + Math.random() * 180,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Create slight flickering effect
var flickerAlpha = beamIntensity + (Math.random() - 0.5) * 0.1;
tween(self.headlightBeam2, {
alpha: Math.max(0.1, flickerAlpha)
}, {
duration: 60,
easing: tween.linear
});
}
});
}
if (shouldShowLeftBeam && self.leftBeam) {
// Update left beam intensity based on speed
var totalSpeed = Math.sqrt(self.momentum.x * self.momentum.x + self.momentum.y * self.momentum.y);
var beamIntensity = Math.min(0.6, Math.max(0.2, totalSpeed * 0.05));
// Animate left beam flickering
tween(self.leftBeam, {
alpha: beamIntensity
}, {
duration: 100 + Math.random() * 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Create slight flickering effect
var flickerAlpha = beamIntensity + (Math.random() - 0.5) * 0.1;
tween(self.leftBeam, {
alpha: Math.max(0.1, flickerAlpha)
}, {
duration: 50,
easing: tween.linear
});
}
});
// Animate second left beam with slight variation
if (self.leftBeam2) {
tween(self.leftBeam2, {
alpha: beamIntensity
}, {
duration: 120 + Math.random() * 180,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Create slight flickering effect
var flickerAlpha = beamIntensity + (Math.random() - 0.5) * 0.1;
tween(self.leftBeam2, {
alpha: Math.max(0.1, flickerAlpha)
}, {
duration: 60,
easing: tween.linear
});
}
});
}
}
// Adjust beam width based on direction and momentum
var beamWidth = 1350 + Math.abs(self.nonTravelMomentum) * 20;
tween(self.headlightBeam, {
width: beamWidth
}, {
duration: 200,
easing: tween.easeOut
});
// Update second beam width as well
tween(self.headlightBeam2, {
width: beamWidth
}, {
duration: 200,
easing: tween.easeOut
});
if (self.leftBeam) {
tween(self.leftBeam, {
width: beamWidth
}, {
duration: 200,
easing: tween.easeOut
});
}
if (self.leftBeam2) {
tween(self.leftBeam2, {
width: beamWidth
}, {
duration: 200,
easing: tween.easeOut
});
}
};
self.changeDirection = function () {
self.direction = self.direction === 0 ? 1 : 0;
carGraphics.scale.x *= -1;
// Synchronize beam rotation with car direction
self.headlightBeam.scale.x *= -1;
self.headlightBeam2.scale.x *= -1;
if (self.leftBeam) {
self.leftBeam.scale.x *= -1;
}
if (self.leftBeam2) {
self.leftBeam2.scale.x *= -1;
}
};
});
var DriftAndDodge = Container.expand(function () {
var self = Container.call(this);
});
var Dust = Container.expand(function () {
var self = Container.call(this);
var dustGraphics = self.attachAsset('dust', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifetime = 60;
self.velocityY = -2 - Math.random() * 3;
self.velocityX = (Math.random() - 0.5) * 2;
self.fadeSpeed = 0.02;
self.scaleSpeed = 0.02;
self.update = function () {
self.y += self.velocityY;
self.x += self.velocityX;
self.alpha -= self.fadeSpeed;
self.scaleX += self.scaleSpeed;
self.scaleY += self.scaleSpeed;
if (self.alpha <= 0 || --self.lifetime <= 0) {
self.destroy();
}
};
return self;
});
var FogOverlay = Container.expand(function () {
var self = Container.call(this);
var fogGraphics = self.attachAsset('fogOverlay', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
// Position fog at screen center for fullscreen effect
fogGraphics.x = 0;
fogGraphics.y = 0;
self.isActive = false;
self.startFog = function () {
self.isActive = true;
// Fade in fog effect
tween(fogGraphics, {
alpha: 0.4
}, {
duration: 2000,
easing: tween.easeInOut
});
// Start continuous fog animation
self.animateFog();
};
self.stopFog = function () {
self.isActive = false;
// Fade out fog effect
tween(fogGraphics, {
alpha: 0
}, {
duration: 2000,
easing: tween.easeInOut
});
};
self.animateFog = function () {
if (!self.isActive) return;
// Create subtle fog movement
tween(fogGraphics, {
alpha: 0.3 + Math.random() * 0.2
}, {
duration: 3000 + Math.random() * 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (self.isActive) {
self.animateFog();
}
}
});
};
return self;
});
// Add the game logic for 'DriftAndDodge' here
var Graphics = Container.expand(function () {
var self = Container.call(this);
self.clear = function () {};
self.beginFill = function (color, alpha) {
self._fillColor = color;
self._fillAlpha = alpha;
};
self.drawRect = function (x, y, width, height) {
var rect = LK.getAsset('beamR', {
// Using 'beamR' asset for rectangle drawing
x: x,
y: y,
width: width,
height: height,
tint: self._fillColor,
alpha: self._fillAlpha
});
self.addChild(rect);
};
self.endFill = function () {};
return self;
});
var Lightning = Container.expand(function () {
var self = Container.call(this);
var lightningGraphics = self.attachAsset('lightning', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
// Position lightning at screen center for fullscreen effect
lightningGraphics.x = 0;
lightningGraphics.y = 0;
self.flashDuration = 150 + Math.random() * 200;
self.flickerCount = 2 + Math.floor(Math.random() * 3);
self.currentFlicker = 0;
self.isActive = false;
self.startLightning = function () {
self.isActive = true;
self.currentFlicker = 0;
self.doFlash();
};
self.doFlash = function () {
if (self.currentFlicker >= self.flickerCount) {
self.endLightning();
return;
}
// Flash to full brightness
tween(lightningGraphics, {
alpha: 0.15 + Math.random() * 0.1
}, {
duration: 50 + Math.random() * 100,
easing: tween.easeOut,
onFinish: function onFinish() {
// Quick fade out
tween(lightningGraphics, {
alpha: 0
}, {
duration: 30 + Math.random() * 70,
easing: tween.easeIn,
onFinish: function onFinish() {
self.currentFlicker++;
if (self.currentFlicker < self.flickerCount) {
// Brief pause before next flicker
LK.setTimeout(function () {
if (self.isActive) {
self.doFlash();
}
}, 100 + Math.random() * 200);
} else {
self.endLightning();
}
}
});
}
});
};
self.endLightning = function () {
self.isActive = false;
lightningGraphics.alpha = 0;
};
return self;
});
var Particle = Container.expand(function () {
var self = Container.call(this);
var particleGraphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
particleGraphics.rotation = Math.PI / 4;
self.lifetime = 100;
self.tick = function () {
if (--self.lifetime <= 0) {
self.destroy();
}
};
});
var RainDrop = Container.expand(function () {
var self = Container.call(this);
// Create raindrop using particle asset with blue tint
var dropGraphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.05,
scaleY: 0.8,
tint: 0x87CEEB,
alpha: 0.6
});
// Rain properties - enhanced for full level coverage
self.fallSpeed = 10 + Math.random() * 6;
self.windDrift = (Math.random() - 0.5) * 3;
self.lifetime = 400 + Math.random() * 300; // Longer lifetime for better coverage
self.maxLifetime = self.lifetime;
// Add slight rotation for realism
dropGraphics.rotation = Math.random() * 0.2 - 0.1;
// Removed tween animations for better performance
self.update = function () {
// Fall down with wind drift
self.y += self.fallSpeed;
self.x += self.windDrift;
// Fade out as lifetime decreases
var fadeRatio = self.lifetime / self.maxLifetime;
self.alpha = Math.max(0, fadeRatio * 0.6);
// Destroy when lifetime expires or when reaching bottom of screen
if (--self.lifetime <= 0 || self.y > 2732 + 100) {
self.destroy();
}
};
return self;
});
var Rock = Container.expand(function () {
var self = Container.call(this);
var rockGraphics = self.attachAsset('rock', {
anchorX: 0.5,
anchorY: 0.5
});
});
var SnowyEmbankment = Container.expand(function () {
var self = Container.call(this);
var embankmentGraphics = self.attachAsset('rods', {
anchorX: 0.5,
anchorY: 0.5
});
});
var Tree = Container.expand(function () {
var self = Container.call(this);
var treeGraphics = self.attachAsset('tree', {
anchorX: 0.5,
anchorY: 0.5
});
});
var Tree2 = Container.expand(function () {
var self = Container.call(this);
var treeGraphics = self.attachAsset('tree2', {
anchorX: 0.5,
anchorY: 0.5
});
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xFFFFFF
});
/****
* Game Code
****/
var filters = {
BlurFilter: function BlurFilter() {
this.blur = 0;
}
};
var driftAndDodge = game.addChild(new DriftAndDodge());
driftAndDodge.on('down', function (x, y, obj) {
// Add the event handler for 'down' event here
});
driftAndDodge.on('up', function (x, y, obj) {
// Add the event handler for 'up' event here
});
driftAndDodge.on('move', function (x, y, obj) {
// Add the event handler for 'move' event here
});
game.calculateDistanceToPoint = function (point, segmentStart, segmentEnd) {
var A = point.x - segmentStart.x;
var B = point.y - segmentStart.y;
var C = segmentEnd.x - segmentStart.x;
var D = segmentEnd.y - segmentStart.y;
var dot = A * C + B * D;
var len_sq = C * C + D * D;
var param = -1;
if (len_sq != 0) {
param = dot / len_sq;
}
var xx, yy;
if (param < 0) {
xx = segmentStart.x;
yy = segmentStart.y;
} else if (param > 1) {
xx = segmentEnd.x;
yy = segmentEnd.y;
} else {
xx = segmentStart.x + param * C;
yy = segmentStart.y + param * D;
}
var dx = point.x - xx;
var dy = point.y - yy;
return Math.sqrt(dx * dx + dy * dy);
};
game.addRoadSegment = function () {
var lastSegment = roadSegments[roadSegments.length - 1];
zigzag = !zigzag;
var segment = roadContainer.attachAsset('roadSegment', {
anchorX: 0.5
});
// Adjust segment width based on difficulty
var difficultyMultiplier = 1.0;
if (currentDifficulty === 'Normal') {
difficultyMultiplier = 0.85; // Make track 15% thinner
} else if (currentDifficulty === 'Hard') {
difficultyMultiplier = 0.5; // Make track 50% thinner for extreme difficulty
}
segment.width = segmentWidth * difficultyMultiplier;
// More controlled width reduction with minimum thresholds to prevent roads becoming too thin
var widthReduction = currentDifficulty === 'Hard' ? 8 : 5;
var minimumWidth = currentDifficulty === 'Hard' ? 600 : 800;
segmentWidth = Math.max(minimumWidth * difficultyMultiplier, segmentWidth - widthReduction);
segment.height = (i === 1 ? 3000 : Math.floor(Math.random() * (4000 - 1200 + 1)) + 1200) * 2;
segment.rotation = zigzag ? -Math.PI - Math.PI / 4 : -Math.PI + Math.PI / 4;
segment.y = currentY;
segment.x = currentX;
var adjustedHeight = segment.height - segmentWidth / 2;
currentY += adjustedHeight * Math.cos(segment.rotation);
currentX -= adjustedHeight * Math.sin(segment.rotation);
segment.shadow = roadContainer.attachAsset('roadSegmentShadow', {
anchorX: 0.5
});
segment.shadow.width = segment.width;
segment.shadow.height = segment.height;
segment.shadow.rotation = segment.rotation;
segment.shadow.x = segment.x;
segment.shadow.y = segment.y + 50;
segment.shadow.alpha = 1;
segment.used = false;
roadSegments.push(segment);
roadContainer.addChildAt(segment.shadow, 0);
roadContainer.addChild(segment);
// Add multiple trees to the left and right of the road segment
var treeSpacing = 300; // Increase space between trees
var numberOfTrees = Math.floor(segment.height / treeSpacing) - 3; // Decrease the number of trees by 3
var numberOfTree2 = Math.floor(segment.height / treeSpacing) - 3; // Decrease the number of tree2 by 3
for (var i = 0; i < numberOfTree2; i++) {
var leftTree = new Tree();
// Adjust tree distance based on difficulty - increase distance in hard mode
var treeDistance = 100;
if (currentDifficulty === 'Hard') {
treeDistance = 800; // Much further from road in hard mode to prevent touching
} else if (currentDifficulty === 'Normal') {
treeDistance = 200; // Moderately further in normal mode
}
leftTree.x = segment.x - segment.width - treeDistance - i * treeSpacing;
leftTree.y = segment.y + i * treeSpacing;
roadContainer.addChild(leftTree);
var rightTree = new Tree2();
rightTree.x = segment.x + segment.width + treeDistance + i * treeSpacing;
rightTree.y = segment.y + i * treeSpacing;
roadContainer.addChild(rightTree);
}
// Add rocks to the left and right of the road segment
var leftRock = new Rock();
leftRock.x = segment.x - segment.width - 1000; // Double the distance from the road
leftRock.y = segment.y;
roadContainer.addChildAt(leftRock, 0);
roadContainer.addChildAt(rightRock, 0);
var rightRock = new Rock();
rightRock.x = segment.x + segment.width + 1000; // Double the distance from the road
rightRock.y = segment.y;
// Attach snowy embankments to the left and right of the road segment
var leftEmbankment = new SnowyEmbankment();
leftEmbankment.x = segment.x - segment.width / 2 - 50; // Increase distance by 50 units
leftEmbankment.y = segment.y;
roadContainer.addChild(leftEmbankment);
var rightEmbankment = new SnowyEmbankment();
rightEmbankment.x = segment.x + segment.width / 2 + 50; // Increase distance by 50 units
rightEmbankment.y = segment.y;
roadContainer.addChild(rightEmbankment);
};
game.setBackgroundColor(0x000000); // Set background to black
var particles = [];
var dustParticles = [];
var rainDrops = [];
var rainIntensity = 3; // Number of raindrops to spawn per tick
var lightningContainer = null;
var lightningTimer = 0;
var nextLightningTime = 0;
var fogOverlay = null;
var mainContainer = game.addChild(new Container());
var roadContainer = mainContainer.addChild(new Container());
var roadSegments = [];
var segmentLength = Math.floor(Math.random() * (1000 - 200 + 1)) + 200;
var segmentWidth = 1200;
var currentX = 2048 / 2;
var currentY = 2732 / 2;
var zigzag = true;
for (var i = 1; i <= 15; i++) {
game.addRoadSegment();
}
var turnText = new Text2('Turn:', {
size: 50,
fill: 0xFFFFFF,
weight: '800',
dropShadow: true,
dropShadowColor: '#373330',
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 6,
dropShadowDistance: 6
});
turnText.anchor.set(1, 0); // Anchor to right edge so it aligns nicely with score
turnText.visible = false; // Hide turn text initially
LK.gui.topRight.addChild(turnText);
var scoreText = new Text2('0', {
size: 50,
fill: 0xFFFFFF,
weight: '800',
dropShadow: true,
dropShadowColor: '#373330',
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 6,
dropShadowDistance: 6
});
scoreText.anchor.set(0, 0);
scoreText.visible = false; // Hide score text initially
LK.gui.topRight.addChild(scoreText);
// Position turn text to the left of score text
turnText.x = scoreText.x - 107;
turnText.y = scoreText.y;
// Move score text left by 69 units
scoreText.x = scoreText.x - 69;
// Add a neon blue-colored speedometer to the bottom left corner of the map
var isMenuShowing = true;
var speedometer = new Text2('Speed: 0', {
size: 50,
fill: 0xFFD700,
// Gold color
weight: '800',
dropShadow: true,
dropShadowColor: '#000000',
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 6,
dropShadowDistance: 6
});
speedometer.anchor.set(0, 1); // Anchor to bottom left
speedometer.visible = false; // Hide speedometer initially
LK.gui.bottomLeft.addChild(speedometer);
var menuContainer = LK.gui.addChild(new Container());
var wallpaper = menuContainer.attachAsset('MW1', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 300,
y: 2732 / 2 - 400
});
var wallpaper2 = menuContainer.attachAsset('MW2', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 300,
y: 2732 / 2 - 400,
alpha: 0
});
var wallpaper3 = menuContainer.attachAsset('MW3', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 300,
y: 2732 / 2 - 400,
alpha: 0
});
var menuBackground = menuContainer.addChild(new BlurBackground({
width: 2048,
height: 2732,
blur: 25
}));
// Initialize slideshow variables
var currentWallpaper = 0; // 0 for MW1, 1 for MW2, 2 for MW3
var slideshowTimer = 0;
var slideshowInterval = 4000; // 4 seconds between transitions
var transitionDuration = 2000; // 2 seconds for each transition
// Start the slideshow gradient transition
function startSlideshowTransition() {
if (currentWallpaper === 0) {
// Fade from MW1 to MW2
tween(wallpaper, {
alpha: 0
}, {
duration: transitionDuration,
easing: tween.easeInOut
});
tween(wallpaper2, {
alpha: 1
}, {
duration: transitionDuration,
easing: tween.easeInOut,
onFinish: function onFinish() {
currentWallpaper = 1;
slideshowTimer = 0;
}
});
} else if (currentWallpaper === 1) {
// Fade from MW2 to MW3
tween(wallpaper2, {
alpha: 0
}, {
duration: transitionDuration,
easing: tween.easeInOut
});
tween(wallpaper3, {
alpha: 1
}, {
duration: transitionDuration,
easing: tween.easeInOut,
onFinish: function onFinish() {
currentWallpaper = 2;
slideshowTimer = 0;
}
});
} else {
// Fade from MW3 to MW1
tween(wallpaper3, {
alpha: 0
}, {
duration: transitionDuration,
easing: tween.easeInOut
});
tween(wallpaper, {
alpha: 1
}, {
duration: transitionDuration,
easing: tween.easeInOut,
onFinish: function onFinish() {
currentWallpaper = 0;
slideshowTimer = 0;
}
});
}
}
// Effects removed from menu
LK.playMusic('menuMusic', {
loop: true
});
var difficultyButtonContainer = new Container();
difficultyButtonContainer.x = 200;
difficultyButtonContainer.y = 2732 / 2 + 377;
var difficultyButtonAsset = difficultyButtonContainer.attachAsset('startbutton', {
anchorX: 0.5,
anchorY: 0.5
});
var difficultyText = new Text2('Difficulty', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
difficultyText.anchor.set(0.5, 0.5);
difficultyButtonContainer.addChild(difficultyText);
menuContainer.addChild(difficultyButtonContainer);
var weatherButtonContainer = new Container();
weatherButtonContainer.x = 600;
weatherButtonContainer.y = 2732 / 2 + 377;
var weatherButtonAsset = weatherButtonContainer.attachAsset('startbutton', {
anchorX: 0.5,
anchorY: 0.5
});
var weatherText = new Text2('Weather', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
weatherText.anchor.set(0.5, 0.5);
weatherButtonContainer.addChild(weatherText);
menuContainer.addChild(weatherButtonContainer);
// Difficulty level buttons removed
// Add underline graphics for selected difficulty
var startButtonContainer = new Container();
startButtonContainer.x = 2048 / 2 - 126 + 333;
startButtonContainer.y = 2732 / 2 + 380;
var startButtonAsset = startButtonContainer.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
width: 369,
height: 90
});
var startButton = new Text2('Start Game', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
startButton.anchor.set(0.5, 0.5);
startButtonContainer.addChild(startButton);
function flashStartButton() {
// No flashing animation for start button
}
// Initialize difficulty level text objects with button backgrounds
var easyButtonContainer = new Container();
var easyButtonAsset = easyButtonContainer.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
easyText = new Text2('EASY', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
easyText.anchor.set(0.5, 0.5);
easyButtonContainer.addChild(easyText);
easyButtonContainer.visible = false;
menuContainer.addChild(easyButtonContainer);
var normalButtonContainer = new Container();
var normalButtonAsset = normalButtonContainer.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
normalText = new Text2('NORMAL', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
normalText.anchor.set(0.5, 0.5);
normalButtonContainer.addChild(normalText);
normalButtonContainer.visible = false;
menuContainer.addChild(normalButtonContainer);
var hardButtonContainer = new Container();
var hardButtonAsset = hardButtonContainer.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
hardText = new Text2('HARD', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
hardText.anchor.set(0.5, 0.5);
hardButtonContainer.addChild(hardText);
hardButtonContainer.visible = false;
menuContainer.addChild(hardButtonContainer);
// Initialize weather level text objects with button backgrounds
var clearButtonContainer = new Container();
var clearButtonAsset = clearButtonContainer.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
clearText = new Text2('CLEAR', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
clearText.anchor.set(0.5, 0.5);
clearButtonContainer.addChild(clearText);
clearButtonContainer.visible = false;
menuContainer.addChild(clearButtonContainer);
var rainButtonContainer = new Container();
var rainButtonAsset = rainButtonContainer.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
rainText = new Text2('RAIN', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
rainText.anchor.set(0.5, 0.5);
rainButtonContainer.addChild(rainText);
rainButtonContainer.visible = false;
menuContainer.addChild(rainButtonContainer);
var stormButtonContainer = new Container();
var stormButtonAsset = stormButtonContainer.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
stormText = new Text2('STORM', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
stormText.anchor.set(0.5, 0.5);
stormButtonContainer.addChild(stormText);
stormButtonContainer.visible = false;
menuContainer.addChild(stormButtonContainer);
var fogButtonContainer = new Container();
var fogButtonAsset = fogButtonContainer.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
fogText = new Text2('FOG', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
fogText.anchor.set(0.5, 0.5);
fogButtonContainer.addChild(fogText);
fogButtonContainer.visible = false;
menuContainer.addChild(fogButtonContainer);
// Start the flashing animation
menuContainer.addChild(startButtonContainer);
difficultyButtonContainer.down = function (x, y, obj) {
// Add pulse feedback animation to difficulty button
tween(difficultyButtonContainer, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(difficultyButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Change difficulty level after pulse feedback
difficultyIndex = (difficultyIndex + 1) % difficultyLevels.length;
currentDifficulty = difficultyLevels[difficultyIndex];
difficultyText.setText('Difficulty');
// Position and show the difficulty level button containers
easyButtonContainer.x = difficultyButtonContainer.x;
easyButtonContainer.y = difficultyButtonContainer.y - easyButtonContainer.height - 10; // Above normal text
normalButtonContainer.x = difficultyButtonContainer.x;
normalButtonContainer.y = difficultyButtonContainer.y - normalButtonContainer.height - 10 - easyButtonContainer.height; // Above hard text
hardButtonContainer.x = difficultyButtonContainer.x;
hardButtonContainer.y = difficultyButtonContainer.y - hardButtonContainer.height - 10 - easyButtonContainer.height - normalButtonContainer.height; // Above difficulty button
hardButtonContainer.visible = true;
normalButtonContainer.visible = true;
easyButtonContainer.visible = true;
}
});
}
});
};
startButtonContainer.down = function (x, y, obj) {
// Check if difficulty has been selected before allowing start
if (!difficultySelected) {
// Show info text
if (!infoText) {
infoText = new Text2('Please choose a difficulty level!', {
size: 40,
fill: 0xFFFFFF,
align: 'center'
});
infoText.anchor.set(0.5, 0.5);
infoText.x = startButtonContainer.x - 512;
infoText.y = startButtonContainer.y - 80 + 180;
menuContainer.addChild(infoText);
}
infoText.visible = true;
// Hide info text after 3 seconds
LK.setTimeout(function () {
if (infoText) {
infoText.visible = false;
}
}, 3000);
// Pulse animation to indicate difficulty must be selected first
tween(startButtonContainer, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(startButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeOut
});
}
});
return; // Exit early if no difficulty is selected
}
// Add pulse feedback animation before loading track
tween(startButtonContainer, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(startButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Load track after pulse feedback completes
isMenuShowing = false;
menuContainer.visible = false;
mainContainer.visible = true;
// Show counters when game starts
scoreText.visible = true;
speedometer.visible = true;
turnText.visible = true;
timerText.visible = true;
// Initialize game start time
gameStartTime = Date.now();
// Properly load and activate the game world
// Initialize car's last position tracking for movement detection
car.lastX = car.x;
car.lastY = car.y;
car.lastWasIntersecting = false;
// Stop menu music and start the game music
LK.stopMusic();
LK.playMusic('Car', {
loop: true
});
// Reset game state
score = 0;
LK.setScore(score);
scoreText.setText(score.toString());
isGameOver = false;
// Initialize lightning system
lightningContainer = game.addChild(new Lightning());
// Position lightning at screen center for fullscreen effect
lightningContainer.x = 2048 / 2;
lightningContainer.y = 2732 / 2;
lightningTimer = 0;
nextLightningTime = 2000 + Math.random() * 5000; // First lightning in 2-7 seconds
// Initialize fog overlay system
fogOverlay = game.addChild(new FogOverlay());
// Position fog overlay at screen center for fullscreen effect
fogOverlay.x = 2048 / 2;
fogOverlay.y = 2732 / 2;
// Ensure all road segments are properly initialized
roadSegments.forEach(function (segment) {
segment.used = false;
});
}
});
}
});
};
var car = mainContainer.addChild(new Car());
car.x = 2048 / 2;
car.y = 2732 / 2;
// Hide main container when menu is showing
mainContainer.visible = false;
// Hide difficulty level button containers when menu is hidden
easyButtonContainer.visible = false;
normalButtonContainer.visible = false;
hardButtonContainer.visible = false;
// Hide weather option button containers when menu is hidden
clearButtonContainer.visible = false;
rainButtonContainer.visible = false;
stormButtonContainer.visible = false;
fogButtonContainer.visible = false;
// Add difficulty selector function to easy, normal, hard text
easyButtonContainer.down = function (x, y, obj) {
// Tint the button orange when touched
tween(easyButtonAsset, {
tint: 0xff8c00
}, {
duration: 150,
easing: tween.easeOut
});
// Add pulse feedback animation to selected mode button
tween(easyButtonContainer, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(easyButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
difficultyIndex = 0;
currentDifficulty = difficultyLevels[difficultyIndex];
difficultyText.setText('Difficulty');
// Hide difficulty level button containers
easyButtonContainer.visible = false;
normalButtonContainer.visible = false;
hardButtonContainer.visible = false;
difficultySelected = true; // Set flag to true when difficulty is selected
startButtonContainer.removeChild(startButtonAsset); // Remove old button asset
startButtonAsset = startButtonContainer.attachAsset('startbutton', {
// Add new startbutton asset
anchorX: 0.5,
anchorY: 0.5,
width: 369,
height: 100
});
startButtonContainer.addChildAt(startButtonAsset, 0); // Add it behind the text
}
});
}
});
};
normalButtonContainer.down = function (x, y, obj) {
// Tint the button orange when touched
tween(normalButtonAsset, {
tint: 0xff8c00
}, {
duration: 150,
easing: tween.easeOut
});
// Add pulse feedback animation to selected mode button
tween(normalButtonContainer, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(normalButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
difficultyIndex = 1;
currentDifficulty = difficultyLevels[difficultyIndex];
difficultyText.setText('Difficulty');
// Hide difficulty level button containers
easyButtonContainer.visible = false;
normalButtonContainer.visible = false;
hardButtonContainer.visible = false;
difficultySelected = true; // Set flag to true when difficulty is selected
startButtonContainer.removeChild(startButtonAsset); // Remove old button asset
startButtonAsset = startButtonContainer.attachAsset('startbutton', {
// Add new startbutton asset
anchorX: 0.5,
anchorY: 0.5,
width: 369,
height: 100
});
startButtonContainer.addChildAt(startButtonAsset, 0); // Add it behind the text
}
});
}
});
};
hardButtonContainer.down = function (x, y, obj) {
// Tint the button orange when touched
tween(hardButtonAsset, {
tint: 0xff8c00
}, {
duration: 150,
easing: tween.easeOut
});
// Add pulse feedback animation to selected mode button
tween(hardButtonContainer, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(hardButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
difficultyIndex = 2;
currentDifficulty = difficultyLevels[difficultyIndex];
difficultyText.setText('Difficulty');
// Hide difficulty level button containers
easyButtonContainer.visible = false;
normalButtonContainer.visible = false;
hardButtonContainer.visible = false;
difficultySelected = true; // Set flag to true when difficulty is selected
startButtonContainer.removeChild(startButtonAsset); // Remove old button asset
startButtonAsset = startButtonContainer.attachAsset('startbutton', {
// Add new startbutton asset
anchorX: 0.5,
anchorY: 0.5,
width: 369,
height: 100
});
startButtonContainer.addChildAt(startButtonAsset, 0); // Add it behind the text
}
});
}
});
};
weatherButtonContainer.down = function (x, y, obj) {
// Add pulse feedback animation to weather button
tween(weatherButtonContainer, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(weatherButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Show weather option buttons after pulse feedback
weatherText.setText('Weather');
// Position and show the weather option button containers
clearButtonContainer.x = weatherButtonContainer.x;
clearButtonContainer.y = weatherButtonContainer.y - clearButtonContainer.height - 10; // Above rain text
rainButtonContainer.x = weatherButtonContainer.x;
rainButtonContainer.y = weatherButtonContainer.y - rainButtonContainer.height - 10 - clearButtonContainer.height; // Above storm text
stormButtonContainer.x = weatherButtonContainer.x;
stormButtonContainer.y = weatherButtonContainer.y - stormButtonContainer.height - 10 - clearButtonContainer.height - rainButtonContainer.height; // Above fog text
fogButtonContainer.x = weatherButtonContainer.x;
fogButtonContainer.y = weatherButtonContainer.y - fogButtonContainer.height - 10 - clearButtonContainer.height - rainButtonContainer.height - stormButtonContainer.height; // Above weather button
fogButtonContainer.visible = true;
stormButtonContainer.visible = true;
rainButtonContainer.visible = true;
clearButtonContainer.visible = true;
}
});
}
});
};
var isGameOver = false;
var score = 0;
var closestSegment = null;
var gameStartTime = 0;
var currentDifficulty = 'Easy'; // Track current difficulty level
var difficultyLevels = ['Easy', 'Normal', 'Hard']; // Available difficulty levels
var difficultyIndex = 0; // Current difficulty index
var easyText;
var normalText;
var hardText;
var difficultySelected = false; // Flag to indicate if a difficulty has been selected
var infoText = null; // Info text for difficulty selection prompt
var currentWeather = 'Clear'; // Track current weather
var weatherTypes = ['Clear', 'Rain', 'Storm', 'Fog']; // Available weather types
var weatherIndex = 0; // Current weather index
var weatherSelected = false; // Flag to indicate if a weather has been selected
// Add weather selector function to clear, rain, storm text
clearButtonContainer.down = function (x, y, obj) {
// Tint the button orange when touched
tween(clearButtonAsset, {
tint: 0xff8c00
}, {
duration: 150,
easing: tween.easeOut
});
// Add pulse feedback animation to selected mode button
tween(clearButtonContainer, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(clearButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
weatherIndex = 0;
currentWeather = weatherTypes[weatherIndex];
weatherText.setText('Weather');
// Hide weather option button containers
clearButtonContainer.visible = false;
rainButtonContainer.visible = false;
stormButtonContainer.visible = false;
fogButtonContainer.visible = false;
weatherSelected = true; // Set flag to true when weather is selected
}
});
}
});
};
rainButtonContainer.down = function (x, y, obj) {
// Tint the button orange when touched
tween(rainButtonAsset, {
tint: 0xff8c00
}, {
duration: 150,
easing: tween.easeOut
});
// Add pulse feedback animation to selected mode button
tween(rainButtonContainer, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(rainButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
weatherIndex = 1;
currentWeather = weatherTypes[weatherIndex];
weatherText.setText('Weather');
// Hide weather option button containers
clearButtonContainer.visible = false;
rainButtonContainer.visible = false;
stormButtonContainer.visible = false;
fogButtonContainer.visible = false;
weatherSelected = true; // Set flag to true when weather is selected
}
});
}
});
};
stormButtonContainer.down = function (x, y, obj) {
// Tint the button orange when touched
tween(stormButtonAsset, {
tint: 0xff8c00
}, {
duration: 150,
easing: tween.easeOut
});
// Add pulse feedback animation to selected mode button
tween(stormButtonContainer, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(stormButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
weatherIndex = 2;
currentWeather = weatherTypes[weatherIndex];
weatherText.setText('Weather');
// Hide weather option button containers
clearButtonContainer.visible = false;
rainButtonContainer.visible = false;
stormButtonContainer.visible = false;
fogButtonContainer.visible = false;
weatherSelected = true; // Set flag to true when weather is selected
}
});
}
});
};
fogButtonContainer.down = function (x, y, obj) {
// Tint the button orange when touched
tween(fogButtonAsset, {
tint: 0xff8c00
}, {
duration: 150,
easing: tween.easeOut
});
// Add pulse feedback animation to selected mode button
tween(fogButtonContainer, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(fogButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
weatherIndex = 3;
currentWeather = weatherTypes[weatherIndex];
weatherText.setText('Weather');
// Hide weather option button containers
clearButtonContainer.visible = false;
rainButtonContainer.visible = false;
stormButtonContainer.visible = false;
fogButtonContainer.visible = false;
weatherSelected = true; // Set flag to true when weather is selected
}
});
}
});
};
var timerText = new Text2('00:00:00:000', {
size: 50,
fill: 0xFFFFFF,
weight: '800',
dropShadow: true,
dropShadowColor: '#373330',
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 6,
dropShadowDistance: 6
});
timerText.anchor.set(1, 1); // Anchor to bottom right
timerText.visible = false; // Hide timer initially
LK.gui.bottomRight.addChild(timerText);
game.down = function (x, y, obj) {
if (isMenuShowing) {
return;
}
car.changeDirection();
};
LK.on('tick', function () {
if (isMenuShowing) {
// Update slideshow timer when menu is showing
slideshowTimer += 16.67; // Approximate milliseconds per tick at 60fps
if (slideshowTimer >= slideshowInterval) {
startSlideshowTransition();
}
return;
}
car._move_migrated();
var carIsOnRoad = false;
var carPosition = {
x: car.x,
y: car.y
};
// Update the speedometer with the current speed
speedometer.setText('Speed: ' + Math.round(Math.sqrt(car.momentum.x * car.momentum.x + car.momentum.y * car.momentum.y) * 5) + ' km/h');
// Maintain consistent rain intensity throughout the level with gradual increase
var baseRainIntensity = Math.min(16, 6 + Math.floor(score / 10)); // Double the intensity - more gradual increase, higher max intensity
// Apply weather-based multiplier
if (currentWeather === 'Clear') {
rainIntensity = 0; // No rain in clear weather
} else if (currentWeather === 'Rain') {
rainIntensity = baseRainIntensity;
} else if (currentWeather === 'Storm') {
rainIntensity = baseRainIntensity * 2; // Double rain density for storm mode
// Handle lightning effects in storm mode
if (lightningContainer) {
lightningTimer += 16.67; // Approximate milliseconds per tick at 60fps
if (lightningTimer >= nextLightningTime && !lightningContainer.isActive) {
// Trigger lightning flash
lightningContainer.startLightning();
// Schedule next lightning strike (5-15 seconds)
lightningTimer = 0;
nextLightningTime = 4000 + Math.random() * 6000;
}
}
} else if (currentWeather === 'Fog') {
rainIntensity = 0; // No rain in fog weather
// Apply fog effect by reducing headlight beam intensity, but keep beam distance/placement as in other weather modes
if (car.headlightBeam) {
car.headlightBeam.alpha = Math.min(car.headlightBeam.alpha, 0.15);
}
if (car.headlightBeam2) {
car.headlightBeam2.alpha = Math.min(car.headlightBeam2.alpha, 0.15);
}
if (car.leftBeam) {
car.leftBeam.alpha = Math.min(car.leftBeam.alpha, 0.15);
}
if (car.leftBeam2) {
car.leftBeam2.alpha = Math.min(car.leftBeam2.alpha, 0.15);
}
// Activate fog overlay effect
if (fogOverlay && !fogOverlay.isActive) {
fogOverlay.startFog();
}
} else {
// Deactivate fog overlay for other weather modes
if (fogOverlay && fogOverlay.isActive) {
fogOverlay.stopFog();
}
}
// Update timer display
var currentTime = Date.now();
var elapsedTime = currentTime - gameStartTime;
var hours = Math.floor(elapsedTime / 3600000);
var minutes = Math.floor(elapsedTime % 3600000 / 60000);
var seconds = Math.floor(elapsedTime % 60000 / 1000);
var milliseconds = elapsedTime % 1000;
var formattedTime = (hours < 10 ? '0' + hours : hours) + ':' + (minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds < 10 ? '0' + seconds : seconds) + ':' + (milliseconds < 100 ? milliseconds < 10 ? '00' + milliseconds : '0' + milliseconds : milliseconds);
timerText.setText(formattedTime);
var currentClosestSegment = null;
var currentClosestDistance = Infinity;
roadSegments.forEach(function (segment) {
var segmentStart = {
x: segment.x + Math.sin(segment.rotation) * 100,
y: segment.y - Math.cos(segment.rotation) * 100
};
var segmentEnd = {
x: segment.x - Math.sin(segment.rotation) * (segment.height - segment.width / 2),
y: segment.y + Math.cos(segment.rotation) * (segment.height - segment.width / 2)
};
var distance = game.calculateDistanceToPoint(carPosition, segmentStart, segmentEnd);
if (distance < currentClosestDistance) {
currentClosestDistance = distance;
currentClosestSegment = segment;
}
if (distance < segment.width / 2 - 50) {
carIsOnRoad = true;
}
});
if (closestSegment !== currentClosestSegment && !currentClosestSegment.used) {
// Check for intersection between car and road segment
if (car.lastWasIntersecting === false && car.intersects(currentClosestSegment)) {
console.log("Car intersected with a road segment");
}
car.lastWasIntersecting = car.intersects(currentClosestSegment);
closestSegment = currentClosestSegment;
closestSegment.used = true;
score++;
LK.setScore(score);
scoreText.setText(score.toString());
}
if (!carIsOnRoad) {
LK.showGameOver();
} else {}
var particle = new Particle();
particle.alpha = Math.max(0, Math.min(1, Math.abs(car.nonTravelMomentum) / 5 - 0.5));
if (particle.alpha > 0) {
var noiseX = (Math.random() - 0.5) * 10;
var noiseY = (Math.random() - 0.5) * 10;
particle.x = car.x + noiseX;
particle.y = car.y + noiseY;
mainContainer.addChildAt(particle, 1);
particles.push(particle);
}
// Create brown dust particles under the car
if (LK.ticks % 3 === 0) {
var dust = new Dust();
dust.x = car.x + (Math.random() - 0.5) * 30;
dust.y = car.y + 40;
dust.alpha = 0.6 + Math.random() * 0.4;
dust.scaleX = 0.5 + Math.random() * 0.5;
dust.scaleY = 0.5 + Math.random() * 0.5;
// Animate dust with random movement
tween(dust, {
rotation: (Math.random() - 0.5) * Math.PI
}, {
duration: 1000 + Math.random() * 1000,
easing: tween.easeOut
});
mainContainer.addChildAt(dust, 1);
dustParticles.push(dust);
}
// Create additional side dust particles next to the car
if (LK.ticks % 2 === 0) {
// Left side dust - primary
var leftDust = new Dust();
leftDust.x = car.x - 60 + (Math.random() - 0.5) * 40;
leftDust.y = car.y + 20 + Math.random() * 30;
leftDust.alpha = 0.4 + Math.random() * 0.3;
leftDust.scaleX = 0.3 + Math.random() * 0.4;
leftDust.scaleY = 0.3 + Math.random() * 0.4;
leftDust.velocityX = -1 - Math.random() * 2;
// Animate left dust with drift movement
tween(leftDust, {
rotation: (Math.random() - 0.5) * Math.PI * 1.5,
scaleX: leftDust.scaleX + 0.3,
scaleY: leftDust.scaleY + 0.3
}, {
duration: 800 + Math.random() * 800,
easing: tween.easeOut
});
mainContainer.addChildAt(leftDust, 1);
dustParticles.push(leftDust);
// Right side dust - primary
var rightDust = new Dust();
rightDust.x = car.x + 60 + (Math.random() - 0.5) * 40;
rightDust.y = car.y + 20 + Math.random() * 30;
rightDust.alpha = 0.4 + Math.random() * 0.3;
rightDust.scaleX = 0.3 + Math.random() * 0.4;
rightDust.scaleY = 0.3 + Math.random() * 0.4;
rightDust.velocityX = 1 + Math.random() * 2;
// Animate right dust with drift movement
tween(rightDust, {
rotation: (Math.random() - 0.5) * Math.PI * 1.5,
scaleX: rightDust.scaleX + 0.3,
scaleY: rightDust.scaleY + 0.3
}, {
duration: 800 + Math.random() * 800,
easing: tween.easeOut
});
mainContainer.addChildAt(rightDust, 1);
dustParticles.push(rightDust);
}
// Create additional dense dust clouds on both sides
if (LK.ticks % 3 === 0) {
// Left side dense dust
var leftDenseDust = new Dust();
leftDenseDust.x = car.x - 80 + (Math.random() - 0.5) * 60;
leftDenseDust.y = car.y + 10 + Math.random() * 40;
leftDenseDust.alpha = 0.5 + Math.random() * 0.4;
leftDenseDust.scaleX = 0.6 + Math.random() * 0.6;
leftDenseDust.scaleY = 0.6 + Math.random() * 0.6;
leftDenseDust.velocityX = -2 - Math.random() * 3;
leftDenseDust.velocityY = -1 - Math.random() * 2;
// Animate left dense dust with swirl movement
tween(leftDenseDust, {
rotation: Math.PI + Math.random() * Math.PI,
scaleX: leftDenseDust.scaleX + 0.5,
scaleY: leftDenseDust.scaleY + 0.5,
alpha: 0.1
}, {
duration: 1200 + Math.random() * 1000,
easing: tween.easeOut
});
mainContainer.addChildAt(leftDenseDust, 1);
dustParticles.push(leftDenseDust);
// Right side dense dust
var rightDenseDust = new Dust();
rightDenseDust.x = car.x + 80 + (Math.random() - 0.5) * 60;
rightDenseDust.y = car.y + 10 + Math.random() * 40;
rightDenseDust.alpha = 0.5 + Math.random() * 0.4;
rightDenseDust.scaleX = 0.6 + Math.random() * 0.6;
rightDenseDust.scaleY = 0.6 + Math.random() * 0.6;
rightDenseDust.velocityX = 2 + Math.random() * 3;
rightDenseDust.velocityY = -1 - Math.random() * 2;
// Animate right dense dust with swirl movement
tween(rightDenseDust, {
rotation: -Math.PI - Math.random() * Math.PI,
scaleX: rightDenseDust.scaleX + 0.5,
scaleY: rightDenseDust.scaleY + 0.5,
alpha: 0.1
}, {
duration: 1200 + Math.random() * 1000,
easing: tween.easeOut
});
mainContainer.addChildAt(rightDenseDust, 1);
dustParticles.push(rightDenseDust);
}
// Create smaller wispy dust particles on both sides
if (LK.ticks % 5 === 0) {
// Left side wispy dust
var leftWispyDust = new Dust();
leftWispyDust.x = car.x - 40 + (Math.random() - 0.5) * 20;
leftWispyDust.y = car.y + 30 + Math.random() * 20;
leftWispyDust.alpha = 0.3 + Math.random() * 0.2;
leftWispyDust.scaleX = 0.2 + Math.random() * 0.3;
leftWispyDust.scaleY = 0.2 + Math.random() * 0.3;
leftWispyDust.velocityX = -0.5 - Math.random();
leftWispyDust.velocityY = -2 - Math.random() * 2;
// Animate left wispy dust with gentle float
tween(leftWispyDust, {
rotation: (Math.random() - 0.5) * Math.PI,
scaleX: leftWispyDust.scaleX + 0.2,
scaleY: leftWispyDust.scaleY + 0.2
}, {
duration: 600 + Math.random() * 600,
easing: tween.easeOut
});
mainContainer.addChildAt(leftWispyDust, 1);
dustParticles.push(leftWispyDust);
// Right side wispy dust
var rightWispyDust = new Dust();
rightWispyDust.x = car.x + 40 + (Math.random() - 0.5) * 20;
rightWispyDust.y = car.y + 30 + Math.random() * 20;
rightWispyDust.alpha = 0.3 + Math.random() * 0.2;
rightWispyDust.scaleX = 0.2 + Math.random() * 0.3;
rightWispyDust.scaleY = 0.2 + Math.random() * 0.3;
rightWispyDust.velocityX = 0.5 + Math.random();
rightWispyDust.velocityY = -2 - Math.random() * 2;
// Animate right wispy dust with gentle float
tween(rightWispyDust, {
rotation: (Math.random() - 0.5) * Math.PI,
scaleX: rightWispyDust.scaleX + 0.2,
scaleY: rightWispyDust.scaleY + 0.2
}, {
duration: 600 + Math.random() * 600,
easing: tween.easeOut
});
mainContainer.addChildAt(rightWispyDust, 1);
dustParticles.push(rightWispyDust);
}
particles.forEach(function (particle, index) {
particle.tick();
if (particle.lifetime <= 0) {
particles.splice(index, 1);
}
});
dustParticles.forEach(function (dust, index) {
dust.update();
if (dust.alpha <= 0 || dust.lifetime <= 0) {
dustParticles.splice(index, 1);
}
});
// Create rain drops continuously throughout the entire level
var rainSpawnRate = Math.max(2, Math.floor(8 / rainIntensity)); // Increased spawn rate for better rain visibility
if (LK.ticks % rainSpawnRate === 0) {
var spawnCount = Math.min(6, rainIntensity); // Increased max raindrops per spawn
for (var r = 0; r < spawnCount; r++) {
var rainDrop = new RainDrop();
// Calculate current camera offset to ensure rain covers visible area
var cameraOffsetX = -mainContainer.x;
var cameraOffsetY = -mainContainer.y;
// Spawn rain across much wider area accounting for camera movement
var spawnWidth = 4096; // Increased spawn width for better coverage
var spawnMargin = 768; // Increased margin
rainDrop.x = cameraOffsetX - spawnMargin + Math.random() * (spawnWidth + 2 * spawnMargin);
rainDrop.y = cameraOffsetY - 300 - Math.random() * 400;
// Add slight opacity variation for realism
rainDrop.alpha = 0.4 + Math.random() * 0.4;
// Remove tween animation to improve performance
mainContainer.addChild(rainDrop);
rainDrops.push(rainDrop);
}
}
// Update and clean up rain drops with performance optimization
for (var dropIndex = rainDrops.length - 1; dropIndex >= 0; dropIndex--) {
var drop = rainDrops[dropIndex];
drop.update();
if (drop.lifetime <= 0 || drop.alpha <= 0 || drop.y > 2732 + 200) {
drop.destroy();
rainDrops.splice(dropIndex, 1);
}
}
// Limit total number of raindrops for performance
if (rainDrops.length > 120) {
var excessDrops = rainDrops.length - 120;
for (var excess = 0; excess < excessDrops; excess++) {
var oldestDrop = rainDrops.shift();
if (oldestDrop) {
oldestDrop.destroy();
}
}
}
var carLocalPosition = game.toLocal(car.position, car.parent);
var offsetX = (2048 / 2 - carLocalPosition.x) / 20;
var offsetY = (2732 - 450 - carLocalPosition.y) / 20;
mainContainer.x += offsetX;
mainContainer.y += offsetY;
for (var i = roadSegments.length - 1; i >= 0; i--) {
var segmentGlobalPosition = game.toLocal(roadSegments[i].position, roadSegments[i].parent);
if (segmentGlobalPosition.y - roadSegments[i].height > 2732 * 2) {
roadSegments[i].shadow.destroy();
roadSegments[i].destroy();
roadSegments.splice(i, 1);
game.addRoadSegment();
}
// Destroy embankments which are off screen
if (roadSegments[i].leftEmbankment && roadSegments[i].leftEmbankment.y < -50) {
roadSegments[i].leftEmbankment.destroy();
}
if (roadSegments[i].rightEmbankment && roadSegments[i].rightEmbankment.y < -50) {
roadSegments[i].rightEmbankment.destroy();
}
}
}); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BlurBackground = Container.expand(function (options) {
var self = Container.call(this);
self.graphics = new Graphics();
self.addChild(self.graphics);
self.options = options || {};
self.options.blur = self.options.blur || 10;
self.options.width = self.options.width || 2048;
self.options.height = self.options.height || 2732;
self.updateGraphics = function () {
self.graphics.clear();
self.graphics.beginFill(0x0a0a0a, 0.95); // Very dark background for modern look
self.graphics.drawRect(0, 0, self.options.width, self.options.height);
self.graphics.endFill();
};
self.applyBlur = function () {
var blurFilter = new filters.BlurFilter();
blurFilter.blur = self.options.blur;
// Filters not supported in LK engine - commenting out assignment
// self.filters = [blurFilter];
};
self.updateGraphics();
self.applyBlur();
return self;
});
var Car = Container.expand(function () {
var self = Container.call(this);
self.projectMovement = function (vector) {
var angle = -Math.PI / 4;
var cosAngle = Math.cos(angle);
var sinAngle = Math.sin(angle);
return {
x: vector.x * cosAngle - vector.y * sinAngle,
y: vector.x * sinAngle + vector.y * cosAngle
};
};
var carGraphics = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
// Create headlight beam effect (right beam)
self.headlightBeam = self.attachAsset('beamR', {
anchorX: 0.5,
anchorY: 1.0,
width: 1400,
height: 1200,
alpha: 0.3,
tint: 0xFFFF88
});
self.headlightBeam.x = 753;
self.headlightBeam.y = -50;
// Create second headlight beam next to the first one
self.headlightBeam2 = self.attachAsset('beamR', {
anchorX: 0.5,
anchorY: 1.0,
width: 1400,
height: 1200,
alpha: 0.3,
tint: 0xFFFF88
});
self.headlightBeam2.x = 838;
self.headlightBeam2.y = -15;
// Create left headlight beam effect
self.leftBeam = self.attachAsset('beamL', {
anchorX: 0.5,
anchorY: 1.0,
width: 1400,
height: 1200,
alpha: 0.3,
tint: 0xFFFF88
});
self.leftBeam.x = -753;
self.leftBeam.y = -50;
self.leftBeam.visible = false;
// Create second left headlight beam next to the first one
self.leftBeam2 = self.attachAsset('beamL', {
anchorX: 0.5,
anchorY: 1.0,
width: 1400,
height: 1200,
alpha: 0.3,
tint: 0xFFFF88
});
self.leftBeam2.x = -838;
self.leftBeam2.y = -15;
self.leftBeam2.visible = false;
self.speed = 5;
self.direction = 0;
self.momentum = {
x: 0,
y: 0
};
self._move_migrated = function () {
var momentumModifier = 0.1;
if (self.direction === 0) {
self.momentum.x += self.speed * momentumModifier;
} else {
self.momentum.y -= self.speed * momentumModifier;
}
var projectedMovement = self.projectMovement(self.momentum);
// Check if car reaches the center of the screen on the X coordinate
if (self.lastX <= 2048 / 2 && self.x > 2048 / 2) {
console.log("Car reached the center of the screen on the X coordinate");
}
self.lastX = self.x;
self.x += projectedMovement.x;
self.y += projectedMovement.y;
// Check if car arrives at a specific X, Y coordinate
if (self.lastY <= 1500 && self.y > 1500 && self.lastX <= 1000 && self.x > 1000) {
console.log("Car arrived at the specific X, Y coordinate");
}
self.lastY = self.y;
self.lastX = self.x;
// Check if car comes close to the bottom of the screen on the Y coordinate
if (self.lastY <= 2732 - 100 && self.y > 2732 - 100) {
console.log("Car is close to the bottom of the screen");
}
self.lastY = self.y;
var nonTravelMomentum;
if (self.direction === 0) {
self.momentum.x *= 0.98;
self.momentum.y *= 0.95;
nonTravelMomentum = self.momentum.y;
} else {
self.momentum.x *= 0.95;
self.momentum.y *= 0.98;
nonTravelMomentum = self.momentum.x;
}
self.nonTravelMomentum = nonTravelMomentum;
// Show beamR only when car is turned to the right (direction === 0)
var shouldShowRightBeam = self.direction === 0;
self.headlightBeam.visible = shouldShowRightBeam;
self.headlightBeam2.visible = shouldShowRightBeam;
// Show beamL only when car is turned to the left (direction === 1)
var shouldShowLeftBeam = self.direction === 1;
if (self.leftBeam) {
self.leftBeam.visible = shouldShowLeftBeam;
}
if (self.leftBeam2) {
self.leftBeam2.visible = shouldShowLeftBeam;
}
if (shouldShowRightBeam) {
// Update headlight beam intensity based on speed
var totalSpeed = Math.sqrt(self.momentum.x * self.momentum.x + self.momentum.y * self.momentum.y);
var beamIntensity = Math.min(0.6, Math.max(0.2, totalSpeed * 0.05));
// Animate headlight beam flickering
tween(self.headlightBeam, {
alpha: beamIntensity
}, {
duration: 100 + Math.random() * 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Create slight flickering effect
var flickerAlpha = beamIntensity + (Math.random() - 0.5) * 0.1;
tween(self.headlightBeam, {
alpha: Math.max(0.1, flickerAlpha)
}, {
duration: 50,
easing: tween.linear
});
}
});
// Animate second beam with slight variation
tween(self.headlightBeam2, {
alpha: beamIntensity
}, {
duration: 120 + Math.random() * 180,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Create slight flickering effect
var flickerAlpha = beamIntensity + (Math.random() - 0.5) * 0.1;
tween(self.headlightBeam2, {
alpha: Math.max(0.1, flickerAlpha)
}, {
duration: 60,
easing: tween.linear
});
}
});
}
if (shouldShowLeftBeam && self.leftBeam) {
// Update left beam intensity based on speed
var totalSpeed = Math.sqrt(self.momentum.x * self.momentum.x + self.momentum.y * self.momentum.y);
var beamIntensity = Math.min(0.6, Math.max(0.2, totalSpeed * 0.05));
// Animate left beam flickering
tween(self.leftBeam, {
alpha: beamIntensity
}, {
duration: 100 + Math.random() * 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Create slight flickering effect
var flickerAlpha = beamIntensity + (Math.random() - 0.5) * 0.1;
tween(self.leftBeam, {
alpha: Math.max(0.1, flickerAlpha)
}, {
duration: 50,
easing: tween.linear
});
}
});
// Animate second left beam with slight variation
if (self.leftBeam2) {
tween(self.leftBeam2, {
alpha: beamIntensity
}, {
duration: 120 + Math.random() * 180,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Create slight flickering effect
var flickerAlpha = beamIntensity + (Math.random() - 0.5) * 0.1;
tween(self.leftBeam2, {
alpha: Math.max(0.1, flickerAlpha)
}, {
duration: 60,
easing: tween.linear
});
}
});
}
}
// Adjust beam width based on direction and momentum
var beamWidth = 1350 + Math.abs(self.nonTravelMomentum) * 20;
tween(self.headlightBeam, {
width: beamWidth
}, {
duration: 200,
easing: tween.easeOut
});
// Update second beam width as well
tween(self.headlightBeam2, {
width: beamWidth
}, {
duration: 200,
easing: tween.easeOut
});
if (self.leftBeam) {
tween(self.leftBeam, {
width: beamWidth
}, {
duration: 200,
easing: tween.easeOut
});
}
if (self.leftBeam2) {
tween(self.leftBeam2, {
width: beamWidth
}, {
duration: 200,
easing: tween.easeOut
});
}
};
self.changeDirection = function () {
self.direction = self.direction === 0 ? 1 : 0;
carGraphics.scale.x *= -1;
// Synchronize beam rotation with car direction
self.headlightBeam.scale.x *= -1;
self.headlightBeam2.scale.x *= -1;
if (self.leftBeam) {
self.leftBeam.scale.x *= -1;
}
if (self.leftBeam2) {
self.leftBeam2.scale.x *= -1;
}
};
});
var DriftAndDodge = Container.expand(function () {
var self = Container.call(this);
});
var Dust = Container.expand(function () {
var self = Container.call(this);
var dustGraphics = self.attachAsset('dust', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifetime = 60;
self.velocityY = -2 - Math.random() * 3;
self.velocityX = (Math.random() - 0.5) * 2;
self.fadeSpeed = 0.02;
self.scaleSpeed = 0.02;
self.update = function () {
self.y += self.velocityY;
self.x += self.velocityX;
self.alpha -= self.fadeSpeed;
self.scaleX += self.scaleSpeed;
self.scaleY += self.scaleSpeed;
if (self.alpha <= 0 || --self.lifetime <= 0) {
self.destroy();
}
};
return self;
});
var FogOverlay = Container.expand(function () {
var self = Container.call(this);
var fogGraphics = self.attachAsset('fogOverlay', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
// Position fog at screen center for fullscreen effect
fogGraphics.x = 0;
fogGraphics.y = 0;
self.isActive = false;
self.startFog = function () {
self.isActive = true;
// Fade in fog effect
tween(fogGraphics, {
alpha: 0.4
}, {
duration: 2000,
easing: tween.easeInOut
});
// Start continuous fog animation
self.animateFog();
};
self.stopFog = function () {
self.isActive = false;
// Fade out fog effect
tween(fogGraphics, {
alpha: 0
}, {
duration: 2000,
easing: tween.easeInOut
});
};
self.animateFog = function () {
if (!self.isActive) return;
// Create subtle fog movement
tween(fogGraphics, {
alpha: 0.3 + Math.random() * 0.2
}, {
duration: 3000 + Math.random() * 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (self.isActive) {
self.animateFog();
}
}
});
};
return self;
});
// Add the game logic for 'DriftAndDodge' here
var Graphics = Container.expand(function () {
var self = Container.call(this);
self.clear = function () {};
self.beginFill = function (color, alpha) {
self._fillColor = color;
self._fillAlpha = alpha;
};
self.drawRect = function (x, y, width, height) {
var rect = LK.getAsset('beamR', {
// Using 'beamR' asset for rectangle drawing
x: x,
y: y,
width: width,
height: height,
tint: self._fillColor,
alpha: self._fillAlpha
});
self.addChild(rect);
};
self.endFill = function () {};
return self;
});
var Lightning = Container.expand(function () {
var self = Container.call(this);
var lightningGraphics = self.attachAsset('lightning', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
// Position lightning at screen center for fullscreen effect
lightningGraphics.x = 0;
lightningGraphics.y = 0;
self.flashDuration = 150 + Math.random() * 200;
self.flickerCount = 2 + Math.floor(Math.random() * 3);
self.currentFlicker = 0;
self.isActive = false;
self.startLightning = function () {
self.isActive = true;
self.currentFlicker = 0;
self.doFlash();
};
self.doFlash = function () {
if (self.currentFlicker >= self.flickerCount) {
self.endLightning();
return;
}
// Flash to full brightness
tween(lightningGraphics, {
alpha: 0.15 + Math.random() * 0.1
}, {
duration: 50 + Math.random() * 100,
easing: tween.easeOut,
onFinish: function onFinish() {
// Quick fade out
tween(lightningGraphics, {
alpha: 0
}, {
duration: 30 + Math.random() * 70,
easing: tween.easeIn,
onFinish: function onFinish() {
self.currentFlicker++;
if (self.currentFlicker < self.flickerCount) {
// Brief pause before next flicker
LK.setTimeout(function () {
if (self.isActive) {
self.doFlash();
}
}, 100 + Math.random() * 200);
} else {
self.endLightning();
}
}
});
}
});
};
self.endLightning = function () {
self.isActive = false;
lightningGraphics.alpha = 0;
};
return self;
});
var Particle = Container.expand(function () {
var self = Container.call(this);
var particleGraphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
particleGraphics.rotation = Math.PI / 4;
self.lifetime = 100;
self.tick = function () {
if (--self.lifetime <= 0) {
self.destroy();
}
};
});
var RainDrop = Container.expand(function () {
var self = Container.call(this);
// Create raindrop using particle asset with blue tint
var dropGraphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.05,
scaleY: 0.8,
tint: 0x87CEEB,
alpha: 0.6
});
// Rain properties - enhanced for full level coverage
self.fallSpeed = 10 + Math.random() * 6;
self.windDrift = (Math.random() - 0.5) * 3;
self.lifetime = 400 + Math.random() * 300; // Longer lifetime for better coverage
self.maxLifetime = self.lifetime;
// Add slight rotation for realism
dropGraphics.rotation = Math.random() * 0.2 - 0.1;
// Removed tween animations for better performance
self.update = function () {
// Fall down with wind drift
self.y += self.fallSpeed;
self.x += self.windDrift;
// Fade out as lifetime decreases
var fadeRatio = self.lifetime / self.maxLifetime;
self.alpha = Math.max(0, fadeRatio * 0.6);
// Destroy when lifetime expires or when reaching bottom of screen
if (--self.lifetime <= 0 || self.y > 2732 + 100) {
self.destroy();
}
};
return self;
});
var Rock = Container.expand(function () {
var self = Container.call(this);
var rockGraphics = self.attachAsset('rock', {
anchorX: 0.5,
anchorY: 0.5
});
});
var SnowyEmbankment = Container.expand(function () {
var self = Container.call(this);
var embankmentGraphics = self.attachAsset('rods', {
anchorX: 0.5,
anchorY: 0.5
});
});
var Tree = Container.expand(function () {
var self = Container.call(this);
var treeGraphics = self.attachAsset('tree', {
anchorX: 0.5,
anchorY: 0.5
});
});
var Tree2 = Container.expand(function () {
var self = Container.call(this);
var treeGraphics = self.attachAsset('tree2', {
anchorX: 0.5,
anchorY: 0.5
});
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xFFFFFF
});
/****
* Game Code
****/
var filters = {
BlurFilter: function BlurFilter() {
this.blur = 0;
}
};
var driftAndDodge = game.addChild(new DriftAndDodge());
driftAndDodge.on('down', function (x, y, obj) {
// Add the event handler for 'down' event here
});
driftAndDodge.on('up', function (x, y, obj) {
// Add the event handler for 'up' event here
});
driftAndDodge.on('move', function (x, y, obj) {
// Add the event handler for 'move' event here
});
game.calculateDistanceToPoint = function (point, segmentStart, segmentEnd) {
var A = point.x - segmentStart.x;
var B = point.y - segmentStart.y;
var C = segmentEnd.x - segmentStart.x;
var D = segmentEnd.y - segmentStart.y;
var dot = A * C + B * D;
var len_sq = C * C + D * D;
var param = -1;
if (len_sq != 0) {
param = dot / len_sq;
}
var xx, yy;
if (param < 0) {
xx = segmentStart.x;
yy = segmentStart.y;
} else if (param > 1) {
xx = segmentEnd.x;
yy = segmentEnd.y;
} else {
xx = segmentStart.x + param * C;
yy = segmentStart.y + param * D;
}
var dx = point.x - xx;
var dy = point.y - yy;
return Math.sqrt(dx * dx + dy * dy);
};
game.addRoadSegment = function () {
var lastSegment = roadSegments[roadSegments.length - 1];
zigzag = !zigzag;
var segment = roadContainer.attachAsset('roadSegment', {
anchorX: 0.5
});
// Adjust segment width based on difficulty
var difficultyMultiplier = 1.0;
if (currentDifficulty === 'Normal') {
difficultyMultiplier = 0.85; // Make track 15% thinner
} else if (currentDifficulty === 'Hard') {
difficultyMultiplier = 0.5; // Make track 50% thinner for extreme difficulty
}
segment.width = segmentWidth * difficultyMultiplier;
// More controlled width reduction with minimum thresholds to prevent roads becoming too thin
var widthReduction = currentDifficulty === 'Hard' ? 8 : 5;
var minimumWidth = currentDifficulty === 'Hard' ? 600 : 800;
segmentWidth = Math.max(minimumWidth * difficultyMultiplier, segmentWidth - widthReduction);
segment.height = (i === 1 ? 3000 : Math.floor(Math.random() * (4000 - 1200 + 1)) + 1200) * 2;
segment.rotation = zigzag ? -Math.PI - Math.PI / 4 : -Math.PI + Math.PI / 4;
segment.y = currentY;
segment.x = currentX;
var adjustedHeight = segment.height - segmentWidth / 2;
currentY += adjustedHeight * Math.cos(segment.rotation);
currentX -= adjustedHeight * Math.sin(segment.rotation);
segment.shadow = roadContainer.attachAsset('roadSegmentShadow', {
anchorX: 0.5
});
segment.shadow.width = segment.width;
segment.shadow.height = segment.height;
segment.shadow.rotation = segment.rotation;
segment.shadow.x = segment.x;
segment.shadow.y = segment.y + 50;
segment.shadow.alpha = 1;
segment.used = false;
roadSegments.push(segment);
roadContainer.addChildAt(segment.shadow, 0);
roadContainer.addChild(segment);
// Add multiple trees to the left and right of the road segment
var treeSpacing = 300; // Increase space between trees
var numberOfTrees = Math.floor(segment.height / treeSpacing) - 3; // Decrease the number of trees by 3
var numberOfTree2 = Math.floor(segment.height / treeSpacing) - 3; // Decrease the number of tree2 by 3
for (var i = 0; i < numberOfTree2; i++) {
var leftTree = new Tree();
// Adjust tree distance based on difficulty - increase distance in hard mode
var treeDistance = 100;
if (currentDifficulty === 'Hard') {
treeDistance = 800; // Much further from road in hard mode to prevent touching
} else if (currentDifficulty === 'Normal') {
treeDistance = 200; // Moderately further in normal mode
}
leftTree.x = segment.x - segment.width - treeDistance - i * treeSpacing;
leftTree.y = segment.y + i * treeSpacing;
roadContainer.addChild(leftTree);
var rightTree = new Tree2();
rightTree.x = segment.x + segment.width + treeDistance + i * treeSpacing;
rightTree.y = segment.y + i * treeSpacing;
roadContainer.addChild(rightTree);
}
// Add rocks to the left and right of the road segment
var leftRock = new Rock();
leftRock.x = segment.x - segment.width - 1000; // Double the distance from the road
leftRock.y = segment.y;
roadContainer.addChildAt(leftRock, 0);
roadContainer.addChildAt(rightRock, 0);
var rightRock = new Rock();
rightRock.x = segment.x + segment.width + 1000; // Double the distance from the road
rightRock.y = segment.y;
// Attach snowy embankments to the left and right of the road segment
var leftEmbankment = new SnowyEmbankment();
leftEmbankment.x = segment.x - segment.width / 2 - 50; // Increase distance by 50 units
leftEmbankment.y = segment.y;
roadContainer.addChild(leftEmbankment);
var rightEmbankment = new SnowyEmbankment();
rightEmbankment.x = segment.x + segment.width / 2 + 50; // Increase distance by 50 units
rightEmbankment.y = segment.y;
roadContainer.addChild(rightEmbankment);
};
game.setBackgroundColor(0x000000); // Set background to black
var particles = [];
var dustParticles = [];
var rainDrops = [];
var rainIntensity = 3; // Number of raindrops to spawn per tick
var lightningContainer = null;
var lightningTimer = 0;
var nextLightningTime = 0;
var fogOverlay = null;
var mainContainer = game.addChild(new Container());
var roadContainer = mainContainer.addChild(new Container());
var roadSegments = [];
var segmentLength = Math.floor(Math.random() * (1000 - 200 + 1)) + 200;
var segmentWidth = 1200;
var currentX = 2048 / 2;
var currentY = 2732 / 2;
var zigzag = true;
for (var i = 1; i <= 15; i++) {
game.addRoadSegment();
}
var turnText = new Text2('Turn:', {
size: 50,
fill: 0xFFFFFF,
weight: '800',
dropShadow: true,
dropShadowColor: '#373330',
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 6,
dropShadowDistance: 6
});
turnText.anchor.set(1, 0); // Anchor to right edge so it aligns nicely with score
turnText.visible = false; // Hide turn text initially
LK.gui.topRight.addChild(turnText);
var scoreText = new Text2('0', {
size: 50,
fill: 0xFFFFFF,
weight: '800',
dropShadow: true,
dropShadowColor: '#373330',
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 6,
dropShadowDistance: 6
});
scoreText.anchor.set(0, 0);
scoreText.visible = false; // Hide score text initially
LK.gui.topRight.addChild(scoreText);
// Position turn text to the left of score text
turnText.x = scoreText.x - 107;
turnText.y = scoreText.y;
// Move score text left by 69 units
scoreText.x = scoreText.x - 69;
// Add a neon blue-colored speedometer to the bottom left corner of the map
var isMenuShowing = true;
var speedometer = new Text2('Speed: 0', {
size: 50,
fill: 0xFFD700,
// Gold color
weight: '800',
dropShadow: true,
dropShadowColor: '#000000',
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 6,
dropShadowDistance: 6
});
speedometer.anchor.set(0, 1); // Anchor to bottom left
speedometer.visible = false; // Hide speedometer initially
LK.gui.bottomLeft.addChild(speedometer);
var menuContainer = LK.gui.addChild(new Container());
var wallpaper = menuContainer.attachAsset('MW1', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 300,
y: 2732 / 2 - 400
});
var wallpaper2 = menuContainer.attachAsset('MW2', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 300,
y: 2732 / 2 - 400,
alpha: 0
});
var wallpaper3 = menuContainer.attachAsset('MW3', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 300,
y: 2732 / 2 - 400,
alpha: 0
});
var menuBackground = menuContainer.addChild(new BlurBackground({
width: 2048,
height: 2732,
blur: 25
}));
// Initialize slideshow variables
var currentWallpaper = 0; // 0 for MW1, 1 for MW2, 2 for MW3
var slideshowTimer = 0;
var slideshowInterval = 4000; // 4 seconds between transitions
var transitionDuration = 2000; // 2 seconds for each transition
// Start the slideshow gradient transition
function startSlideshowTransition() {
if (currentWallpaper === 0) {
// Fade from MW1 to MW2
tween(wallpaper, {
alpha: 0
}, {
duration: transitionDuration,
easing: tween.easeInOut
});
tween(wallpaper2, {
alpha: 1
}, {
duration: transitionDuration,
easing: tween.easeInOut,
onFinish: function onFinish() {
currentWallpaper = 1;
slideshowTimer = 0;
}
});
} else if (currentWallpaper === 1) {
// Fade from MW2 to MW3
tween(wallpaper2, {
alpha: 0
}, {
duration: transitionDuration,
easing: tween.easeInOut
});
tween(wallpaper3, {
alpha: 1
}, {
duration: transitionDuration,
easing: tween.easeInOut,
onFinish: function onFinish() {
currentWallpaper = 2;
slideshowTimer = 0;
}
});
} else {
// Fade from MW3 to MW1
tween(wallpaper3, {
alpha: 0
}, {
duration: transitionDuration,
easing: tween.easeInOut
});
tween(wallpaper, {
alpha: 1
}, {
duration: transitionDuration,
easing: tween.easeInOut,
onFinish: function onFinish() {
currentWallpaper = 0;
slideshowTimer = 0;
}
});
}
}
// Effects removed from menu
LK.playMusic('menuMusic', {
loop: true
});
var difficultyButtonContainer = new Container();
difficultyButtonContainer.x = 200;
difficultyButtonContainer.y = 2732 / 2 + 377;
var difficultyButtonAsset = difficultyButtonContainer.attachAsset('startbutton', {
anchorX: 0.5,
anchorY: 0.5
});
var difficultyText = new Text2('Difficulty', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
difficultyText.anchor.set(0.5, 0.5);
difficultyButtonContainer.addChild(difficultyText);
menuContainer.addChild(difficultyButtonContainer);
var weatherButtonContainer = new Container();
weatherButtonContainer.x = 600;
weatherButtonContainer.y = 2732 / 2 + 377;
var weatherButtonAsset = weatherButtonContainer.attachAsset('startbutton', {
anchorX: 0.5,
anchorY: 0.5
});
var weatherText = new Text2('Weather', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
weatherText.anchor.set(0.5, 0.5);
weatherButtonContainer.addChild(weatherText);
menuContainer.addChild(weatherButtonContainer);
// Difficulty level buttons removed
// Add underline graphics for selected difficulty
var startButtonContainer = new Container();
startButtonContainer.x = 2048 / 2 - 126 + 333;
startButtonContainer.y = 2732 / 2 + 380;
var startButtonAsset = startButtonContainer.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
width: 369,
height: 90
});
var startButton = new Text2('Start Game', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
startButton.anchor.set(0.5, 0.5);
startButtonContainer.addChild(startButton);
function flashStartButton() {
// No flashing animation for start button
}
// Initialize difficulty level text objects with button backgrounds
var easyButtonContainer = new Container();
var easyButtonAsset = easyButtonContainer.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
easyText = new Text2('EASY', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
easyText.anchor.set(0.5, 0.5);
easyButtonContainer.addChild(easyText);
easyButtonContainer.visible = false;
menuContainer.addChild(easyButtonContainer);
var normalButtonContainer = new Container();
var normalButtonAsset = normalButtonContainer.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
normalText = new Text2('NORMAL', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
normalText.anchor.set(0.5, 0.5);
normalButtonContainer.addChild(normalText);
normalButtonContainer.visible = false;
menuContainer.addChild(normalButtonContainer);
var hardButtonContainer = new Container();
var hardButtonAsset = hardButtonContainer.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
hardText = new Text2('HARD', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
hardText.anchor.set(0.5, 0.5);
hardButtonContainer.addChild(hardText);
hardButtonContainer.visible = false;
menuContainer.addChild(hardButtonContainer);
// Initialize weather level text objects with button backgrounds
var clearButtonContainer = new Container();
var clearButtonAsset = clearButtonContainer.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
clearText = new Text2('CLEAR', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
clearText.anchor.set(0.5, 0.5);
clearButtonContainer.addChild(clearText);
clearButtonContainer.visible = false;
menuContainer.addChild(clearButtonContainer);
var rainButtonContainer = new Container();
var rainButtonAsset = rainButtonContainer.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
rainText = new Text2('RAIN', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
rainText.anchor.set(0.5, 0.5);
rainButtonContainer.addChild(rainText);
rainButtonContainer.visible = false;
menuContainer.addChild(rainButtonContainer);
var stormButtonContainer = new Container();
var stormButtonAsset = stormButtonContainer.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
stormText = new Text2('STORM', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
stormText.anchor.set(0.5, 0.5);
stormButtonContainer.addChild(stormText);
stormButtonContainer.visible = false;
menuContainer.addChild(stormButtonContainer);
var fogButtonContainer = new Container();
var fogButtonAsset = fogButtonContainer.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
fogText = new Text2('FOG', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
fogText.anchor.set(0.5, 0.5);
fogButtonContainer.addChild(fogText);
fogButtonContainer.visible = false;
menuContainer.addChild(fogButtonContainer);
// Start the flashing animation
menuContainer.addChild(startButtonContainer);
difficultyButtonContainer.down = function (x, y, obj) {
// Add pulse feedback animation to difficulty button
tween(difficultyButtonContainer, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(difficultyButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Change difficulty level after pulse feedback
difficultyIndex = (difficultyIndex + 1) % difficultyLevels.length;
currentDifficulty = difficultyLevels[difficultyIndex];
difficultyText.setText('Difficulty');
// Position and show the difficulty level button containers
easyButtonContainer.x = difficultyButtonContainer.x;
easyButtonContainer.y = difficultyButtonContainer.y - easyButtonContainer.height - 10; // Above normal text
normalButtonContainer.x = difficultyButtonContainer.x;
normalButtonContainer.y = difficultyButtonContainer.y - normalButtonContainer.height - 10 - easyButtonContainer.height; // Above hard text
hardButtonContainer.x = difficultyButtonContainer.x;
hardButtonContainer.y = difficultyButtonContainer.y - hardButtonContainer.height - 10 - easyButtonContainer.height - normalButtonContainer.height; // Above difficulty button
hardButtonContainer.visible = true;
normalButtonContainer.visible = true;
easyButtonContainer.visible = true;
}
});
}
});
};
startButtonContainer.down = function (x, y, obj) {
// Check if difficulty has been selected before allowing start
if (!difficultySelected) {
// Show info text
if (!infoText) {
infoText = new Text2('Please choose a difficulty level!', {
size: 40,
fill: 0xFFFFFF,
align: 'center'
});
infoText.anchor.set(0.5, 0.5);
infoText.x = startButtonContainer.x - 512;
infoText.y = startButtonContainer.y - 80 + 180;
menuContainer.addChild(infoText);
}
infoText.visible = true;
// Hide info text after 3 seconds
LK.setTimeout(function () {
if (infoText) {
infoText.visible = false;
}
}, 3000);
// Pulse animation to indicate difficulty must be selected first
tween(startButtonContainer, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(startButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeOut
});
}
});
return; // Exit early if no difficulty is selected
}
// Add pulse feedback animation before loading track
tween(startButtonContainer, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(startButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Load track after pulse feedback completes
isMenuShowing = false;
menuContainer.visible = false;
mainContainer.visible = true;
// Show counters when game starts
scoreText.visible = true;
speedometer.visible = true;
turnText.visible = true;
timerText.visible = true;
// Initialize game start time
gameStartTime = Date.now();
// Properly load and activate the game world
// Initialize car's last position tracking for movement detection
car.lastX = car.x;
car.lastY = car.y;
car.lastWasIntersecting = false;
// Stop menu music and start the game music
LK.stopMusic();
LK.playMusic('Car', {
loop: true
});
// Reset game state
score = 0;
LK.setScore(score);
scoreText.setText(score.toString());
isGameOver = false;
// Initialize lightning system
lightningContainer = game.addChild(new Lightning());
// Position lightning at screen center for fullscreen effect
lightningContainer.x = 2048 / 2;
lightningContainer.y = 2732 / 2;
lightningTimer = 0;
nextLightningTime = 2000 + Math.random() * 5000; // First lightning in 2-7 seconds
// Initialize fog overlay system
fogOverlay = game.addChild(new FogOverlay());
// Position fog overlay at screen center for fullscreen effect
fogOverlay.x = 2048 / 2;
fogOverlay.y = 2732 / 2;
// Ensure all road segments are properly initialized
roadSegments.forEach(function (segment) {
segment.used = false;
});
}
});
}
});
};
var car = mainContainer.addChild(new Car());
car.x = 2048 / 2;
car.y = 2732 / 2;
// Hide main container when menu is showing
mainContainer.visible = false;
// Hide difficulty level button containers when menu is hidden
easyButtonContainer.visible = false;
normalButtonContainer.visible = false;
hardButtonContainer.visible = false;
// Hide weather option button containers when menu is hidden
clearButtonContainer.visible = false;
rainButtonContainer.visible = false;
stormButtonContainer.visible = false;
fogButtonContainer.visible = false;
// Add difficulty selector function to easy, normal, hard text
easyButtonContainer.down = function (x, y, obj) {
// Tint the button orange when touched
tween(easyButtonAsset, {
tint: 0xff8c00
}, {
duration: 150,
easing: tween.easeOut
});
// Add pulse feedback animation to selected mode button
tween(easyButtonContainer, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(easyButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
difficultyIndex = 0;
currentDifficulty = difficultyLevels[difficultyIndex];
difficultyText.setText('Difficulty');
// Hide difficulty level button containers
easyButtonContainer.visible = false;
normalButtonContainer.visible = false;
hardButtonContainer.visible = false;
difficultySelected = true; // Set flag to true when difficulty is selected
startButtonContainer.removeChild(startButtonAsset); // Remove old button asset
startButtonAsset = startButtonContainer.attachAsset('startbutton', {
// Add new startbutton asset
anchorX: 0.5,
anchorY: 0.5,
width: 369,
height: 100
});
startButtonContainer.addChildAt(startButtonAsset, 0); // Add it behind the text
}
});
}
});
};
normalButtonContainer.down = function (x, y, obj) {
// Tint the button orange when touched
tween(normalButtonAsset, {
tint: 0xff8c00
}, {
duration: 150,
easing: tween.easeOut
});
// Add pulse feedback animation to selected mode button
tween(normalButtonContainer, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(normalButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
difficultyIndex = 1;
currentDifficulty = difficultyLevels[difficultyIndex];
difficultyText.setText('Difficulty');
// Hide difficulty level button containers
easyButtonContainer.visible = false;
normalButtonContainer.visible = false;
hardButtonContainer.visible = false;
difficultySelected = true; // Set flag to true when difficulty is selected
startButtonContainer.removeChild(startButtonAsset); // Remove old button asset
startButtonAsset = startButtonContainer.attachAsset('startbutton', {
// Add new startbutton asset
anchorX: 0.5,
anchorY: 0.5,
width: 369,
height: 100
});
startButtonContainer.addChildAt(startButtonAsset, 0); // Add it behind the text
}
});
}
});
};
hardButtonContainer.down = function (x, y, obj) {
// Tint the button orange when touched
tween(hardButtonAsset, {
tint: 0xff8c00
}, {
duration: 150,
easing: tween.easeOut
});
// Add pulse feedback animation to selected mode button
tween(hardButtonContainer, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(hardButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
difficultyIndex = 2;
currentDifficulty = difficultyLevels[difficultyIndex];
difficultyText.setText('Difficulty');
// Hide difficulty level button containers
easyButtonContainer.visible = false;
normalButtonContainer.visible = false;
hardButtonContainer.visible = false;
difficultySelected = true; // Set flag to true when difficulty is selected
startButtonContainer.removeChild(startButtonAsset); // Remove old button asset
startButtonAsset = startButtonContainer.attachAsset('startbutton', {
// Add new startbutton asset
anchorX: 0.5,
anchorY: 0.5,
width: 369,
height: 100
});
startButtonContainer.addChildAt(startButtonAsset, 0); // Add it behind the text
}
});
}
});
};
weatherButtonContainer.down = function (x, y, obj) {
// Add pulse feedback animation to weather button
tween(weatherButtonContainer, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(weatherButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Show weather option buttons after pulse feedback
weatherText.setText('Weather');
// Position and show the weather option button containers
clearButtonContainer.x = weatherButtonContainer.x;
clearButtonContainer.y = weatherButtonContainer.y - clearButtonContainer.height - 10; // Above rain text
rainButtonContainer.x = weatherButtonContainer.x;
rainButtonContainer.y = weatherButtonContainer.y - rainButtonContainer.height - 10 - clearButtonContainer.height; // Above storm text
stormButtonContainer.x = weatherButtonContainer.x;
stormButtonContainer.y = weatherButtonContainer.y - stormButtonContainer.height - 10 - clearButtonContainer.height - rainButtonContainer.height; // Above fog text
fogButtonContainer.x = weatherButtonContainer.x;
fogButtonContainer.y = weatherButtonContainer.y - fogButtonContainer.height - 10 - clearButtonContainer.height - rainButtonContainer.height - stormButtonContainer.height; // Above weather button
fogButtonContainer.visible = true;
stormButtonContainer.visible = true;
rainButtonContainer.visible = true;
clearButtonContainer.visible = true;
}
});
}
});
};
var isGameOver = false;
var score = 0;
var closestSegment = null;
var gameStartTime = 0;
var currentDifficulty = 'Easy'; // Track current difficulty level
var difficultyLevels = ['Easy', 'Normal', 'Hard']; // Available difficulty levels
var difficultyIndex = 0; // Current difficulty index
var easyText;
var normalText;
var hardText;
var difficultySelected = false; // Flag to indicate if a difficulty has been selected
var infoText = null; // Info text for difficulty selection prompt
var currentWeather = 'Clear'; // Track current weather
var weatherTypes = ['Clear', 'Rain', 'Storm', 'Fog']; // Available weather types
var weatherIndex = 0; // Current weather index
var weatherSelected = false; // Flag to indicate if a weather has been selected
// Add weather selector function to clear, rain, storm text
clearButtonContainer.down = function (x, y, obj) {
// Tint the button orange when touched
tween(clearButtonAsset, {
tint: 0xff8c00
}, {
duration: 150,
easing: tween.easeOut
});
// Add pulse feedback animation to selected mode button
tween(clearButtonContainer, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(clearButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
weatherIndex = 0;
currentWeather = weatherTypes[weatherIndex];
weatherText.setText('Weather');
// Hide weather option button containers
clearButtonContainer.visible = false;
rainButtonContainer.visible = false;
stormButtonContainer.visible = false;
fogButtonContainer.visible = false;
weatherSelected = true; // Set flag to true when weather is selected
}
});
}
});
};
rainButtonContainer.down = function (x, y, obj) {
// Tint the button orange when touched
tween(rainButtonAsset, {
tint: 0xff8c00
}, {
duration: 150,
easing: tween.easeOut
});
// Add pulse feedback animation to selected mode button
tween(rainButtonContainer, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(rainButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
weatherIndex = 1;
currentWeather = weatherTypes[weatherIndex];
weatherText.setText('Weather');
// Hide weather option button containers
clearButtonContainer.visible = false;
rainButtonContainer.visible = false;
stormButtonContainer.visible = false;
fogButtonContainer.visible = false;
weatherSelected = true; // Set flag to true when weather is selected
}
});
}
});
};
stormButtonContainer.down = function (x, y, obj) {
// Tint the button orange when touched
tween(stormButtonAsset, {
tint: 0xff8c00
}, {
duration: 150,
easing: tween.easeOut
});
// Add pulse feedback animation to selected mode button
tween(stormButtonContainer, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(stormButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
weatherIndex = 2;
currentWeather = weatherTypes[weatherIndex];
weatherText.setText('Weather');
// Hide weather option button containers
clearButtonContainer.visible = false;
rainButtonContainer.visible = false;
stormButtonContainer.visible = false;
fogButtonContainer.visible = false;
weatherSelected = true; // Set flag to true when weather is selected
}
});
}
});
};
fogButtonContainer.down = function (x, y, obj) {
// Tint the button orange when touched
tween(fogButtonAsset, {
tint: 0xff8c00
}, {
duration: 150,
easing: tween.easeOut
});
// Add pulse feedback animation to selected mode button
tween(fogButtonContainer, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(fogButtonContainer, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
weatherIndex = 3;
currentWeather = weatherTypes[weatherIndex];
weatherText.setText('Weather');
// Hide weather option button containers
clearButtonContainer.visible = false;
rainButtonContainer.visible = false;
stormButtonContainer.visible = false;
fogButtonContainer.visible = false;
weatherSelected = true; // Set flag to true when weather is selected
}
});
}
});
};
var timerText = new Text2('00:00:00:000', {
size: 50,
fill: 0xFFFFFF,
weight: '800',
dropShadow: true,
dropShadowColor: '#373330',
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 6,
dropShadowDistance: 6
});
timerText.anchor.set(1, 1); // Anchor to bottom right
timerText.visible = false; // Hide timer initially
LK.gui.bottomRight.addChild(timerText);
game.down = function (x, y, obj) {
if (isMenuShowing) {
return;
}
car.changeDirection();
};
LK.on('tick', function () {
if (isMenuShowing) {
// Update slideshow timer when menu is showing
slideshowTimer += 16.67; // Approximate milliseconds per tick at 60fps
if (slideshowTimer >= slideshowInterval) {
startSlideshowTransition();
}
return;
}
car._move_migrated();
var carIsOnRoad = false;
var carPosition = {
x: car.x,
y: car.y
};
// Update the speedometer with the current speed
speedometer.setText('Speed: ' + Math.round(Math.sqrt(car.momentum.x * car.momentum.x + car.momentum.y * car.momentum.y) * 5) + ' km/h');
// Maintain consistent rain intensity throughout the level with gradual increase
var baseRainIntensity = Math.min(16, 6 + Math.floor(score / 10)); // Double the intensity - more gradual increase, higher max intensity
// Apply weather-based multiplier
if (currentWeather === 'Clear') {
rainIntensity = 0; // No rain in clear weather
} else if (currentWeather === 'Rain') {
rainIntensity = baseRainIntensity;
} else if (currentWeather === 'Storm') {
rainIntensity = baseRainIntensity * 2; // Double rain density for storm mode
// Handle lightning effects in storm mode
if (lightningContainer) {
lightningTimer += 16.67; // Approximate milliseconds per tick at 60fps
if (lightningTimer >= nextLightningTime && !lightningContainer.isActive) {
// Trigger lightning flash
lightningContainer.startLightning();
// Schedule next lightning strike (5-15 seconds)
lightningTimer = 0;
nextLightningTime = 4000 + Math.random() * 6000;
}
}
} else if (currentWeather === 'Fog') {
rainIntensity = 0; // No rain in fog weather
// Apply fog effect by reducing headlight beam intensity, but keep beam distance/placement as in other weather modes
if (car.headlightBeam) {
car.headlightBeam.alpha = Math.min(car.headlightBeam.alpha, 0.15);
}
if (car.headlightBeam2) {
car.headlightBeam2.alpha = Math.min(car.headlightBeam2.alpha, 0.15);
}
if (car.leftBeam) {
car.leftBeam.alpha = Math.min(car.leftBeam.alpha, 0.15);
}
if (car.leftBeam2) {
car.leftBeam2.alpha = Math.min(car.leftBeam2.alpha, 0.15);
}
// Activate fog overlay effect
if (fogOverlay && !fogOverlay.isActive) {
fogOverlay.startFog();
}
} else {
// Deactivate fog overlay for other weather modes
if (fogOverlay && fogOverlay.isActive) {
fogOverlay.stopFog();
}
}
// Update timer display
var currentTime = Date.now();
var elapsedTime = currentTime - gameStartTime;
var hours = Math.floor(elapsedTime / 3600000);
var minutes = Math.floor(elapsedTime % 3600000 / 60000);
var seconds = Math.floor(elapsedTime % 60000 / 1000);
var milliseconds = elapsedTime % 1000;
var formattedTime = (hours < 10 ? '0' + hours : hours) + ':' + (minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds < 10 ? '0' + seconds : seconds) + ':' + (milliseconds < 100 ? milliseconds < 10 ? '00' + milliseconds : '0' + milliseconds : milliseconds);
timerText.setText(formattedTime);
var currentClosestSegment = null;
var currentClosestDistance = Infinity;
roadSegments.forEach(function (segment) {
var segmentStart = {
x: segment.x + Math.sin(segment.rotation) * 100,
y: segment.y - Math.cos(segment.rotation) * 100
};
var segmentEnd = {
x: segment.x - Math.sin(segment.rotation) * (segment.height - segment.width / 2),
y: segment.y + Math.cos(segment.rotation) * (segment.height - segment.width / 2)
};
var distance = game.calculateDistanceToPoint(carPosition, segmentStart, segmentEnd);
if (distance < currentClosestDistance) {
currentClosestDistance = distance;
currentClosestSegment = segment;
}
if (distance < segment.width / 2 - 50) {
carIsOnRoad = true;
}
});
if (closestSegment !== currentClosestSegment && !currentClosestSegment.used) {
// Check for intersection between car and road segment
if (car.lastWasIntersecting === false && car.intersects(currentClosestSegment)) {
console.log("Car intersected with a road segment");
}
car.lastWasIntersecting = car.intersects(currentClosestSegment);
closestSegment = currentClosestSegment;
closestSegment.used = true;
score++;
LK.setScore(score);
scoreText.setText(score.toString());
}
if (!carIsOnRoad) {
LK.showGameOver();
} else {}
var particle = new Particle();
particle.alpha = Math.max(0, Math.min(1, Math.abs(car.nonTravelMomentum) / 5 - 0.5));
if (particle.alpha > 0) {
var noiseX = (Math.random() - 0.5) * 10;
var noiseY = (Math.random() - 0.5) * 10;
particle.x = car.x + noiseX;
particle.y = car.y + noiseY;
mainContainer.addChildAt(particle, 1);
particles.push(particle);
}
// Create brown dust particles under the car
if (LK.ticks % 3 === 0) {
var dust = new Dust();
dust.x = car.x + (Math.random() - 0.5) * 30;
dust.y = car.y + 40;
dust.alpha = 0.6 + Math.random() * 0.4;
dust.scaleX = 0.5 + Math.random() * 0.5;
dust.scaleY = 0.5 + Math.random() * 0.5;
// Animate dust with random movement
tween(dust, {
rotation: (Math.random() - 0.5) * Math.PI
}, {
duration: 1000 + Math.random() * 1000,
easing: tween.easeOut
});
mainContainer.addChildAt(dust, 1);
dustParticles.push(dust);
}
// Create additional side dust particles next to the car
if (LK.ticks % 2 === 0) {
// Left side dust - primary
var leftDust = new Dust();
leftDust.x = car.x - 60 + (Math.random() - 0.5) * 40;
leftDust.y = car.y + 20 + Math.random() * 30;
leftDust.alpha = 0.4 + Math.random() * 0.3;
leftDust.scaleX = 0.3 + Math.random() * 0.4;
leftDust.scaleY = 0.3 + Math.random() * 0.4;
leftDust.velocityX = -1 - Math.random() * 2;
// Animate left dust with drift movement
tween(leftDust, {
rotation: (Math.random() - 0.5) * Math.PI * 1.5,
scaleX: leftDust.scaleX + 0.3,
scaleY: leftDust.scaleY + 0.3
}, {
duration: 800 + Math.random() * 800,
easing: tween.easeOut
});
mainContainer.addChildAt(leftDust, 1);
dustParticles.push(leftDust);
// Right side dust - primary
var rightDust = new Dust();
rightDust.x = car.x + 60 + (Math.random() - 0.5) * 40;
rightDust.y = car.y + 20 + Math.random() * 30;
rightDust.alpha = 0.4 + Math.random() * 0.3;
rightDust.scaleX = 0.3 + Math.random() * 0.4;
rightDust.scaleY = 0.3 + Math.random() * 0.4;
rightDust.velocityX = 1 + Math.random() * 2;
// Animate right dust with drift movement
tween(rightDust, {
rotation: (Math.random() - 0.5) * Math.PI * 1.5,
scaleX: rightDust.scaleX + 0.3,
scaleY: rightDust.scaleY + 0.3
}, {
duration: 800 + Math.random() * 800,
easing: tween.easeOut
});
mainContainer.addChildAt(rightDust, 1);
dustParticles.push(rightDust);
}
// Create additional dense dust clouds on both sides
if (LK.ticks % 3 === 0) {
// Left side dense dust
var leftDenseDust = new Dust();
leftDenseDust.x = car.x - 80 + (Math.random() - 0.5) * 60;
leftDenseDust.y = car.y + 10 + Math.random() * 40;
leftDenseDust.alpha = 0.5 + Math.random() * 0.4;
leftDenseDust.scaleX = 0.6 + Math.random() * 0.6;
leftDenseDust.scaleY = 0.6 + Math.random() * 0.6;
leftDenseDust.velocityX = -2 - Math.random() * 3;
leftDenseDust.velocityY = -1 - Math.random() * 2;
// Animate left dense dust with swirl movement
tween(leftDenseDust, {
rotation: Math.PI + Math.random() * Math.PI,
scaleX: leftDenseDust.scaleX + 0.5,
scaleY: leftDenseDust.scaleY + 0.5,
alpha: 0.1
}, {
duration: 1200 + Math.random() * 1000,
easing: tween.easeOut
});
mainContainer.addChildAt(leftDenseDust, 1);
dustParticles.push(leftDenseDust);
// Right side dense dust
var rightDenseDust = new Dust();
rightDenseDust.x = car.x + 80 + (Math.random() - 0.5) * 60;
rightDenseDust.y = car.y + 10 + Math.random() * 40;
rightDenseDust.alpha = 0.5 + Math.random() * 0.4;
rightDenseDust.scaleX = 0.6 + Math.random() * 0.6;
rightDenseDust.scaleY = 0.6 + Math.random() * 0.6;
rightDenseDust.velocityX = 2 + Math.random() * 3;
rightDenseDust.velocityY = -1 - Math.random() * 2;
// Animate right dense dust with swirl movement
tween(rightDenseDust, {
rotation: -Math.PI - Math.random() * Math.PI,
scaleX: rightDenseDust.scaleX + 0.5,
scaleY: rightDenseDust.scaleY + 0.5,
alpha: 0.1
}, {
duration: 1200 + Math.random() * 1000,
easing: tween.easeOut
});
mainContainer.addChildAt(rightDenseDust, 1);
dustParticles.push(rightDenseDust);
}
// Create smaller wispy dust particles on both sides
if (LK.ticks % 5 === 0) {
// Left side wispy dust
var leftWispyDust = new Dust();
leftWispyDust.x = car.x - 40 + (Math.random() - 0.5) * 20;
leftWispyDust.y = car.y + 30 + Math.random() * 20;
leftWispyDust.alpha = 0.3 + Math.random() * 0.2;
leftWispyDust.scaleX = 0.2 + Math.random() * 0.3;
leftWispyDust.scaleY = 0.2 + Math.random() * 0.3;
leftWispyDust.velocityX = -0.5 - Math.random();
leftWispyDust.velocityY = -2 - Math.random() * 2;
// Animate left wispy dust with gentle float
tween(leftWispyDust, {
rotation: (Math.random() - 0.5) * Math.PI,
scaleX: leftWispyDust.scaleX + 0.2,
scaleY: leftWispyDust.scaleY + 0.2
}, {
duration: 600 + Math.random() * 600,
easing: tween.easeOut
});
mainContainer.addChildAt(leftWispyDust, 1);
dustParticles.push(leftWispyDust);
// Right side wispy dust
var rightWispyDust = new Dust();
rightWispyDust.x = car.x + 40 + (Math.random() - 0.5) * 20;
rightWispyDust.y = car.y + 30 + Math.random() * 20;
rightWispyDust.alpha = 0.3 + Math.random() * 0.2;
rightWispyDust.scaleX = 0.2 + Math.random() * 0.3;
rightWispyDust.scaleY = 0.2 + Math.random() * 0.3;
rightWispyDust.velocityX = 0.5 + Math.random();
rightWispyDust.velocityY = -2 - Math.random() * 2;
// Animate right wispy dust with gentle float
tween(rightWispyDust, {
rotation: (Math.random() - 0.5) * Math.PI,
scaleX: rightWispyDust.scaleX + 0.2,
scaleY: rightWispyDust.scaleY + 0.2
}, {
duration: 600 + Math.random() * 600,
easing: tween.easeOut
});
mainContainer.addChildAt(rightWispyDust, 1);
dustParticles.push(rightWispyDust);
}
particles.forEach(function (particle, index) {
particle.tick();
if (particle.lifetime <= 0) {
particles.splice(index, 1);
}
});
dustParticles.forEach(function (dust, index) {
dust.update();
if (dust.alpha <= 0 || dust.lifetime <= 0) {
dustParticles.splice(index, 1);
}
});
// Create rain drops continuously throughout the entire level
var rainSpawnRate = Math.max(2, Math.floor(8 / rainIntensity)); // Increased spawn rate for better rain visibility
if (LK.ticks % rainSpawnRate === 0) {
var spawnCount = Math.min(6, rainIntensity); // Increased max raindrops per spawn
for (var r = 0; r < spawnCount; r++) {
var rainDrop = new RainDrop();
// Calculate current camera offset to ensure rain covers visible area
var cameraOffsetX = -mainContainer.x;
var cameraOffsetY = -mainContainer.y;
// Spawn rain across much wider area accounting for camera movement
var spawnWidth = 4096; // Increased spawn width for better coverage
var spawnMargin = 768; // Increased margin
rainDrop.x = cameraOffsetX - spawnMargin + Math.random() * (spawnWidth + 2 * spawnMargin);
rainDrop.y = cameraOffsetY - 300 - Math.random() * 400;
// Add slight opacity variation for realism
rainDrop.alpha = 0.4 + Math.random() * 0.4;
// Remove tween animation to improve performance
mainContainer.addChild(rainDrop);
rainDrops.push(rainDrop);
}
}
// Update and clean up rain drops with performance optimization
for (var dropIndex = rainDrops.length - 1; dropIndex >= 0; dropIndex--) {
var drop = rainDrops[dropIndex];
drop.update();
if (drop.lifetime <= 0 || drop.alpha <= 0 || drop.y > 2732 + 200) {
drop.destroy();
rainDrops.splice(dropIndex, 1);
}
}
// Limit total number of raindrops for performance
if (rainDrops.length > 120) {
var excessDrops = rainDrops.length - 120;
for (var excess = 0; excess < excessDrops; excess++) {
var oldestDrop = rainDrops.shift();
if (oldestDrop) {
oldestDrop.destroy();
}
}
}
var carLocalPosition = game.toLocal(car.position, car.parent);
var offsetX = (2048 / 2 - carLocalPosition.x) / 20;
var offsetY = (2732 - 450 - carLocalPosition.y) / 20;
mainContainer.x += offsetX;
mainContainer.y += offsetY;
for (var i = roadSegments.length - 1; i >= 0; i--) {
var segmentGlobalPosition = game.toLocal(roadSegments[i].position, roadSegments[i].parent);
if (segmentGlobalPosition.y - roadSegments[i].height > 2732 * 2) {
roadSegments[i].shadow.destroy();
roadSegments[i].destroy();
roadSegments.splice(i, 1);
game.addRoadSegment();
}
// Destroy embankments which are off screen
if (roadSegments[i].leftEmbankment && roadSegments[i].leftEmbankment.y < -50) {
roadSegments[i].leftEmbankment.destroy();
}
if (roadSegments[i].rightEmbankment && roadSegments[i].rightEmbankment.y < -50) {
roadSegments[i].rightEmbankment.destroy();
}
}
});