User prompt
Why are You no showing well the beams in correct place as like in other weather modes? I mean the bigger distance in front of the car. Repair it
User prompt
Fit them
User prompt
You are no showing well the beams in correct place in the fog mode
User prompt
Repair it from storm info
User prompt
Add info text next to the pause button if STORM MODE selected: 'Attention! The lightning of the storm will bring up a white flashing effect! If you are sensitive to it, please avoid using this mode! Not just pop up this info text but no hide it while start button or other weather mode pressed
User prompt
NO HIDE STORM INFO TEXT AND NO FLASHING!!!
User prompt
Do not hide storm info text while start game button not pressed or other weather mode selected
User prompt
Storm info text should be looking as the first info text in below
User prompt
Remove flashing animation from storm info text
User prompt
Increase storm info text size to double
User prompt
Storm info text should be looking as the first info text in below
User prompt
Add info text next to the pause button if STORM MODE selected: 'Attention! The lightning of the storm will bring up a white flashing effect! If you are sensitive to it, please avoid using this mode!
User prompt
Add info text to the top of the map if STORM MODE selected: 'Attention! The lightning of the storm will bring up a white flashing effect! If you are sensitive to it, please avoid using this mode! ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add fog effect to the map in fog mode ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add FOG Mode to the weather selector ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Reduce the first to 2-7, Subsequent strikes to 4-10
User prompt
Lightning effect should be fullscreen ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add animated Stormy lightning effect to storm mode ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
You reduced too much raindrops
User prompt
Repair raindrops occured lags
User prompt
Destroy raindrops when they reach bottom of the screen
User prompt
Reset raindrops when they reach bottom of the screen
User prompt
Increase rain density to 2x in storm mode
User prompt
Avoid lags
User prompt
Optimise the game running to smoot without lags.
/**** * 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; }); // 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 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; // Animate slight scale variations for realistic effect tween(dropGraphics, { scaleY: dropGraphics.scaleY + (Math.random() - 0.5) * 0.2 }, { duration: 200 + Math.random() * 300, easing: tween.easeInOut, onFinish: function onFinish() { tween(dropGraphics, { scaleY: dropGraphics.scaleY - (Math.random() - 0.5) * 0.1 }, { duration: 200 + Math.random() * 300, easing: tween.easeInOut }); } }); 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 off 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 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); // 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; // 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; // 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 weather button 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']; // 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; 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; 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; 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 rainIntensity = Math.min(16, 6 + Math.floor(score / 10)); // Double the intensity - more gradual increase, higher max intensity // 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; // Optimize road segment iteration for better performance for (var s = 0; s < roadSegments.length; s++) { var segment = roadSegments[s]; 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 - optimized if (LK.ticks % 5 === 0 && dustParticles.length < 30) { 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; // Reduce tween frequency for performance if (Math.random() < 0.4) { // Only animate 40% of dust particles 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 - optimized if (LK.ticks % 4 === 0 && dustParticles.length < 40) { // 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 - optimized if (LK.ticks % 6 === 0 && dustParticles.length < 35) { // 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 - optimized if (LK.ticks % 8 === 0 && dustParticles.length < 25) { // 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); } // Optimize particle cleanup with reverse iteration for (var p = particles.length - 1; p >= 0; p--) { particles[p].tick(); if (particles[p].lifetime <= 0) { particles.splice(p, 1); } } for (var d = dustParticles.length - 1; d >= 0; d--) { dustParticles[d].update(); if (dustParticles[d].alpha <= 0 || dustParticles[d].lifetime <= 0) { dustParticles.splice(d, 1); } } // Create rain drops continuously throughout the entire level - optimized if (currentWeather !== 'Clear') { var stormMultiplier = currentWeather === 'Storm' ? 2 : 1; // Reduced from 3x to 2x var adjustedRainIntensity = Math.min(12, rainIntensity * stormMultiplier); // Cap max intensity var rainSpawnRate = Math.max(2, Math.floor(12 / adjustedRainIntensity)); // Less frequent spawning if (LK.ticks % rainSpawnRate === 0 && rainDrops.length < 50) { // Limit max raindrops for (var r = 0; r < Math.min(3, adjustedRainIntensity); r++) { // Cap spawns per tick var rainDrop = new RainDrop(); // Calculate current camera offset to ensure rain covers visible area var cameraOffsetX = -mainContainer.x; var cameraOffsetY = -mainContainer.y; // Reduced spawn area for better performance var spawnWidth = 2560; // Reduced from 4096 var spawnMargin = 512; // Reduced from 1024 rainDrop.x = cameraOffsetX - spawnMargin + Math.random() * (spawnWidth + 2 * spawnMargin); rainDrop.y = cameraOffsetY - 400 - Math.random() * 600; // Add slight opacity variation for realism rainDrop.alpha = 0.4 + Math.random() * 0.4; // Reduced tween frequency for performance if (Math.random() < 0.3) { // Only animate 30% of raindrops tween(rainDrop, { rotation: rainDrop.rotation + (Math.random() - 0.5) * 0.4 }, { duration: 800 + Math.random() * 1500, easing: tween.linear }); } mainContainer.addChild(rainDrop); rainDrops.push(rainDrop); } } } // Update and clean up rain drops - optimized for (var rd = rainDrops.length - 1; rd >= 0; rd--) { rainDrops[rd].update(); if (rainDrops[rd].lifetime <= 0 || rainDrops[rd].alpha <= 0) { rainDrops.splice(rd, 1); } } 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;
});
// 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 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;
// Animate slight scale variations for realistic effect
tween(dropGraphics, {
scaleY: dropGraphics.scaleY + (Math.random() - 0.5) * 0.2
}, {
duration: 200 + Math.random() * 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(dropGraphics, {
scaleY: dropGraphics.scaleY - (Math.random() - 0.5) * 0.1
}, {
duration: 200 + Math.random() * 300,
easing: tween.easeInOut
});
}
});
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 off 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 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);
// 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;
// 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;
// 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 weather button
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']; // 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;
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;
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;
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
rainIntensity = Math.min(16, 6 + Math.floor(score / 10)); // Double the intensity - more gradual increase, higher max intensity
// 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;
// Optimize road segment iteration for better performance
for (var s = 0; s < roadSegments.length; s++) {
var segment = roadSegments[s];
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 - optimized
if (LK.ticks % 5 === 0 && dustParticles.length < 30) {
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;
// Reduce tween frequency for performance
if (Math.random() < 0.4) {
// Only animate 40% of dust particles
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 - optimized
if (LK.ticks % 4 === 0 && dustParticles.length < 40) {
// 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 - optimized
if (LK.ticks % 6 === 0 && dustParticles.length < 35) {
// 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 - optimized
if (LK.ticks % 8 === 0 && dustParticles.length < 25) {
// 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);
}
// Optimize particle cleanup with reverse iteration
for (var p = particles.length - 1; p >= 0; p--) {
particles[p].tick();
if (particles[p].lifetime <= 0) {
particles.splice(p, 1);
}
}
for (var d = dustParticles.length - 1; d >= 0; d--) {
dustParticles[d].update();
if (dustParticles[d].alpha <= 0 || dustParticles[d].lifetime <= 0) {
dustParticles.splice(d, 1);
}
}
// Create rain drops continuously throughout the entire level - optimized
if (currentWeather !== 'Clear') {
var stormMultiplier = currentWeather === 'Storm' ? 2 : 1; // Reduced from 3x to 2x
var adjustedRainIntensity = Math.min(12, rainIntensity * stormMultiplier); // Cap max intensity
var rainSpawnRate = Math.max(2, Math.floor(12 / adjustedRainIntensity)); // Less frequent spawning
if (LK.ticks % rainSpawnRate === 0 && rainDrops.length < 50) {
// Limit max raindrops
for (var r = 0; r < Math.min(3, adjustedRainIntensity); r++) {
// Cap spawns per tick
var rainDrop = new RainDrop();
// Calculate current camera offset to ensure rain covers visible area
var cameraOffsetX = -mainContainer.x;
var cameraOffsetY = -mainContainer.y;
// Reduced spawn area for better performance
var spawnWidth = 2560; // Reduced from 4096
var spawnMargin = 512; // Reduced from 1024
rainDrop.x = cameraOffsetX - spawnMargin + Math.random() * (spawnWidth + 2 * spawnMargin);
rainDrop.y = cameraOffsetY - 400 - Math.random() * 600;
// Add slight opacity variation for realism
rainDrop.alpha = 0.4 + Math.random() * 0.4;
// Reduced tween frequency for performance
if (Math.random() < 0.3) {
// Only animate 30% of raindrops
tween(rainDrop, {
rotation: rainDrop.rotation + (Math.random() - 0.5) * 0.4
}, {
duration: 800 + Math.random() * 1500,
easing: tween.linear
});
}
mainContainer.addChild(rainDrop);
rainDrops.push(rainDrop);
}
}
}
// Update and clean up rain drops - optimized
for (var rd = rainDrops.length - 1; rd >= 0; rd--) {
rainDrops[rd].update();
if (rainDrops[rd].lifetime <= 0 || rainDrops[rd].alpha <= 0) {
rainDrops.splice(rd, 1);
}
}
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();
}
}
});