/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Boost class var Boost = Container.expand(function () { var self = Container.call(this); var boostAsset = self.attachAsset('boost', { anchorX: 0.5, anchorY: 0.5 }); self.width = boostAsset.width; self.height = boostAsset.height; self.update = function () { self.y += roadSpeed; }; return self; }); // Car class var Car = Container.expand(function () { var self = Container.call(this); var carAsset = self.attachAsset('car', { anchorX: 0.5, anchorY: 0.5 }); // For collision, use the container itself self.width = carAsset.width; self.height = carAsset.height; // Car speed (pixels per tick) self.speed = 10; // Lateral speed (for smooth steering) self.lateralSpeed = 0; // Max lateral speed self.maxLateralSpeed = 32; // Steering target (x position) self.targetX = self.x; // Update method self.update = function () { // Smoothly move towards targetX var dx = self.targetX - self.x; if (Math.abs(dx) > 2) { self.lateralSpeed = Math.max(-self.maxLateralSpeed, Math.min(self.maxLateralSpeed, dx * 0.25)); self.x += self.lateralSpeed; } else { self.lateralSpeed = 0; self.x = self.targetX; } }; return self; }); // Lane marker class var Lane = Container.expand(function () { var self = Container.call(this); var laneAsset = self.attachAsset('lane', { anchorX: 0.5, anchorY: 0.5 }); self.width = laneAsset.width; self.height = laneAsset.height; self.update = function () { self.y += roadSpeed; }; return self; }); // Nitro class (collectible for nitro boost) var Nitro = Container.expand(function () { var self = Container.call(this); var nitroAsset = self.attachAsset('Nitro', { anchorX: 0.5, anchorY: 0.5 }); self.width = nitroAsset.width; self.height = nitroAsset.height; self.update = function () { self.y += roadSpeed; }; return self; }); // Obstacle class (default car) var Obstacle = Container.expand(function () { var self = Container.call(this); var obsAsset = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5 }); self.width = obsAsset.width; self.height = obsAsset.height; // Update method: move down by road speed self.update = function () { // Track lastY for scoring if (self.lastY === undefined) self.lastY = self.y; self.y += roadSpeed; // No score for passing obstacles; scoring is handled by collecting boosts. self.lastY = self.y; }; return self; }); // Motorcycle obstacle var ObstacleMotorcycle = Container.expand(function () { var self = Container.call(this); var obsAsset = self.attachAsset('obstacle_motorcycle', { anchorX: 0.5, anchorY: 0.5 }); self.width = obsAsset.width; self.height = obsAsset.height; self.update = function () { if (self.lastY === undefined) self.lastY = self.y; self.y += roadSpeed; self.lastY = self.y; }; return self; }); // Sports car obstacle var ObstacleSportsCar = Container.expand(function () { var self = Container.call(this); var obsAsset = self.attachAsset('obstacle_sportscar', { anchorX: 0.5, anchorY: 0.5 }); self.width = obsAsset.width; self.height = obsAsset.height; self.update = function () { if (self.lastY === undefined) self.lastY = self.y; self.y += roadSpeed; self.lastY = self.y; }; return self; }); // Truck obstacle var ObstacleTruck = Container.expand(function () { var self = Container.call(this); var obsAsset = self.attachAsset('obstacle_truck', { anchorX: 0.5, anchorY: 0.5 }); self.width = obsAsset.width; self.height = obsAsset.height; self.update = function () { if (self.lastY === undefined) self.lastY = self.y; self.y += roadSpeed; self.lastY = self.y; }; return self; }); // Van obstacle var ObstacleVan = Container.expand(function () { var self = Container.call(this); var obsAsset = self.attachAsset('obstacle_van', { anchorX: 0.5, anchorY: 0.5 }); self.width = obsAsset.width; self.height = obsAsset.height; self.update = function () { if (self.lastY === undefined) self.lastY = self.y; self.y += roadSpeed; self.lastY = self.y; }; return self; }); // Police Car class var PolisCar = Container.expand(function () { var self = Container.call(this); var polisAsset = self.attachAsset('Polisecar', { anchorX: 0.5, anchorY: 0.5 }); self.width = polisAsset.width; self.height = polisAsset.height; // Police car speed (moves down the road) self.update = function () { if (self.lastY === undefined) self.lastY = self.y; self.y += roadSpeed; self.lastY = self.y; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222222 }); /**** * Game Code ****/ // Play start sound at the very beginning of the game // Car (player) // Obstacle // Speed boost // Road // Lane marker LK.getSound('Arabasssw').play(); LK.stopMusic(); // Stop any background music if playing LK.playMusic('Aeabasec'); // Play only Aeabasec music on car selection // Game constants var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var ROAD_WIDTH = 900; var ROAD_X = (GAME_WIDTH - ROAD_WIDTH) / 2; var ROAD_Y = 0; var LANE_COUNT = 3; var LANE_WIDTH = ROAD_WIDTH / LANE_COUNT; var CAR_START_X = GAME_WIDTH / 2; var CAR_START_Y = GAME_HEIGHT - 500; var OBSTACLE_MIN_GAP = 600; var OBSTACLE_MAX_GAP = 1100; var BOOST_CHANCE = 0.25; // 25% chance to spawn a boost with an obstacle // Road speed (pixels per tick) var roadSpeed = 10; var roadSpeedBase = 10; var roadSpeedBoosted = 16; var boostDuration = 90; // ticks (1.5 seconds) var boostTicks = 0; // Score var score = 0; // Boost collected counter var boostCollectedCount = 0; // Score text var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Boost collected text var boostCollectedTxt = new Text2('$ 0', { size: 80, fill: 0x00FF00 }); boostCollectedTxt.anchor.set(0.5, 0); // Position boostCollectedTxt below the score, always centered and with enough margin boostCollectedTxt.x = LK.gui.width / 2 - 220; boostCollectedTxt.y = scoreTxt.y + scoreTxt.height + 10; LK.gui.top.addChild(boostCollectedTxt); // Re-align on resize to keep it visible and centered LK.on('resize', function () { boostCollectedTxt.x = LK.gui.width / 2 - 220; boostCollectedTxt.y = scoreTxt.y + scoreTxt.height + 10; distanceText.x = LK.gui.width / 2 - 220; }); function updateBoostCollectedTxt() { boostCollectedTxt.setText('$ ' + boostCollectedCount); } // Arrays for game objects var obstacles = []; var boosts = []; var lanes = []; var nitros = []; // Nitro collectibles // Track how many times the player passes close to a truck obstacle var truckPassCount = 0; // Nitro state var nitroCount = 0; var nitroActive = false; var nitroTicks = 0; // Weather effect globals var weatherParticles = []; var weatherOverlay = undefined; // --- Thunder sound timer for rainy weather --- var gokgurultuTimer = null; function startGokgurultuTimer() { if (gokgurultuTimer) LK.clearInterval(gokgurultuTimer); gokgurultuTimer = LK.setInterval(function () { // Only play if rainy weather is active and car selection overlay is hidden if (typeof selectedWeather !== "undefined" && selectedWeather === "rainy" && carSelectOverlay && carSelectOverlay.visible === false) { LK.getSound('Gokgurultu').play(); } }, 5000); } function stopGokgurultuTimer() { if (gokgurultuTimer) { LK.clearInterval(gokgurultuTimer); gokgurultuTimer = null; } } // Road background var road = LK.getAsset('road', { anchorX: 0, anchorY: 0, x: ROAD_X, y: ROAD_Y }); // Score text var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Distance traveled (meters) var distanceTraveled = 0; // Distance text var distanceText = new Text2('0 m', { size: 80, fill: 0xFFD700 }); distanceText.anchor.set(0.5, 0); // Place at the very top center, with a small margin from the top distanceText.x = LK.gui.width / 2 - 220; distanceText.y = 0; // absolutely at the top // Add distanceText as the first child so it appears above the score LK.gui.top.addChild(distanceText); // Nitro GUI icon and counter (must be after scoreTxt is created) var nitroIcon = LK.getAsset('Nitro', { anchorX: 0, anchorY: 0, width: 90, height: 90 }); nitroIcon.x = LK.gui.width / 2 + scoreTxt.width / 2 + 350; nitroIcon.y = 10; LK.gui.top.addChild(nitroIcon); var nitroLabel = new Text2('x' + nitroCount, { size: 80, fill: 0x00EAFF }); nitroLabel.anchor.set(0, 0); nitroLabel.x = nitroIcon.x + nitroIcon.width + 10; nitroLabel.y = nitroIcon.y + 10; LK.gui.top.addChild(nitroLabel); function updateNitroLabel() { nitroLabel.setText('x' + nitroCount); } // (El freni (brake) button and logic removed) game.addChild(road); // Lane markers function spawnLaneMarkers() { // Remove old lanes for (var i = 0; i < lanes.length; i++) { lanes[i].destroy(); } lanes = []; // For each lane (except the leftmost), draw a marker for (var l = 1; l < LANE_COUNT; l++) { for (var y = -200; y < GAME_HEIGHT + 200; y += 400) { var lane = new Lane(); lane.x = ROAD_X + l * LANE_WIDTH; lane.y = y; lanes.push(lane); game.addChild(lane); } } } spawnLaneMarkers(); // --- Car Selection Screen Implementation --- // Available car options (asset id, display name) var carOptions = [{ id: 'car', name: 'Klasik' }, { id: 'obstacle_sportscar', name: 'Spor' }, { id: 'obstacle_truck', name: 'Motosiklet' }, // Yeni arabalar eklendi { id: 'obstacle_motorcycle', name: 'Lüks Araba' }]; // Weather options var weatherOptions = [{ id: 'rainy', name: 'Yağmurlu' }, { id: 'snowy', name: 'Karlı' }, { id: 'sunny', name: 'Güneşli' }]; var selectedWeatherIndex = 0; // Selected car index (default 0) var selectedCarIndex = 0; // Car selection overlay container var carSelectOverlay = new Container(); carSelectOverlay.zIndex = 10000; // Always on top game.addChild(carSelectOverlay); // Animated background for car selection overlay var overlayBg = LK.getAsset('road', { anchorX: 0, anchorY: 0, width: GAME_WIDTH, height: GAME_HEIGHT }); overlayBg.alpha = 0.85; overlayBg.tint = 0x000000; carSelectOverlay.addChild(overlayBg); // Add a second, lighter overlay for animation effect var overlayAnim = LK.getAsset('road', { anchorX: 0, anchorY: 0, width: GAME_WIDTH, height: GAME_HEIGHT }); overlayAnim.alpha = 0.18; overlayAnim.tint = 0x00EAFF; carSelectOverlay.addChild(overlayAnim); // Animate the overlayAnim's alpha and tint for a lively effect function animateOverlayBg() { tween(overlayAnim, { alpha: 0.32 }, { duration: 1200, easing: tween.sineInOut, onFinish: function onFinish() { tween(overlayAnim, { alpha: 0.18 }, { duration: 1200, easing: tween.sineInOut, onFinish: animateOverlayBg }); } }); // Animate tint between two colors for a subtle color shift var tints = [0x00EAFF, 0xFFD700, 0x00EAFF]; var idx = 0; function nextTint() { idx = (idx + 1) % tints.length; tween(overlayAnim, { tint: tints[idx] }, { duration: 1800, easing: tween.linear, onFinish: nextTint }); } nextTint(); } animateOverlayBg(); // Title var selectTitle = new Text2('Araba Seç', { size: 180, fill: 0xFFD700 }); selectTitle.anchor.set(0.5, 0); selectTitle.x = GAME_WIDTH / 2; selectTitle.y = 120; carSelectOverlay.addChild(selectTitle); // Car preview asset var carPreview = LK.getAsset(carOptions[selectedCarIndex].id, { anchorX: 0.5, anchorY: 0.5, width: 420, height: 270 }); carPreview.x = GAME_WIDTH / 2; carPreview.y = 700; carSelectOverlay.addChild(carPreview); // Car name label var carNameLabel = new Text2(carOptions[selectedCarIndex].name, { size: 120, fill: "#fff" }); carNameLabel.anchor.set(0.5, 0); carNameLabel.x = GAME_WIDTH / 2; carNameLabel.y = carPreview.y + carPreview.height / 2 + 40; carSelectOverlay.addChild(carNameLabel); // Weather selection UI var weatherTitle = new Text2('Hava Durumu', { size: 100, fill: 0xFFD700 }); weatherTitle.anchor.set(0.5, 0); weatherTitle.x = GAME_WIDTH / 2; weatherTitle.y = carNameLabel.y + 180; carSelectOverlay.addChild(weatherTitle); var weatherLabel = new Text2(weatherOptions[selectedWeatherIndex].name, { size: 100, fill: "#fff" }); weatherLabel.anchor.set(0.5, 0); weatherLabel.x = GAME_WIDTH / 2; weatherLabel.y = weatherTitle.y + 120; carSelectOverlay.addChild(weatherLabel); var leftWeatherArrow = LK.getAsset('lane', { anchorX: 0.5, anchorY: 0.5, width: 60, height: 120 }); leftWeatherArrow.x = weatherLabel.x - 250; leftWeatherArrow.y = weatherLabel.y + 60; leftWeatherArrow.rotation = Math.PI; leftWeatherArrow.tint = 0xFFD700; carSelectOverlay.addChild(leftWeatherArrow); var rightWeatherArrow = LK.getAsset('lane', { anchorX: 0.5, anchorY: 0.5, width: 60, height: 120 }); rightWeatherArrow.x = weatherLabel.x + 250; rightWeatherArrow.y = weatherLabel.y + 60; rightWeatherArrow.tint = 0xFFD700; carSelectOverlay.addChild(rightWeatherArrow); leftWeatherArrow.interactive = true; rightWeatherArrow.interactive = true; // Helper to clear weather preview particles/overlay from car selection overlay function clearWeatherPreview() { if (typeof weatherPreviewOverlay !== "undefined" && weatherPreviewOverlay && weatherPreviewOverlay.parent) { weatherPreviewOverlay.parent.removeChild(weatherPreviewOverlay); weatherPreviewOverlay = undefined; } if (typeof weatherPreviewParticles !== "undefined" && Array.isArray(weatherPreviewParticles)) { for (var wp = 0; wp < weatherPreviewParticles.length; wp++) { if (weatherPreviewParticles[wp].parent) weatherPreviewParticles[wp].parent.removeChild(weatherPreviewParticles[wp]); } weatherPreviewParticles = []; } } // Helper to show weather preview on car selection overlay function showWeatherPreview() { clearWeatherPreview(); var wx = weatherOptions[selectedWeatherIndex].id; weatherPreviewParticles = []; if (wx === "rainy") { // Blue transparent overlay for rain weatherPreviewOverlay = LK.getAsset('Background', { anchorX: 0, anchorY: 0, width: GAME_WIDTH, height: GAME_HEIGHT }); weatherPreviewOverlay.alpha = 0.18; weatherPreviewOverlay.tint = 0x00aaff; carSelectOverlay.addChildAt(weatherPreviewOverlay, 1); // Animated rain particles var rainCount = 24; for (var r = 0; r < rainCount; r++) { var drop = LK.getAsset('lane', { anchorX: 0.5, anchorY: 0, width: 8, height: 60 }); drop.alpha = 0.32 + Math.random() * 0.18; drop.tint = 0x00bfff; drop.x = ROAD_X + Math.random() * ROAD_WIDTH; drop.y = Math.random() * GAME_HEIGHT; drop._rainSpeed = 18 + Math.random() * 10; drop._rainDrift = (Math.random() - 0.5) * 4; carSelectOverlay.addChild(drop); weatherPreviewParticles.push(drop); } // Play rain sound (no loop) for preview if (!carSelectOverlay._rainSoundPlaying) { LK.getSound('Yagmursesi').play(); carSelectOverlay._rainSoundPlaying = true; } // Stop snow music if it was playing in preview if (carSelectOverlay._snowMusicPlaying) { LK.getSound('Karlihava').stop(); carSelectOverlay._snowMusicPlaying = false; } } else { // Stop rain sound if it was playing in preview if (carSelectOverlay._rainSoundPlaying) { LK.getSound('Yagmursesi').stop(); carSelectOverlay._rainSoundPlaying = false; } // Stop snow music if it was playing in preview if (carSelectOverlay._snowMusicPlaying) { LK.getSound('Karlihava').stop(); carSelectOverlay._snowMusicPlaying = false; } } if (wx === "snowy") { // White transparent overlay for snow weatherPreviewOverlay = LK.getAsset('Background', { anchorX: 0, anchorY: 0, width: GAME_WIDTH, height: GAME_HEIGHT }); weatherPreviewOverlay.alpha = 0.22; weatherPreviewOverlay.tint = 0xffffff; carSelectOverlay.addChildAt(weatherPreviewOverlay, 1); // Animated snow particles var snowCount = 18; for (var s = 0; s < snowCount; s++) { var flake = LK.getAsset('Background', { anchorX: 0.5, anchorY: 0.5, width: 18 + Math.random() * 18, height: 18 + Math.random() * 18 }); flake.alpha = 0.18 + Math.random() * 0.18; flake.tint = 0xffffff; flake.x = ROAD_X + Math.random() * ROAD_WIDTH; flake.y = Math.random() * GAME_HEIGHT; flake._snowSpeed = 3 + Math.random() * 4; flake._snowDrift = (Math.random() - 0.5) * 1.5; flake._snowAngle = Math.random() * Math.PI * 2; carSelectOverlay.addChild(flake); weatherPreviewParticles.push(flake); } // Play snow music (no loop) for preview if (!carSelectOverlay._snowMusicPlaying) { LK.getSound('Karlihava').play(); carSelectOverlay._snowMusicPlaying = true; } } // For sunny, nothing to add (clear) } // Animate weather preview particles on car selection overlay function animateWeatherPreview() { if (carSelectOverlay.visible && typeof weatherPreviewParticles !== "undefined" && Array.isArray(weatherPreviewParticles) && weatherPreviewParticles.length > 0) { var wx = weatherOptions[selectedWeatherIndex].id; for (var wp = 0; wp < weatherPreviewParticles.length; wp++) { var p = weatherPreviewParticles[wp]; if (wx === "rainy") { p.y += p._rainSpeed; p.x += p._rainDrift; if (p.y > GAME_HEIGHT + 40) { p.y = -60; p.x = ROAD_X + Math.random() * ROAD_WIDTH; } if (p.x < ROAD_X) p.x = ROAD_X + ROAD_WIDTH - 10; if (p.x > ROAD_X + ROAD_WIDTH) p.x = ROAD_X + 10; } else if (wx === "snowy") { p.y += p._snowSpeed; p.x += Math.sin(p._snowAngle) * p._snowDrift; p._snowAngle += 0.02 + Math.random() * 0.01; if (p.y > GAME_HEIGHT + 30) { p.y = -20; p.x = ROAD_X + Math.random() * ROAD_WIDTH; } if (p.x < ROAD_X) p.x = ROAD_X + ROAD_WIDTH - 10; if (p.x > ROAD_X + ROAD_WIDTH) p.x = ROAD_X + 10; } } } LK.setTimeout(animateWeatherPreview, 33); // ~30fps } animateWeatherPreview(); leftWeatherArrow.down = function (x, y, obj) { selectedWeatherIndex = (selectedWeatherIndex - 1 + weatherOptions.length) % weatherOptions.length; weatherLabel.setText(weatherOptions[selectedWeatherIndex].name); showWeatherPreview(); }; rightWeatherArrow.down = function (x, y, obj) { selectedWeatherIndex = (selectedWeatherIndex + 1) % weatherOptions.length; weatherLabel.setText(weatherOptions[selectedWeatherIndex].name); showWeatherPreview(); }; // Show initial weather preview showWeatherPreview(); // Left/right arrow assets var leftArrow = LK.getAsset('lane', { anchorX: 0.5, anchorY: 0.5, width: 80, height: 180 }); leftArrow.x = carPreview.x - 350; leftArrow.y = carPreview.y; leftArrow.rotation = Math.PI; // Point left leftArrow.tint = 0xFFD700; carSelectOverlay.addChild(leftArrow); var rightArrow = LK.getAsset('lane', { anchorX: 0.5, anchorY: 0.5, width: 80, height: 180 }); rightArrow.x = carPreview.x + 350; rightArrow.y = carPreview.y; rightArrow.tint = 0xFFD700; carSelectOverlay.addChild(rightArrow); // Start button as asset var startBtn = LK.getAsset('Start', { anchorX: 0.5, anchorY: 0.5, width: 400, height: 140 }); startBtn.x = GAME_WIDTH / 2; startBtn.y = weatherLabel.y + 220; carSelectOverlay.addChild(startBtn); // Dance animation for startBtn asset function danceStartBtn() { // Scale up and rotate right tween(startBtn, { scaleX: 1.18, scaleY: 0.88, rotation: 0.18 }, { duration: 180, easing: tween.sineInOut, onFinish: function onFinish() { // Scale down and rotate left tween(startBtn, { scaleX: 0.88, scaleY: 1.18, rotation: -0.18 }, { duration: 180, easing: tween.sineInOut, onFinish: function onFinish() { // Return to normal tween(startBtn, { scaleX: 1, scaleY: 1, rotation: 0 }, { duration: 180, easing: tween.sineInOut, onFinish: danceStartBtn }); } }); } }); } danceStartBtn(); // Arrow touch logic leftArrow.interactive = true; rightArrow.interactive = true; startBtn.interactive = true; leftArrow.down = function (x, y, obj) { selectedCarIndex = (selectedCarIndex - 1 + carOptions.length) % carOptions.length; carSelectOverlay.removeChild(carPreview); carPreview = LK.getAsset(carOptions[selectedCarIndex].id, { anchorX: 0.5, anchorY: 0.5, width: 420, height: 270 }); carPreview.x = GAME_WIDTH / 2; carPreview.y = 700; carSelectOverlay.addChildAt(carPreview, 2); carNameLabel.setText(carOptions[selectedCarIndex].name); }; rightArrow.down = function (x, y, obj) { selectedCarIndex = (selectedCarIndex + 1) % carOptions.length; carSelectOverlay.removeChild(carPreview); carPreview = LK.getAsset(carOptions[selectedCarIndex].id, { anchorX: 0.5, anchorY: 0.5, width: 420, height: 270 }); carPreview.x = GAME_WIDTH / 2; carPreview.y = 700; carSelectOverlay.addChildAt(carPreview, 2); carNameLabel.setText(carOptions[selectedCarIndex].name); }; // Start button logic var car; // Declare car globally for later use var selectedWeather = weatherOptions[selectedWeatherIndex].id; startBtn.down = function (x, y, obj) { carSelectOverlay.visible = false; LK.stopMusic(); LK.playMusic('Music'); car = new Car(); car.removeChildAt(0); var carAsset = car.attachAsset(carOptions[selectedCarIndex].id, { anchorX: 0.5, anchorY: 0.5 }); car.width = carAsset.width; car.height = carAsset.height; car.x = CAR_START_X; car.y = CAR_START_Y; car.targetX = car.x; game.addChild(car); // Save selected weather for gameplay selectedWeather = weatherOptions[selectedWeatherIndex].id; }; // Show overlay at start, hide game controls until car is picked carSelectOverlay.visible = true; // LK.playMusic('Aeabasec'); // Already handled above, no need to call again // --- End Car Selection Screen --- // Score text var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Lives (hak) system var lives = 3; // Image-based lives display var livesImages = []; var LIVES_IMAGE_ID = 'car'; // Use car image for lives var LIVES_IMAGE_SIZE = 90; // px var LIVES_IMAGE_MARGIN = 20; // px between images // Positioning: Place lives images to the right of the score counter var LIVES_IMAGE_TOP_MARGIN = 10; // px from top of GUI var LIVES_IMAGE_LEFT_MARGIN = 40; // px from left of score counter function updateLivesImages() { // Remove old images for (var i = 0; i < livesImages.length; i++) { livesImages[i].destroy(); } livesImages = []; // Place lives images to the right of the score counter // Get scoreTxt position in GUI coordinates var scoreX = scoreTxt.x * LK.gui.width; var scoreY = scoreTxt.y * LK.gui.height; // But since anchor is (0.5, 0), scoreTxt.x is centered in GUI width var baseX = LK.gui.width / 2 + scoreTxt.width / 2 + LIVES_IMAGE_LEFT_MARGIN; var baseY = LIVES_IMAGE_TOP_MARGIN; for (var i = 0; i < lives; i++) { var img = LK.getAsset(LIVES_IMAGE_ID, { anchorX: 0, anchorY: 0, width: LIVES_IMAGE_SIZE, height: LIVES_IMAGE_SIZE }); // Position: right of score, stacked horizontally with margin img.x = baseX + i * (LIVES_IMAGE_SIZE + LIVES_IMAGE_MARGIN); img.y = baseY; LK.gui.top.addChild(img); livesImages.push(img); } } // Helper to update lives text (legacy, keep for compatibility) function updateLivesText() { updateLivesImages(); if (typeof livesLabel !== "undefined") { livesLabel.setText('Can: ' + lives); } } updateLivesImages(); // Add a text label to show remaining lives (can/hak) on the GUI, next to the last life image var livesLabel = new Text2('Can: ' + lives, { size: 80, fill: "#fff" }); livesLabel.anchor.set(0, 0); // left-top // Initial position: right of the last life image, will be updated on resize and updateLivesImages function updateLivesLabelPosition() { if (livesImages.length > 0) { var lastImg = livesImages[livesImages.length - 1]; livesLabel.x = lastImg.x + lastImg.width + 20; livesLabel.y = lastImg.y + 10; } else { // Fallback: right of score livesLabel.x = LK.gui.width / 2 + scoreTxt.width / 2 + LIVES_IMAGE_LEFT_MARGIN; livesLabel.y = LIVES_IMAGE_TOP_MARGIN + 10; } } LK.gui.top.addChild(livesLabel); updateLivesLabelPosition(); // Ensure lives images and label are always visible on resize LK.on('resize', function () { updateLivesImages(); updateLivesLabelPosition(); }); // Helper: get lane center x function getLaneCenter(laneIdx) { return ROAD_X + LANE_WIDTH * (laneIdx + 0.5); } // Helper: spawn an obstacle (and maybe a boost) function spawnObstacle() { // Random lane var laneIdx = Math.floor(Math.random() * LANE_COUNT); // Randomly select one of five obstacle types (NO police car here) var type = Math.floor(Math.random() * 5); var obs; if (type === 0) { obs = new Obstacle(); // default car } else if (type === 1) { obs = new ObstacleTruck(); } else if (type === 2) { obs = new ObstacleMotorcycle(); } else if (type === 3) { obs = new ObstacleSportsCar(); } else if (type === 4) { obs = new ObstacleVan(); } obs.x = getLaneCenter(laneIdx); obs.y = -obs.height / 2; obstacles.push(obs); game.addChild(obs); // Maybe spawn a boost in a different lane if (Math.random() < BOOST_CHANCE) { var boostLane = laneIdx; // Try to pick a different lane if (LANE_COUNT > 1) { while (boostLane === laneIdx) { boostLane = Math.floor(Math.random() * LANE_COUNT); } } var boost = new Boost(); boost.x = getLaneCenter(boostLane); boost.y = obs.y; boosts.push(boost); game.addChild(boost); } // Maybe spawn a nitro in a random lane (10% chance) if (Math.random() < 0.10) { var nitroLane = laneIdx; if (LANE_COUNT > 1) { while (nitroLane === laneIdx) { nitroLane = Math.floor(Math.random() * LANE_COUNT); } } var nitro = new Nitro(); nitro.x = getLaneCenter(nitroLane); nitro.y = obs.y; nitros.push(nitro); game.addChild(nitro); } } // Timer for spawning police car every 10 seconds var polisCarTimer = LK.setInterval(function () { // Spawn police car in a random lane var laneIdx = Math.floor(Math.random() * LANE_COUNT); var polis = new PolisCar(); polis.x = getLaneCenter(laneIdx); polis.y = -polis.height / 2; obstacles.push(polis); game.addChild(polis); }, 10000); // Helper: spawn lane markers as they scroll function updateLaneMarkers() { for (var i = lanes.length - 1; i >= 0; i--) { var lane = lanes[i]; lane.update(); if (lane.y > GAME_HEIGHT + 200) { // Recycle to top lane.y -= GAME_HEIGHT + 400; } } } // Dragging var dragging = false; var dragOffsetX = 0; // Touch/mouse controls game.down = function (x, y, obj) { // (El freni (brake) button logic removed) // Nitro activation: if touch is on nitro icon, activate nitro if available if (x >= nitroIcon.x && x <= nitroIcon.x + nitroIcon.width && y >= nitroIcon.y && y <= nitroIcon.y + nitroIcon.height) { if (nitroCount > 0 && !nitroActive) { nitroCount--; updateNitroLabel(); nitroActive = true; nitroTicks = NITRO_DURATION; roadSpeed = roadSpeedBoosted; LK.effects.flashObject(car, 0x00eaff, 800); // Optionally, flash screen blue for effect LK.effects.flashScreen(0x00eaff, 300); } return; } // Only allow drag if car is selected and touch is on car or on road if (car && x > ROAD_X && x < ROAD_X + ROAD_WIDTH && y > 0 && y < GAME_HEIGHT) { dragging = true; dragOffsetX = x - car.x; // Set targetX immediately car.targetX = Math.max(ROAD_X + car.width / 2, Math.min(ROAD_X + ROAD_WIDTH - car.width / 2, x - dragOffsetX)); } }; game.up = function (x, y, obj) { dragging = false; // (El freni (brake) button logic removed) }; game.move = function (x, y, obj) { if (dragging && car) { // Clamp to road var tx = Math.max(ROAD_X + car.width / 2, Math.min(ROAD_X + ROAD_WIDTH - car.width / 2, x - dragOffsetX)); car.targetX = tx; } }; // Game update var lastObstacleY = -OBSTACLE_MIN_GAP; game.update = function () { // (El freni (brake) effect removed) if (!nitroActive) { roadSpeed = roadSpeedBase; } // Update car (only if selected) if (car) car.update(); // Weather effect (visual only, can be expanded for gameplay effects) if (typeof selectedWeather !== "undefined" && car && carSelectOverlay.visible === false) { // --- Rain and snow sound/music logic --- if (selectedWeather === "rainy") { if (!game._rainSoundPlaying) { LK.getSound('Yagmursesi').play(); game._rainSoundPlaying = true; } // Start thunder timer if not already running and overlay is hidden if (carSelectOverlay && carSelectOverlay.visible === false && !gokgurultuTimer) { startGokgurultuTimer(); } // Stop snow music if it was playing if (game._snowMusicPlaying) { LK.getSound('Karlihava').stop(); game._snowMusicPlaying = false; } } else if (selectedWeather === "snowy") { stopGokgurultuTimer(); if (game._gokgurultuPlayed) game._gokgurultuPlayed = false; // Stop rain sound if it was playing if (game._rainSoundPlaying) { LK.getSound('Yagmursesi').stop(); game._rainSoundPlaying = false; } // Play snow music if not already playing if (!game._snowMusicPlaying) { LK.getSound('Karlihava').play(); game._snowMusicPlaying = true; } } else { stopGokgurultuTimer(); if (game._rainSoundPlaying) { LK.getSound('Yagmursesi').stop(); game._rainSoundPlaying = false; } if (game._snowMusicPlaying) { LK.getSound('Karlihava').stop(); game._snowMusicPlaying = false; } } // Remove any previous weather overlays and particles if (typeof weatherOverlay !== "undefined" && weatherOverlay.parent) { weatherOverlay.parent.removeChild(weatherOverlay); weatherOverlay = undefined; } if (typeof weatherParticles !== "undefined" && Array.isArray(weatherParticles)) { for (var wp = 0; wp < weatherParticles.length; wp++) { if (weatherParticles[wp].parent) weatherParticles[wp].parent.removeChild(weatherParticles[wp]); } weatherParticles = []; } // Add weather overlay and animated particles if needed if (selectedWeather === "rainy") { // Blue transparent overlay for rain weatherOverlay = LK.getAsset('Background', { anchorX: 0, anchorY: 0, width: GAME_WIDTH, height: GAME_HEIGHT }); weatherOverlay.alpha = 0.18; weatherOverlay.tint = 0x00aaff; game.addChild(weatherOverlay); if (game.children && game.children.length > 0) { game.setChildIndex(weatherOverlay, game.children.length - 1); } // Animated rain particles if (typeof weatherParticles === "undefined") weatherParticles = []; var rainCount = 32; for (var r = 0; r < rainCount; r++) { var drop = LK.getAsset('lane', { anchorX: 0.5, anchorY: 0, width: 8, height: 60 }); drop.alpha = 0.32 + Math.random() * 0.18; drop.tint = 0x00bfff; drop.x = ROAD_X + Math.random() * ROAD_WIDTH; drop.y = Math.random() * GAME_HEIGHT; drop._rainSpeed = 32 + Math.random() * 18; drop._rainDrift = (Math.random() - 0.5) * 6; game.addChild(drop); weatherParticles.push(drop); } } else if (selectedWeather === "snowy") { // White transparent overlay for snow weatherOverlay = LK.getAsset('Background', { anchorX: 0, anchorY: 0, width: GAME_WIDTH, height: GAME_HEIGHT }); weatherOverlay.alpha = 0.22; weatherOverlay.tint = 0xffffff; game.addChild(weatherOverlay); if (game.children && game.children.length > 0) { game.setChildIndex(weatherOverlay, game.children.length - 1); } // Animated snow particles if (typeof weatherParticles === "undefined") weatherParticles = []; var snowCount = 28; for (var s = 0; s < snowCount; s++) { var flake = LK.getAsset('Background', { anchorX: 0.5, anchorY: 0.5, width: 18 + Math.random() * 18, height: 18 + Math.random() * 18 }); flake.alpha = 0.18 + Math.random() * 0.18; flake.tint = 0xffffff; flake.x = ROAD_X + Math.random() * ROAD_WIDTH; flake.y = Math.random() * GAME_HEIGHT; flake._snowSpeed = 6 + Math.random() * 8; flake._snowDrift = (Math.random() - 0.5) * 2.5; flake._snowAngle = Math.random() * Math.PI * 2; game.addChild(flake); weatherParticles.push(flake); } } // For sunny, no overlay or particles needed if (selectedWeather === "sunny") { if (typeof weatherOverlay !== "undefined" && weatherOverlay.parent) { weatherOverlay.parent.removeChild(weatherOverlay); weatherOverlay = undefined; } if (typeof weatherParticles !== "undefined" && Array.isArray(weatherParticles)) { for (var wp = 0; wp < weatherParticles.length; wp++) { if (weatherParticles[wp].parent) weatherParticles[wp].parent.removeChild(weatherParticles[wp]); } weatherParticles = []; } } } else { // If overlay/particles exist but weather is not active, remove them if (typeof weatherOverlay !== "undefined" && weatherOverlay.parent) { weatherOverlay.parent.removeChild(weatherOverlay); weatherOverlay = undefined; } if (typeof weatherParticles !== "undefined" && Array.isArray(weatherParticles)) { for (var wp = 0; wp < weatherParticles.length; wp++) { if (weatherParticles[wp].parent) weatherParticles[wp].parent.removeChild(weatherParticles[wp]); } weatherParticles = []; } } // Animate weather particles if present if (typeof weatherParticles !== "undefined" && Array.isArray(weatherParticles) && weatherParticles.length > 0) { for (var wp = 0; wp < weatherParticles.length; wp++) { var p = weatherParticles[wp]; if (selectedWeather === "rainy") { p.y += p._rainSpeed; p.x += p._rainDrift; if (p.y > GAME_HEIGHT + 40) { p.y = -60; p.x = ROAD_X + Math.random() * ROAD_WIDTH; } if (p.x < ROAD_X) p.x = ROAD_X + ROAD_WIDTH - 10; if (p.x > ROAD_X + ROAD_WIDTH) p.x = ROAD_X + 10; } else if (selectedWeather === "snowy") { p.y += p._snowSpeed; p.x += Math.sin(p._snowAngle) * p._snowDrift; p._snowAngle += 0.02 + Math.random() * 0.01; if (p.y > GAME_HEIGHT + 30) { p.y = -20; p.x = ROAD_X + Math.random() * ROAD_WIDTH; } if (p.x < ROAD_X) p.x = ROAD_X + ROAD_WIDTH - 10; if (p.x > ROAD_X + ROAD_WIDTH) p.x = ROAD_X + 10; } } } // Update lane markers updateLaneMarkers(); // Update distance traveled (add roadSpeed per tick, convert to meters) distanceTraveled += roadSpeed; if (distanceText) { // Show as meters or km if (distanceTraveled < 1000) { distanceText.setText(Math.floor(distanceTraveled) + " m"); } else { distanceText.setText((distanceTraveled / 1000).toFixed(2) + " km"); } } // Update obstacles for (var i = obstacles.length - 1; i >= 0; i--) { var obs = obstacles[i]; obs.update(); // --- Play police sound when passing close to a police car --- if (obs instanceof PolisCar && car) { // Track lastY for police car if not already if (obs.lastYForSound === undefined) obs.lastYForSound = obs.y; // Define "passing by" as car's y is within 100px above or below the police car's y (center to center) var closeRange = 100; var wasFar = Math.abs(car.y - obs.lastYForSound) > closeRange; var isClose = Math.abs(car.y - obs.y) <= closeRange; // Only play sound on the frame we enter the close range if (wasFar && isClose) { LK.getSound('Polissound').play(); } obs.lastYForSound = obs.y; } // --- Play horn sound when passing close to a truck obstacle --- if (obs instanceof ObstacleTruck && car) { if (obs.lastYForHorn === undefined) obs.lastYForHorn = obs.y; var hornRange = 120; var wasFarHorn = Math.abs(car.y - obs.lastYForHorn) > hornRange; var isCloseHorn = Math.abs(car.y - obs.y) <= hornRange; if (wasFarHorn && isCloseHorn) { truckPassCount++; if (truckPassCount >= 6) { LK.getSound('Kornasesi').play(); truckPassCount = 0; } } obs.lastYForHorn = obs.y; } // Remove if off screen if (obs.y - obs.height / 2 > GAME_HEIGHT + 100) { obs.destroy(); obstacles.splice(i, 1); continue; } // Collision with car if (car && car.intersects(obs)) { // Play damage sound LK.getSound('Damage').play(); // Flash screen red LK.effects.flashScreen(0xff0000, 800); // Remove the obstacle so it doesn't trigger again obs.destroy(); obstacles.splice(i, 1); // Decrement lives lives--; updateLivesText(); if (lives <= 0) { LK.showGameOver(); return; } // If not game over, briefly flash car and continue LK.effects.flashObject(car, 0xff0000, 600); continue; } } // Update boosts for (var i = boosts.length - 1; i >= 0; i--) { var boost = boosts[i]; boost.update(); // Remove if off screen if (boost.y - boost.height / 2 > GAME_HEIGHT + 100) { boost.destroy(); boosts.splice(i, 1); continue; } // Collect boost if (car && car.intersects(boost)) { boost.destroy(); boosts.splice(i, 1); // Add score for collecting boost score += 100; scoreTxt.setText(score); // Increment boost collected counter and update GUI boostCollectedCount++; updateBoostCollectedTxt(); // Play coin sound LK.getSound('Coinsound').play(); // Flash car yellow LK.effects.flashObject(car, 0xffeb3b, 400); // Show green $ sign effect on car var dollarText = new Text2('$', { size: 180, fill: 0x00ff00 }); dollarText.anchor.set(0.5, 0.5); dollarText.x = car.x; dollarText.y = car.y - car.height / 2 - 40; game.addChild(dollarText); tween(dollarText, { y: dollarText.y - 120, alpha: 0 }, { duration: 700, easing: tween.sineInOut, onFinish: function onFinish() { if (dollarText.parent) dollarText.parent.removeChild(dollarText); } }); } } // Update nitros for (var i = nitros.length - 1; i >= 0; i--) { var nitro = nitros[i]; nitro.update(); // Remove if off screen if (nitro.y - nitro.height / 2 > GAME_HEIGHT + 100) { nitro.destroy(); nitros.splice(i, 1); continue; } // Collect nitro if (car && car.intersects(nitro)) { nitro.destroy(); nitros.splice(i, 1); nitroCount++; updateNitroLabel(); // Play nitro sound LK.getSound('Nitrosound').play(); // Flash car cyan LK.effects.flashObject(car, 0x00eaff, 400); // Activate nitro immediately for 4 seconds (240 ticks at 60fps) nitroActive = true; nitroTicks = 240; roadSpeed = roadSpeedBoosted; } } // Handle boost timer if (boostTicks > 0) { boostTicks--; if (boostTicks === 0) { roadSpeed = roadSpeedBase; } } // Handle nitro timer if (nitroActive) { nitroTicks--; if (nitroTicks <= 0) { nitroActive = false; roadSpeed = roadSpeedBase; } } // Spawn new obstacles if (obstacles.length === 0 || obstacles[obstacles.length - 1].y > OBSTACLE_MIN_GAP + Math.random() * (OBSTACLE_MAX_GAP - OBSTACLE_MIN_GAP)) { spawnObstacle(); } // Score is only updated by collecting boosts. // Check if car is off the road if (car && (car.x - car.width / 2 < ROAD_X || car.x + car.width / 2 > ROAD_X + ROAD_WIDTH)) { // Play damage sound before game over LK.getSound('Damage').play(); LK.effects.flashScreen(0xff0000, 800); // Decrement lives lives--; updateLivesText(); if (lives <= 0) { // Delay game over slightly to ensure sound plays LK.setTimeout(function () { LK.showGameOver(); }, 100); return; } // If not game over, flash car and move car back to road center LK.effects.flashObject(car, 0xff0000, 600); car.x = CAR_START_X; car.targetX = car.x; // Don't return, let the game continue } }; // Reset game state on new game LK.on('gameStart', function () { // Stop thunder timer on game start stopGokgurultuTimer(); // Clear and restart police car timer to avoid multiple timers if (typeof polisCarTimer !== "undefined") { LK.clearInterval(polisCarTimer); } polisCarTimer = LK.setInterval(function () { var laneIdx = Math.floor(Math.random() * LANE_COUNT); var polis = new PolisCar(); polis.x = getLaneCenter(laneIdx); polis.y = -polis.height / 2; obstacles.push(polis); game.addChild(polis); }, 10000); // Play start sound LK.getSound('Arabasssw').play(); LK.playMusic('Music'); game._gokgurultuPlayed = false; // Remove all obstacles and boosts for (var i = 0; i < obstacles.length; i++) obstacles[i].destroy(); for (var i = 0; i < boosts.length; i++) boosts[i].destroy(); obstacles = []; boosts = []; // Show car selection overlay again if (carSelectOverlay) { carSelectOverlay.visible = true; } // Reset weather selection to default (sunny) selectedWeatherIndex = 0; if (typeof weatherLabel !== "undefined") { weatherLabel.setText(weatherOptions[selectedWeatherIndex].name); } selectedWeather = weatherOptions[selectedWeatherIndex].id; // Remove weather overlay if exists if (typeof weatherOverlay !== "undefined" && weatherOverlay.parent) { weatherOverlay.parent.removeChild(weatherOverlay); weatherOverlay = undefined; } // Remove weather particles if exist if (typeof weatherParticles !== "undefined" && Array.isArray(weatherParticles)) { for (var wp = 0; wp < weatherParticles.length; wp++) { if (weatherParticles[wp].parent) weatherParticles[wp].parent.removeChild(weatherParticles[wp]); } weatherParticles = []; } // Remove car from game if exists if (car && car.parent) { car.parent.removeChild(car); } car = undefined; // Reset score and speed score = 0; scoreTxt.setText(score); roadSpeed = roadSpeedBase; boostTicks = 0; // Reset boost collected counter and GUI boostCollectedCount = 0; if (typeof updateBoostCollectedTxt === "function") updateBoostCollectedTxt(); // Reset truck pass count for horn truckPassCount = 0; // Stop rain sound if playing if (game._rainSoundPlaying) { LK.getSound('Yagmursesi').stop(); game._rainSoundPlaying = false; } // Stop snow music if playing if (game._snowMusicPlaying) { LK.getSound('Karlihava').stop(); game._snowMusicPlaying = false; } // Reset distance traveled distanceTraveled = 0; if (distanceText) { distanceText.setText("0 m"); } // Reset lane markers spawnLaneMarkers(); // Reset lives lives = 3; updateLivesText(); if (typeof livesLabel !== "undefined") { livesLabel.setText('Can: ' + lives); } // Reset nitro state nitroCount = 0; nitroActive = false; nitroTicks = 0; updateNitroLabel(); });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Boost class
var Boost = Container.expand(function () {
var self = Container.call(this);
var boostAsset = self.attachAsset('boost', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = boostAsset.width;
self.height = boostAsset.height;
self.update = function () {
self.y += roadSpeed;
};
return self;
});
// Car class
var Car = Container.expand(function () {
var self = Container.call(this);
var carAsset = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
// For collision, use the container itself
self.width = carAsset.width;
self.height = carAsset.height;
// Car speed (pixels per tick)
self.speed = 10;
// Lateral speed (for smooth steering)
self.lateralSpeed = 0;
// Max lateral speed
self.maxLateralSpeed = 32;
// Steering target (x position)
self.targetX = self.x;
// Update method
self.update = function () {
// Smoothly move towards targetX
var dx = self.targetX - self.x;
if (Math.abs(dx) > 2) {
self.lateralSpeed = Math.max(-self.maxLateralSpeed, Math.min(self.maxLateralSpeed, dx * 0.25));
self.x += self.lateralSpeed;
} else {
self.lateralSpeed = 0;
self.x = self.targetX;
}
};
return self;
});
// Lane marker class
var Lane = Container.expand(function () {
var self = Container.call(this);
var laneAsset = self.attachAsset('lane', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = laneAsset.width;
self.height = laneAsset.height;
self.update = function () {
self.y += roadSpeed;
};
return self;
});
// Nitro class (collectible for nitro boost)
var Nitro = Container.expand(function () {
var self = Container.call(this);
var nitroAsset = self.attachAsset('Nitro', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = nitroAsset.width;
self.height = nitroAsset.height;
self.update = function () {
self.y += roadSpeed;
};
return self;
});
// Obstacle class (default car)
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obsAsset = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obsAsset.width;
self.height = obsAsset.height;
// Update method: move down by road speed
self.update = function () {
// Track lastY for scoring
if (self.lastY === undefined) self.lastY = self.y;
self.y += roadSpeed;
// No score for passing obstacles; scoring is handled by collecting boosts.
self.lastY = self.y;
};
return self;
});
// Motorcycle obstacle
var ObstacleMotorcycle = Container.expand(function () {
var self = Container.call(this);
var obsAsset = self.attachAsset('obstacle_motorcycle', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obsAsset.width;
self.height = obsAsset.height;
self.update = function () {
if (self.lastY === undefined) self.lastY = self.y;
self.y += roadSpeed;
self.lastY = self.y;
};
return self;
});
// Sports car obstacle
var ObstacleSportsCar = Container.expand(function () {
var self = Container.call(this);
var obsAsset = self.attachAsset('obstacle_sportscar', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obsAsset.width;
self.height = obsAsset.height;
self.update = function () {
if (self.lastY === undefined) self.lastY = self.y;
self.y += roadSpeed;
self.lastY = self.y;
};
return self;
});
// Truck obstacle
var ObstacleTruck = Container.expand(function () {
var self = Container.call(this);
var obsAsset = self.attachAsset('obstacle_truck', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obsAsset.width;
self.height = obsAsset.height;
self.update = function () {
if (self.lastY === undefined) self.lastY = self.y;
self.y += roadSpeed;
self.lastY = self.y;
};
return self;
});
// Van obstacle
var ObstacleVan = Container.expand(function () {
var self = Container.call(this);
var obsAsset = self.attachAsset('obstacle_van', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obsAsset.width;
self.height = obsAsset.height;
self.update = function () {
if (self.lastY === undefined) self.lastY = self.y;
self.y += roadSpeed;
self.lastY = self.y;
};
return self;
});
// Police Car class
var PolisCar = Container.expand(function () {
var self = Container.call(this);
var polisAsset = self.attachAsset('Polisecar', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = polisAsset.width;
self.height = polisAsset.height;
// Police car speed (moves down the road)
self.update = function () {
if (self.lastY === undefined) self.lastY = self.y;
self.y += roadSpeed;
self.lastY = self.y;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Play start sound at the very beginning of the game
// Car (player)
// Obstacle
// Speed boost
// Road
// Lane marker
LK.getSound('Arabasssw').play();
LK.stopMusic(); // Stop any background music if playing
LK.playMusic('Aeabasec'); // Play only Aeabasec music on car selection
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var ROAD_WIDTH = 900;
var ROAD_X = (GAME_WIDTH - ROAD_WIDTH) / 2;
var ROAD_Y = 0;
var LANE_COUNT = 3;
var LANE_WIDTH = ROAD_WIDTH / LANE_COUNT;
var CAR_START_X = GAME_WIDTH / 2;
var CAR_START_Y = GAME_HEIGHT - 500;
var OBSTACLE_MIN_GAP = 600;
var OBSTACLE_MAX_GAP = 1100;
var BOOST_CHANCE = 0.25; // 25% chance to spawn a boost with an obstacle
// Road speed (pixels per tick)
var roadSpeed = 10;
var roadSpeedBase = 10;
var roadSpeedBoosted = 16;
var boostDuration = 90; // ticks (1.5 seconds)
var boostTicks = 0;
// Score
var score = 0;
// Boost collected counter
var boostCollectedCount = 0;
// Score text
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Boost collected text
var boostCollectedTxt = new Text2('$ 0', {
size: 80,
fill: 0x00FF00
});
boostCollectedTxt.anchor.set(0.5, 0);
// Position boostCollectedTxt below the score, always centered and with enough margin
boostCollectedTxt.x = LK.gui.width / 2 - 220;
boostCollectedTxt.y = scoreTxt.y + scoreTxt.height + 10;
LK.gui.top.addChild(boostCollectedTxt);
// Re-align on resize to keep it visible and centered
LK.on('resize', function () {
boostCollectedTxt.x = LK.gui.width / 2 - 220;
boostCollectedTxt.y = scoreTxt.y + scoreTxt.height + 10;
distanceText.x = LK.gui.width / 2 - 220;
});
function updateBoostCollectedTxt() {
boostCollectedTxt.setText('$ ' + boostCollectedCount);
}
// Arrays for game objects
var obstacles = [];
var boosts = [];
var lanes = [];
var nitros = []; // Nitro collectibles
// Track how many times the player passes close to a truck obstacle
var truckPassCount = 0;
// Nitro state
var nitroCount = 0;
var nitroActive = false;
var nitroTicks = 0;
// Weather effect globals
var weatherParticles = [];
var weatherOverlay = undefined;
// --- Thunder sound timer for rainy weather ---
var gokgurultuTimer = null;
function startGokgurultuTimer() {
if (gokgurultuTimer) LK.clearInterval(gokgurultuTimer);
gokgurultuTimer = LK.setInterval(function () {
// Only play if rainy weather is active and car selection overlay is hidden
if (typeof selectedWeather !== "undefined" && selectedWeather === "rainy" && carSelectOverlay && carSelectOverlay.visible === false) {
LK.getSound('Gokgurultu').play();
}
}, 5000);
}
function stopGokgurultuTimer() {
if (gokgurultuTimer) {
LK.clearInterval(gokgurultuTimer);
gokgurultuTimer = null;
}
}
// Road background
var road = LK.getAsset('road', {
anchorX: 0,
anchorY: 0,
x: ROAD_X,
y: ROAD_Y
});
// Score text
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Distance traveled (meters)
var distanceTraveled = 0;
// Distance text
var distanceText = new Text2('0 m', {
size: 80,
fill: 0xFFD700
});
distanceText.anchor.set(0.5, 0);
// Place at the very top center, with a small margin from the top
distanceText.x = LK.gui.width / 2 - 220;
distanceText.y = 0; // absolutely at the top
// Add distanceText as the first child so it appears above the score
LK.gui.top.addChild(distanceText);
// Nitro GUI icon and counter (must be after scoreTxt is created)
var nitroIcon = LK.getAsset('Nitro', {
anchorX: 0,
anchorY: 0,
width: 90,
height: 90
});
nitroIcon.x = LK.gui.width / 2 + scoreTxt.width / 2 + 350;
nitroIcon.y = 10;
LK.gui.top.addChild(nitroIcon);
var nitroLabel = new Text2('x' + nitroCount, {
size: 80,
fill: 0x00EAFF
});
nitroLabel.anchor.set(0, 0);
nitroLabel.x = nitroIcon.x + nitroIcon.width + 10;
nitroLabel.y = nitroIcon.y + 10;
LK.gui.top.addChild(nitroLabel);
function updateNitroLabel() {
nitroLabel.setText('x' + nitroCount);
}
// (El freni (brake) button and logic removed)
game.addChild(road);
// Lane markers
function spawnLaneMarkers() {
// Remove old lanes
for (var i = 0; i < lanes.length; i++) {
lanes[i].destroy();
}
lanes = [];
// For each lane (except the leftmost), draw a marker
for (var l = 1; l < LANE_COUNT; l++) {
for (var y = -200; y < GAME_HEIGHT + 200; y += 400) {
var lane = new Lane();
lane.x = ROAD_X + l * LANE_WIDTH;
lane.y = y;
lanes.push(lane);
game.addChild(lane);
}
}
}
spawnLaneMarkers();
// --- Car Selection Screen Implementation ---
// Available car options (asset id, display name)
var carOptions = [{
id: 'car',
name: 'Klasik'
}, {
id: 'obstacle_sportscar',
name: 'Spor'
}, {
id: 'obstacle_truck',
name: 'Motosiklet'
},
// Yeni arabalar eklendi
{
id: 'obstacle_motorcycle',
name: 'Lüks Araba'
}];
// Weather options
var weatherOptions = [{
id: 'rainy',
name: 'Yağmurlu'
}, {
id: 'snowy',
name: 'Karlı'
}, {
id: 'sunny',
name: 'Güneşli'
}];
var selectedWeatherIndex = 0;
// Selected car index (default 0)
var selectedCarIndex = 0;
// Car selection overlay container
var carSelectOverlay = new Container();
carSelectOverlay.zIndex = 10000; // Always on top
game.addChild(carSelectOverlay);
// Animated background for car selection overlay
var overlayBg = LK.getAsset('road', {
anchorX: 0,
anchorY: 0,
width: GAME_WIDTH,
height: GAME_HEIGHT
});
overlayBg.alpha = 0.85;
overlayBg.tint = 0x000000;
carSelectOverlay.addChild(overlayBg);
// Add a second, lighter overlay for animation effect
var overlayAnim = LK.getAsset('road', {
anchorX: 0,
anchorY: 0,
width: GAME_WIDTH,
height: GAME_HEIGHT
});
overlayAnim.alpha = 0.18;
overlayAnim.tint = 0x00EAFF;
carSelectOverlay.addChild(overlayAnim);
// Animate the overlayAnim's alpha and tint for a lively effect
function animateOverlayBg() {
tween(overlayAnim, {
alpha: 0.32
}, {
duration: 1200,
easing: tween.sineInOut,
onFinish: function onFinish() {
tween(overlayAnim, {
alpha: 0.18
}, {
duration: 1200,
easing: tween.sineInOut,
onFinish: animateOverlayBg
});
}
});
// Animate tint between two colors for a subtle color shift
var tints = [0x00EAFF, 0xFFD700, 0x00EAFF];
var idx = 0;
function nextTint() {
idx = (idx + 1) % tints.length;
tween(overlayAnim, {
tint: tints[idx]
}, {
duration: 1800,
easing: tween.linear,
onFinish: nextTint
});
}
nextTint();
}
animateOverlayBg();
// Title
var selectTitle = new Text2('Araba Seç', {
size: 180,
fill: 0xFFD700
});
selectTitle.anchor.set(0.5, 0);
selectTitle.x = GAME_WIDTH / 2;
selectTitle.y = 120;
carSelectOverlay.addChild(selectTitle);
// Car preview asset
var carPreview = LK.getAsset(carOptions[selectedCarIndex].id, {
anchorX: 0.5,
anchorY: 0.5,
width: 420,
height: 270
});
carPreview.x = GAME_WIDTH / 2;
carPreview.y = 700;
carSelectOverlay.addChild(carPreview);
// Car name label
var carNameLabel = new Text2(carOptions[selectedCarIndex].name, {
size: 120,
fill: "#fff"
});
carNameLabel.anchor.set(0.5, 0);
carNameLabel.x = GAME_WIDTH / 2;
carNameLabel.y = carPreview.y + carPreview.height / 2 + 40;
carSelectOverlay.addChild(carNameLabel);
// Weather selection UI
var weatherTitle = new Text2('Hava Durumu', {
size: 100,
fill: 0xFFD700
});
weatherTitle.anchor.set(0.5, 0);
weatherTitle.x = GAME_WIDTH / 2;
weatherTitle.y = carNameLabel.y + 180;
carSelectOverlay.addChild(weatherTitle);
var weatherLabel = new Text2(weatherOptions[selectedWeatherIndex].name, {
size: 100,
fill: "#fff"
});
weatherLabel.anchor.set(0.5, 0);
weatherLabel.x = GAME_WIDTH / 2;
weatherLabel.y = weatherTitle.y + 120;
carSelectOverlay.addChild(weatherLabel);
var leftWeatherArrow = LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0.5,
width: 60,
height: 120
});
leftWeatherArrow.x = weatherLabel.x - 250;
leftWeatherArrow.y = weatherLabel.y + 60;
leftWeatherArrow.rotation = Math.PI;
leftWeatherArrow.tint = 0xFFD700;
carSelectOverlay.addChild(leftWeatherArrow);
var rightWeatherArrow = LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0.5,
width: 60,
height: 120
});
rightWeatherArrow.x = weatherLabel.x + 250;
rightWeatherArrow.y = weatherLabel.y + 60;
rightWeatherArrow.tint = 0xFFD700;
carSelectOverlay.addChild(rightWeatherArrow);
leftWeatherArrow.interactive = true;
rightWeatherArrow.interactive = true;
// Helper to clear weather preview particles/overlay from car selection overlay
function clearWeatherPreview() {
if (typeof weatherPreviewOverlay !== "undefined" && weatherPreviewOverlay && weatherPreviewOverlay.parent) {
weatherPreviewOverlay.parent.removeChild(weatherPreviewOverlay);
weatherPreviewOverlay = undefined;
}
if (typeof weatherPreviewParticles !== "undefined" && Array.isArray(weatherPreviewParticles)) {
for (var wp = 0; wp < weatherPreviewParticles.length; wp++) {
if (weatherPreviewParticles[wp].parent) weatherPreviewParticles[wp].parent.removeChild(weatherPreviewParticles[wp]);
}
weatherPreviewParticles = [];
}
}
// Helper to show weather preview on car selection overlay
function showWeatherPreview() {
clearWeatherPreview();
var wx = weatherOptions[selectedWeatherIndex].id;
weatherPreviewParticles = [];
if (wx === "rainy") {
// Blue transparent overlay for rain
weatherPreviewOverlay = LK.getAsset('Background', {
anchorX: 0,
anchorY: 0,
width: GAME_WIDTH,
height: GAME_HEIGHT
});
weatherPreviewOverlay.alpha = 0.18;
weatherPreviewOverlay.tint = 0x00aaff;
carSelectOverlay.addChildAt(weatherPreviewOverlay, 1);
// Animated rain particles
var rainCount = 24;
for (var r = 0; r < rainCount; r++) {
var drop = LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0,
width: 8,
height: 60
});
drop.alpha = 0.32 + Math.random() * 0.18;
drop.tint = 0x00bfff;
drop.x = ROAD_X + Math.random() * ROAD_WIDTH;
drop.y = Math.random() * GAME_HEIGHT;
drop._rainSpeed = 18 + Math.random() * 10;
drop._rainDrift = (Math.random() - 0.5) * 4;
carSelectOverlay.addChild(drop);
weatherPreviewParticles.push(drop);
}
// Play rain sound (no loop) for preview
if (!carSelectOverlay._rainSoundPlaying) {
LK.getSound('Yagmursesi').play();
carSelectOverlay._rainSoundPlaying = true;
}
// Stop snow music if it was playing in preview
if (carSelectOverlay._snowMusicPlaying) {
LK.getSound('Karlihava').stop();
carSelectOverlay._snowMusicPlaying = false;
}
} else {
// Stop rain sound if it was playing in preview
if (carSelectOverlay._rainSoundPlaying) {
LK.getSound('Yagmursesi').stop();
carSelectOverlay._rainSoundPlaying = false;
}
// Stop snow music if it was playing in preview
if (carSelectOverlay._snowMusicPlaying) {
LK.getSound('Karlihava').stop();
carSelectOverlay._snowMusicPlaying = false;
}
}
if (wx === "snowy") {
// White transparent overlay for snow
weatherPreviewOverlay = LK.getAsset('Background', {
anchorX: 0,
anchorY: 0,
width: GAME_WIDTH,
height: GAME_HEIGHT
});
weatherPreviewOverlay.alpha = 0.22;
weatherPreviewOverlay.tint = 0xffffff;
carSelectOverlay.addChildAt(weatherPreviewOverlay, 1);
// Animated snow particles
var snowCount = 18;
for (var s = 0; s < snowCount; s++) {
var flake = LK.getAsset('Background', {
anchorX: 0.5,
anchorY: 0.5,
width: 18 + Math.random() * 18,
height: 18 + Math.random() * 18
});
flake.alpha = 0.18 + Math.random() * 0.18;
flake.tint = 0xffffff;
flake.x = ROAD_X + Math.random() * ROAD_WIDTH;
flake.y = Math.random() * GAME_HEIGHT;
flake._snowSpeed = 3 + Math.random() * 4;
flake._snowDrift = (Math.random() - 0.5) * 1.5;
flake._snowAngle = Math.random() * Math.PI * 2;
carSelectOverlay.addChild(flake);
weatherPreviewParticles.push(flake);
}
// Play snow music (no loop) for preview
if (!carSelectOverlay._snowMusicPlaying) {
LK.getSound('Karlihava').play();
carSelectOverlay._snowMusicPlaying = true;
}
}
// For sunny, nothing to add (clear)
}
// Animate weather preview particles on car selection overlay
function animateWeatherPreview() {
if (carSelectOverlay.visible && typeof weatherPreviewParticles !== "undefined" && Array.isArray(weatherPreviewParticles) && weatherPreviewParticles.length > 0) {
var wx = weatherOptions[selectedWeatherIndex].id;
for (var wp = 0; wp < weatherPreviewParticles.length; wp++) {
var p = weatherPreviewParticles[wp];
if (wx === "rainy") {
p.y += p._rainSpeed;
p.x += p._rainDrift;
if (p.y > GAME_HEIGHT + 40) {
p.y = -60;
p.x = ROAD_X + Math.random() * ROAD_WIDTH;
}
if (p.x < ROAD_X) p.x = ROAD_X + ROAD_WIDTH - 10;
if (p.x > ROAD_X + ROAD_WIDTH) p.x = ROAD_X + 10;
} else if (wx === "snowy") {
p.y += p._snowSpeed;
p.x += Math.sin(p._snowAngle) * p._snowDrift;
p._snowAngle += 0.02 + Math.random() * 0.01;
if (p.y > GAME_HEIGHT + 30) {
p.y = -20;
p.x = ROAD_X + Math.random() * ROAD_WIDTH;
}
if (p.x < ROAD_X) p.x = ROAD_X + ROAD_WIDTH - 10;
if (p.x > ROAD_X + ROAD_WIDTH) p.x = ROAD_X + 10;
}
}
}
LK.setTimeout(animateWeatherPreview, 33); // ~30fps
}
animateWeatherPreview();
leftWeatherArrow.down = function (x, y, obj) {
selectedWeatherIndex = (selectedWeatherIndex - 1 + weatherOptions.length) % weatherOptions.length;
weatherLabel.setText(weatherOptions[selectedWeatherIndex].name);
showWeatherPreview();
};
rightWeatherArrow.down = function (x, y, obj) {
selectedWeatherIndex = (selectedWeatherIndex + 1) % weatherOptions.length;
weatherLabel.setText(weatherOptions[selectedWeatherIndex].name);
showWeatherPreview();
};
// Show initial weather preview
showWeatherPreview();
// Left/right arrow assets
var leftArrow = LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0.5,
width: 80,
height: 180
});
leftArrow.x = carPreview.x - 350;
leftArrow.y = carPreview.y;
leftArrow.rotation = Math.PI; // Point left
leftArrow.tint = 0xFFD700;
carSelectOverlay.addChild(leftArrow);
var rightArrow = LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0.5,
width: 80,
height: 180
});
rightArrow.x = carPreview.x + 350;
rightArrow.y = carPreview.y;
rightArrow.tint = 0xFFD700;
carSelectOverlay.addChild(rightArrow);
// Start button as asset
var startBtn = LK.getAsset('Start', {
anchorX: 0.5,
anchorY: 0.5,
width: 400,
height: 140
});
startBtn.x = GAME_WIDTH / 2;
startBtn.y = weatherLabel.y + 220;
carSelectOverlay.addChild(startBtn);
// Dance animation for startBtn asset
function danceStartBtn() {
// Scale up and rotate right
tween(startBtn, {
scaleX: 1.18,
scaleY: 0.88,
rotation: 0.18
}, {
duration: 180,
easing: tween.sineInOut,
onFinish: function onFinish() {
// Scale down and rotate left
tween(startBtn, {
scaleX: 0.88,
scaleY: 1.18,
rotation: -0.18
}, {
duration: 180,
easing: tween.sineInOut,
onFinish: function onFinish() {
// Return to normal
tween(startBtn, {
scaleX: 1,
scaleY: 1,
rotation: 0
}, {
duration: 180,
easing: tween.sineInOut,
onFinish: danceStartBtn
});
}
});
}
});
}
danceStartBtn();
// Arrow touch logic
leftArrow.interactive = true;
rightArrow.interactive = true;
startBtn.interactive = true;
leftArrow.down = function (x, y, obj) {
selectedCarIndex = (selectedCarIndex - 1 + carOptions.length) % carOptions.length;
carSelectOverlay.removeChild(carPreview);
carPreview = LK.getAsset(carOptions[selectedCarIndex].id, {
anchorX: 0.5,
anchorY: 0.5,
width: 420,
height: 270
});
carPreview.x = GAME_WIDTH / 2;
carPreview.y = 700;
carSelectOverlay.addChildAt(carPreview, 2);
carNameLabel.setText(carOptions[selectedCarIndex].name);
};
rightArrow.down = function (x, y, obj) {
selectedCarIndex = (selectedCarIndex + 1) % carOptions.length;
carSelectOverlay.removeChild(carPreview);
carPreview = LK.getAsset(carOptions[selectedCarIndex].id, {
anchorX: 0.5,
anchorY: 0.5,
width: 420,
height: 270
});
carPreview.x = GAME_WIDTH / 2;
carPreview.y = 700;
carSelectOverlay.addChildAt(carPreview, 2);
carNameLabel.setText(carOptions[selectedCarIndex].name);
};
// Start button logic
var car; // Declare car globally for later use
var selectedWeather = weatherOptions[selectedWeatherIndex].id;
startBtn.down = function (x, y, obj) {
carSelectOverlay.visible = false;
LK.stopMusic();
LK.playMusic('Music');
car = new Car();
car.removeChildAt(0);
var carAsset = car.attachAsset(carOptions[selectedCarIndex].id, {
anchorX: 0.5,
anchorY: 0.5
});
car.width = carAsset.width;
car.height = carAsset.height;
car.x = CAR_START_X;
car.y = CAR_START_Y;
car.targetX = car.x;
game.addChild(car);
// Save selected weather for gameplay
selectedWeather = weatherOptions[selectedWeatherIndex].id;
};
// Show overlay at start, hide game controls until car is picked
carSelectOverlay.visible = true;
// LK.playMusic('Aeabasec'); // Already handled above, no need to call again
// --- End Car Selection Screen ---
// Score text
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Lives (hak) system
var lives = 3;
// Image-based lives display
var livesImages = [];
var LIVES_IMAGE_ID = 'car'; // Use car image for lives
var LIVES_IMAGE_SIZE = 90; // px
var LIVES_IMAGE_MARGIN = 20; // px between images
// Positioning: Place lives images to the right of the score counter
var LIVES_IMAGE_TOP_MARGIN = 10; // px from top of GUI
var LIVES_IMAGE_LEFT_MARGIN = 40; // px from left of score counter
function updateLivesImages() {
// Remove old images
for (var i = 0; i < livesImages.length; i++) {
livesImages[i].destroy();
}
livesImages = [];
// Place lives images to the right of the score counter
// Get scoreTxt position in GUI coordinates
var scoreX = scoreTxt.x * LK.gui.width;
var scoreY = scoreTxt.y * LK.gui.height;
// But since anchor is (0.5, 0), scoreTxt.x is centered in GUI width
var baseX = LK.gui.width / 2 + scoreTxt.width / 2 + LIVES_IMAGE_LEFT_MARGIN;
var baseY = LIVES_IMAGE_TOP_MARGIN;
for (var i = 0; i < lives; i++) {
var img = LK.getAsset(LIVES_IMAGE_ID, {
anchorX: 0,
anchorY: 0,
width: LIVES_IMAGE_SIZE,
height: LIVES_IMAGE_SIZE
});
// Position: right of score, stacked horizontally with margin
img.x = baseX + i * (LIVES_IMAGE_SIZE + LIVES_IMAGE_MARGIN);
img.y = baseY;
LK.gui.top.addChild(img);
livesImages.push(img);
}
}
// Helper to update lives text (legacy, keep for compatibility)
function updateLivesText() {
updateLivesImages();
if (typeof livesLabel !== "undefined") {
livesLabel.setText('Can: ' + lives);
}
}
updateLivesImages();
// Add a text label to show remaining lives (can/hak) on the GUI, next to the last life image
var livesLabel = new Text2('Can: ' + lives, {
size: 80,
fill: "#fff"
});
livesLabel.anchor.set(0, 0); // left-top
// Initial position: right of the last life image, will be updated on resize and updateLivesImages
function updateLivesLabelPosition() {
if (livesImages.length > 0) {
var lastImg = livesImages[livesImages.length - 1];
livesLabel.x = lastImg.x + lastImg.width + 20;
livesLabel.y = lastImg.y + 10;
} else {
// Fallback: right of score
livesLabel.x = LK.gui.width / 2 + scoreTxt.width / 2 + LIVES_IMAGE_LEFT_MARGIN;
livesLabel.y = LIVES_IMAGE_TOP_MARGIN + 10;
}
}
LK.gui.top.addChild(livesLabel);
updateLivesLabelPosition();
// Ensure lives images and label are always visible on resize
LK.on('resize', function () {
updateLivesImages();
updateLivesLabelPosition();
});
// Helper: get lane center x
function getLaneCenter(laneIdx) {
return ROAD_X + LANE_WIDTH * (laneIdx + 0.5);
}
// Helper: spawn an obstacle (and maybe a boost)
function spawnObstacle() {
// Random lane
var laneIdx = Math.floor(Math.random() * LANE_COUNT);
// Randomly select one of five obstacle types (NO police car here)
var type = Math.floor(Math.random() * 5);
var obs;
if (type === 0) {
obs = new Obstacle(); // default car
} else if (type === 1) {
obs = new ObstacleTruck();
} else if (type === 2) {
obs = new ObstacleMotorcycle();
} else if (type === 3) {
obs = new ObstacleSportsCar();
} else if (type === 4) {
obs = new ObstacleVan();
}
obs.x = getLaneCenter(laneIdx);
obs.y = -obs.height / 2;
obstacles.push(obs);
game.addChild(obs);
// Maybe spawn a boost in a different lane
if (Math.random() < BOOST_CHANCE) {
var boostLane = laneIdx;
// Try to pick a different lane
if (LANE_COUNT > 1) {
while (boostLane === laneIdx) {
boostLane = Math.floor(Math.random() * LANE_COUNT);
}
}
var boost = new Boost();
boost.x = getLaneCenter(boostLane);
boost.y = obs.y;
boosts.push(boost);
game.addChild(boost);
}
// Maybe spawn a nitro in a random lane (10% chance)
if (Math.random() < 0.10) {
var nitroLane = laneIdx;
if (LANE_COUNT > 1) {
while (nitroLane === laneIdx) {
nitroLane = Math.floor(Math.random() * LANE_COUNT);
}
}
var nitro = new Nitro();
nitro.x = getLaneCenter(nitroLane);
nitro.y = obs.y;
nitros.push(nitro);
game.addChild(nitro);
}
}
// Timer for spawning police car every 10 seconds
var polisCarTimer = LK.setInterval(function () {
// Spawn police car in a random lane
var laneIdx = Math.floor(Math.random() * LANE_COUNT);
var polis = new PolisCar();
polis.x = getLaneCenter(laneIdx);
polis.y = -polis.height / 2;
obstacles.push(polis);
game.addChild(polis);
}, 10000);
// Helper: spawn lane markers as they scroll
function updateLaneMarkers() {
for (var i = lanes.length - 1; i >= 0; i--) {
var lane = lanes[i];
lane.update();
if (lane.y > GAME_HEIGHT + 200) {
// Recycle to top
lane.y -= GAME_HEIGHT + 400;
}
}
}
// Dragging
var dragging = false;
var dragOffsetX = 0;
// Touch/mouse controls
game.down = function (x, y, obj) {
// (El freni (brake) button logic removed)
// Nitro activation: if touch is on nitro icon, activate nitro if available
if (x >= nitroIcon.x && x <= nitroIcon.x + nitroIcon.width && y >= nitroIcon.y && y <= nitroIcon.y + nitroIcon.height) {
if (nitroCount > 0 && !nitroActive) {
nitroCount--;
updateNitroLabel();
nitroActive = true;
nitroTicks = NITRO_DURATION;
roadSpeed = roadSpeedBoosted;
LK.effects.flashObject(car, 0x00eaff, 800);
// Optionally, flash screen blue for effect
LK.effects.flashScreen(0x00eaff, 300);
}
return;
}
// Only allow drag if car is selected and touch is on car or on road
if (car && x > ROAD_X && x < ROAD_X + ROAD_WIDTH && y > 0 && y < GAME_HEIGHT) {
dragging = true;
dragOffsetX = x - car.x;
// Set targetX immediately
car.targetX = Math.max(ROAD_X + car.width / 2, Math.min(ROAD_X + ROAD_WIDTH - car.width / 2, x - dragOffsetX));
}
};
game.up = function (x, y, obj) {
dragging = false;
// (El freni (brake) button logic removed)
};
game.move = function (x, y, obj) {
if (dragging && car) {
// Clamp to road
var tx = Math.max(ROAD_X + car.width / 2, Math.min(ROAD_X + ROAD_WIDTH - car.width / 2, x - dragOffsetX));
car.targetX = tx;
}
};
// Game update
var lastObstacleY = -OBSTACLE_MIN_GAP;
game.update = function () {
// (El freni (brake) effect removed)
if (!nitroActive) {
roadSpeed = roadSpeedBase;
}
// Update car (only if selected)
if (car) car.update();
// Weather effect (visual only, can be expanded for gameplay effects)
if (typeof selectedWeather !== "undefined" && car && carSelectOverlay.visible === false) {
// --- Rain and snow sound/music logic ---
if (selectedWeather === "rainy") {
if (!game._rainSoundPlaying) {
LK.getSound('Yagmursesi').play();
game._rainSoundPlaying = true;
}
// Start thunder timer if not already running and overlay is hidden
if (carSelectOverlay && carSelectOverlay.visible === false && !gokgurultuTimer) {
startGokgurultuTimer();
}
// Stop snow music if it was playing
if (game._snowMusicPlaying) {
LK.getSound('Karlihava').stop();
game._snowMusicPlaying = false;
}
} else if (selectedWeather === "snowy") {
stopGokgurultuTimer();
if (game._gokgurultuPlayed) game._gokgurultuPlayed = false;
// Stop rain sound if it was playing
if (game._rainSoundPlaying) {
LK.getSound('Yagmursesi').stop();
game._rainSoundPlaying = false;
}
// Play snow music if not already playing
if (!game._snowMusicPlaying) {
LK.getSound('Karlihava').play();
game._snowMusicPlaying = true;
}
} else {
stopGokgurultuTimer();
if (game._rainSoundPlaying) {
LK.getSound('Yagmursesi').stop();
game._rainSoundPlaying = false;
}
if (game._snowMusicPlaying) {
LK.getSound('Karlihava').stop();
game._snowMusicPlaying = false;
}
}
// Remove any previous weather overlays and particles
if (typeof weatherOverlay !== "undefined" && weatherOverlay.parent) {
weatherOverlay.parent.removeChild(weatherOverlay);
weatherOverlay = undefined;
}
if (typeof weatherParticles !== "undefined" && Array.isArray(weatherParticles)) {
for (var wp = 0; wp < weatherParticles.length; wp++) {
if (weatherParticles[wp].parent) weatherParticles[wp].parent.removeChild(weatherParticles[wp]);
}
weatherParticles = [];
}
// Add weather overlay and animated particles if needed
if (selectedWeather === "rainy") {
// Blue transparent overlay for rain
weatherOverlay = LK.getAsset('Background', {
anchorX: 0,
anchorY: 0,
width: GAME_WIDTH,
height: GAME_HEIGHT
});
weatherOverlay.alpha = 0.18;
weatherOverlay.tint = 0x00aaff;
game.addChild(weatherOverlay);
if (game.children && game.children.length > 0) {
game.setChildIndex(weatherOverlay, game.children.length - 1);
}
// Animated rain particles
if (typeof weatherParticles === "undefined") weatherParticles = [];
var rainCount = 32;
for (var r = 0; r < rainCount; r++) {
var drop = LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0,
width: 8,
height: 60
});
drop.alpha = 0.32 + Math.random() * 0.18;
drop.tint = 0x00bfff;
drop.x = ROAD_X + Math.random() * ROAD_WIDTH;
drop.y = Math.random() * GAME_HEIGHT;
drop._rainSpeed = 32 + Math.random() * 18;
drop._rainDrift = (Math.random() - 0.5) * 6;
game.addChild(drop);
weatherParticles.push(drop);
}
} else if (selectedWeather === "snowy") {
// White transparent overlay for snow
weatherOverlay = LK.getAsset('Background', {
anchorX: 0,
anchorY: 0,
width: GAME_WIDTH,
height: GAME_HEIGHT
});
weatherOverlay.alpha = 0.22;
weatherOverlay.tint = 0xffffff;
game.addChild(weatherOverlay);
if (game.children && game.children.length > 0) {
game.setChildIndex(weatherOverlay, game.children.length - 1);
}
// Animated snow particles
if (typeof weatherParticles === "undefined") weatherParticles = [];
var snowCount = 28;
for (var s = 0; s < snowCount; s++) {
var flake = LK.getAsset('Background', {
anchorX: 0.5,
anchorY: 0.5,
width: 18 + Math.random() * 18,
height: 18 + Math.random() * 18
});
flake.alpha = 0.18 + Math.random() * 0.18;
flake.tint = 0xffffff;
flake.x = ROAD_X + Math.random() * ROAD_WIDTH;
flake.y = Math.random() * GAME_HEIGHT;
flake._snowSpeed = 6 + Math.random() * 8;
flake._snowDrift = (Math.random() - 0.5) * 2.5;
flake._snowAngle = Math.random() * Math.PI * 2;
game.addChild(flake);
weatherParticles.push(flake);
}
}
// For sunny, no overlay or particles needed
if (selectedWeather === "sunny") {
if (typeof weatherOverlay !== "undefined" && weatherOverlay.parent) {
weatherOverlay.parent.removeChild(weatherOverlay);
weatherOverlay = undefined;
}
if (typeof weatherParticles !== "undefined" && Array.isArray(weatherParticles)) {
for (var wp = 0; wp < weatherParticles.length; wp++) {
if (weatherParticles[wp].parent) weatherParticles[wp].parent.removeChild(weatherParticles[wp]);
}
weatherParticles = [];
}
}
} else {
// If overlay/particles exist but weather is not active, remove them
if (typeof weatherOverlay !== "undefined" && weatherOverlay.parent) {
weatherOverlay.parent.removeChild(weatherOverlay);
weatherOverlay = undefined;
}
if (typeof weatherParticles !== "undefined" && Array.isArray(weatherParticles)) {
for (var wp = 0; wp < weatherParticles.length; wp++) {
if (weatherParticles[wp].parent) weatherParticles[wp].parent.removeChild(weatherParticles[wp]);
}
weatherParticles = [];
}
}
// Animate weather particles if present
if (typeof weatherParticles !== "undefined" && Array.isArray(weatherParticles) && weatherParticles.length > 0) {
for (var wp = 0; wp < weatherParticles.length; wp++) {
var p = weatherParticles[wp];
if (selectedWeather === "rainy") {
p.y += p._rainSpeed;
p.x += p._rainDrift;
if (p.y > GAME_HEIGHT + 40) {
p.y = -60;
p.x = ROAD_X + Math.random() * ROAD_WIDTH;
}
if (p.x < ROAD_X) p.x = ROAD_X + ROAD_WIDTH - 10;
if (p.x > ROAD_X + ROAD_WIDTH) p.x = ROAD_X + 10;
} else if (selectedWeather === "snowy") {
p.y += p._snowSpeed;
p.x += Math.sin(p._snowAngle) * p._snowDrift;
p._snowAngle += 0.02 + Math.random() * 0.01;
if (p.y > GAME_HEIGHT + 30) {
p.y = -20;
p.x = ROAD_X + Math.random() * ROAD_WIDTH;
}
if (p.x < ROAD_X) p.x = ROAD_X + ROAD_WIDTH - 10;
if (p.x > ROAD_X + ROAD_WIDTH) p.x = ROAD_X + 10;
}
}
}
// Update lane markers
updateLaneMarkers();
// Update distance traveled (add roadSpeed per tick, convert to meters)
distanceTraveled += roadSpeed;
if (distanceText) {
// Show as meters or km
if (distanceTraveled < 1000) {
distanceText.setText(Math.floor(distanceTraveled) + " m");
} else {
distanceText.setText((distanceTraveled / 1000).toFixed(2) + " km");
}
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
obs.update();
// --- Play police sound when passing close to a police car ---
if (obs instanceof PolisCar && car) {
// Track lastY for police car if not already
if (obs.lastYForSound === undefined) obs.lastYForSound = obs.y;
// Define "passing by" as car's y is within 100px above or below the police car's y (center to center)
var closeRange = 100;
var wasFar = Math.abs(car.y - obs.lastYForSound) > closeRange;
var isClose = Math.abs(car.y - obs.y) <= closeRange;
// Only play sound on the frame we enter the close range
if (wasFar && isClose) {
LK.getSound('Polissound').play();
}
obs.lastYForSound = obs.y;
}
// --- Play horn sound when passing close to a truck obstacle ---
if (obs instanceof ObstacleTruck && car) {
if (obs.lastYForHorn === undefined) obs.lastYForHorn = obs.y;
var hornRange = 120;
var wasFarHorn = Math.abs(car.y - obs.lastYForHorn) > hornRange;
var isCloseHorn = Math.abs(car.y - obs.y) <= hornRange;
if (wasFarHorn && isCloseHorn) {
truckPassCount++;
if (truckPassCount >= 6) {
LK.getSound('Kornasesi').play();
truckPassCount = 0;
}
}
obs.lastYForHorn = obs.y;
}
// Remove if off screen
if (obs.y - obs.height / 2 > GAME_HEIGHT + 100) {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
// Collision with car
if (car && car.intersects(obs)) {
// Play damage sound
LK.getSound('Damage').play();
// Flash screen red
LK.effects.flashScreen(0xff0000, 800);
// Remove the obstacle so it doesn't trigger again
obs.destroy();
obstacles.splice(i, 1);
// Decrement lives
lives--;
updateLivesText();
if (lives <= 0) {
LK.showGameOver();
return;
}
// If not game over, briefly flash car and continue
LK.effects.flashObject(car, 0xff0000, 600);
continue;
}
}
// Update boosts
for (var i = boosts.length - 1; i >= 0; i--) {
var boost = boosts[i];
boost.update();
// Remove if off screen
if (boost.y - boost.height / 2 > GAME_HEIGHT + 100) {
boost.destroy();
boosts.splice(i, 1);
continue;
}
// Collect boost
if (car && car.intersects(boost)) {
boost.destroy();
boosts.splice(i, 1);
// Add score for collecting boost
score += 100;
scoreTxt.setText(score);
// Increment boost collected counter and update GUI
boostCollectedCount++;
updateBoostCollectedTxt();
// Play coin sound
LK.getSound('Coinsound').play();
// Flash car yellow
LK.effects.flashObject(car, 0xffeb3b, 400);
// Show green $ sign effect on car
var dollarText = new Text2('$', {
size: 180,
fill: 0x00ff00
});
dollarText.anchor.set(0.5, 0.5);
dollarText.x = car.x;
dollarText.y = car.y - car.height / 2 - 40;
game.addChild(dollarText);
tween(dollarText, {
y: dollarText.y - 120,
alpha: 0
}, {
duration: 700,
easing: tween.sineInOut,
onFinish: function onFinish() {
if (dollarText.parent) dollarText.parent.removeChild(dollarText);
}
});
}
}
// Update nitros
for (var i = nitros.length - 1; i >= 0; i--) {
var nitro = nitros[i];
nitro.update();
// Remove if off screen
if (nitro.y - nitro.height / 2 > GAME_HEIGHT + 100) {
nitro.destroy();
nitros.splice(i, 1);
continue;
}
// Collect nitro
if (car && car.intersects(nitro)) {
nitro.destroy();
nitros.splice(i, 1);
nitroCount++;
updateNitroLabel();
// Play nitro sound
LK.getSound('Nitrosound').play();
// Flash car cyan
LK.effects.flashObject(car, 0x00eaff, 400);
// Activate nitro immediately for 4 seconds (240 ticks at 60fps)
nitroActive = true;
nitroTicks = 240;
roadSpeed = roadSpeedBoosted;
}
}
// Handle boost timer
if (boostTicks > 0) {
boostTicks--;
if (boostTicks === 0) {
roadSpeed = roadSpeedBase;
}
}
// Handle nitro timer
if (nitroActive) {
nitroTicks--;
if (nitroTicks <= 0) {
nitroActive = false;
roadSpeed = roadSpeedBase;
}
}
// Spawn new obstacles
if (obstacles.length === 0 || obstacles[obstacles.length - 1].y > OBSTACLE_MIN_GAP + Math.random() * (OBSTACLE_MAX_GAP - OBSTACLE_MIN_GAP)) {
spawnObstacle();
}
// Score is only updated by collecting boosts.
// Check if car is off the road
if (car && (car.x - car.width / 2 < ROAD_X || car.x + car.width / 2 > ROAD_X + ROAD_WIDTH)) {
// Play damage sound before game over
LK.getSound('Damage').play();
LK.effects.flashScreen(0xff0000, 800);
// Decrement lives
lives--;
updateLivesText();
if (lives <= 0) {
// Delay game over slightly to ensure sound plays
LK.setTimeout(function () {
LK.showGameOver();
}, 100);
return;
}
// If not game over, flash car and move car back to road center
LK.effects.flashObject(car, 0xff0000, 600);
car.x = CAR_START_X;
car.targetX = car.x;
// Don't return, let the game continue
}
};
// Reset game state on new game
LK.on('gameStart', function () {
// Stop thunder timer on game start
stopGokgurultuTimer();
// Clear and restart police car timer to avoid multiple timers
if (typeof polisCarTimer !== "undefined") {
LK.clearInterval(polisCarTimer);
}
polisCarTimer = LK.setInterval(function () {
var laneIdx = Math.floor(Math.random() * LANE_COUNT);
var polis = new PolisCar();
polis.x = getLaneCenter(laneIdx);
polis.y = -polis.height / 2;
obstacles.push(polis);
game.addChild(polis);
}, 10000);
// Play start sound
LK.getSound('Arabasssw').play();
LK.playMusic('Music');
game._gokgurultuPlayed = false;
// Remove all obstacles and boosts
for (var i = 0; i < obstacles.length; i++) obstacles[i].destroy();
for (var i = 0; i < boosts.length; i++) boosts[i].destroy();
obstacles = [];
boosts = [];
// Show car selection overlay again
if (carSelectOverlay) {
carSelectOverlay.visible = true;
}
// Reset weather selection to default (sunny)
selectedWeatherIndex = 0;
if (typeof weatherLabel !== "undefined") {
weatherLabel.setText(weatherOptions[selectedWeatherIndex].name);
}
selectedWeather = weatherOptions[selectedWeatherIndex].id;
// Remove weather overlay if exists
if (typeof weatherOverlay !== "undefined" && weatherOverlay.parent) {
weatherOverlay.parent.removeChild(weatherOverlay);
weatherOverlay = undefined;
}
// Remove weather particles if exist
if (typeof weatherParticles !== "undefined" && Array.isArray(weatherParticles)) {
for (var wp = 0; wp < weatherParticles.length; wp++) {
if (weatherParticles[wp].parent) weatherParticles[wp].parent.removeChild(weatherParticles[wp]);
}
weatherParticles = [];
}
// Remove car from game if exists
if (car && car.parent) {
car.parent.removeChild(car);
}
car = undefined;
// Reset score and speed
score = 0;
scoreTxt.setText(score);
roadSpeed = roadSpeedBase;
boostTicks = 0;
// Reset boost collected counter and GUI
boostCollectedCount = 0;
if (typeof updateBoostCollectedTxt === "function") updateBoostCollectedTxt();
// Reset truck pass count for horn
truckPassCount = 0;
// Stop rain sound if playing
if (game._rainSoundPlaying) {
LK.getSound('Yagmursesi').stop();
game._rainSoundPlaying = false;
}
// Stop snow music if playing
if (game._snowMusicPlaying) {
LK.getSound('Karlihava').stop();
game._snowMusicPlaying = false;
}
// Reset distance traveled
distanceTraveled = 0;
if (distanceText) {
distanceText.setText("0 m");
}
// Reset lane markers
spawnLaneMarkers();
// Reset lives
lives = 3;
updateLivesText();
if (typeof livesLabel !== "undefined") {
livesLabel.setText('Can: ' + lives);
}
// Reset nitro state
nitroCount = 0;
nitroActive = false;
nitroTicks = 0;
updateNitroLabel();
});