/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highestScore: 0 }); /**** * Classes ****/ // Bulut (Cloud) background object class var Bulut = Container.expand(function () { var self = Container.call(this); var bulutImg = self.attachAsset('bulut', { anchorX: 0.5, anchorY: 0.5 }); self.width = bulutImg.width; self.height = bulutImg.height; // Set a random speed for parallax effect self.speed = 2 + Math.random() * 2; self.update = function () { self.x -= self.speed; }; return self; }); // Car class var Car = Container.expand(function () { var self = Container.call(this); // Car body var carBody = self.attachAsset('car', { anchorX: 0.5, anchorY: 1 }); // Physics self.vy = 0; // vertical velocity self.isJumping = false; // Car size for collision self.bodyWidth = carBody.width; self.bodyHeight = carBody.height; // Update method self.update = function () { // Gravity and jump physics self.y += self.vy; if (self.y < groundY) { self.vy += gravity; if (self.y > groundY) { self.y = groundY; self.vy = 0; self.isJumping = false; } } else if (self.y > groundY) { self.y = groundY; self.vy = 0; self.isJumping = false; } }; // Jump method self.jump = function () { if (!self.isJumping && self.y >= groundY) { self.vy = jumpVelocity; self.isJumping = true; LK.getSound('jump').play(); // Tilt the car slightly when jumping, then return to normal after 300ms if (typeof tween !== "undefined" && typeof tween.create === "function") { // Animate tilt to -0.25 var tiltTween = tween.create(carBody, { rotation: -0.25 }, 120, { easing: "easeOutCubic" }); tiltTween.then(function () { // Animate back to 0 var resetTween = tween.create(carBody, { rotation: 0 }, 180, { easing: "easeInCubic" }); // No need to do anything after reset }); } else { carBody.rotation = -0.25; // Fallback: reset after 300ms LK.setTimeout(function () { carBody.rotation = 0; }, 300); } } }; return self; }); // Coin collectible class var CoinCollectible = Container.expand(function () { var self = Container.call(this); // Use a yellow coin emoji for collectible var coinTxt = new Text2('🪙', { size: 110, fill: 0xffe066 // gold/yellow }); coinTxt.anchor.set(0.5, 1); self.addChild(coinTxt); self.width = coinTxt.width; self.height = coinTxt.height; // For collision self.bodyWidth = coinTxt.width; self.bodyHeight = coinTxt.height; self.update = function () { var speed = typeof effectiveGameSpeed !== "undefined" ? effectiveGameSpeed : gameSpeed; self.x -= speed; }; return self; }); // Gold heart collectible class var GoldHeartCollectible = Container.expand(function () { var self = Container.call(this); // Use a text heart for collectible, gold color var goldHeartTxt = new Text2('❤', { size: 120, fill: 0xffd700 // gold }); goldHeartTxt.anchor.set(0.5, 1); self.addChild(goldHeartTxt); self.width = goldHeartTxt.width; self.height = goldHeartTxt.height; // For collision self.bodyWidth = goldHeartTxt.width; self.bodyHeight = goldHeartTxt.height; self.update = function () { var speed = typeof effectiveGameSpeed !== "undefined" ? effectiveGameSpeed : gameSpeed; self.x -= speed; }; return self; }); // Heart collectible class var HeartCollectible = Container.expand(function () { var self = Container.call(this); // Use a text heart for collectible var heartTxt = new Text2('❤', { size: 92, fill: 0xff69b4 }); heartTxt.anchor.set(0.5, 1); self.addChild(heartTxt); self.width = heartTxt.width; self.height = heartTxt.height; // For collision self.bodyWidth = heartTxt.width; self.bodyHeight = heartTxt.height; // Update method self.update = function () { // Use effectiveGameSpeed if defined, else fallback to gameSpeed var speed = typeof effectiveGameSpeed !== "undefined" ? effectiveGameSpeed : gameSpeed; self.x -= speed; }; return self; }); // Obstacle class var Obstacle = Container.expand(function () { var self = Container.call(this); var obs = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 1 }); self.width = obs.width; self.height = obs.height; // For collision self.bodyWidth = obs.width; self.bodyHeight = obs.height; // Update method self.update = function () { // Use effectiveGameSpeed if defined, else fallback to gameSpeed var speed = typeof effectiveGameSpeed !== "undefined" ? effectiveGameSpeed : gameSpeed; self.x -= speed; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87ceeb // sky blue }); /**** * Game Code ****/ // Jump sound // Road: dark gray box // Obstacle: red box // Wheel: black ellipse // Car: blue box // Constants // Left-right moving obstacle asset var groundY = 2200; // y position of the road top var gravity = 4.5; var jumpVelocity = -80; var initialGameSpeed = 24; var maxGameSpeed = 60; var gameSpeed = initialGameSpeed; var obstacleMinGap = 500; var obstacleMaxGap = 1100; var obstacleMinY = groundY; var obstacleMaxY = groundY; var minObstacleHeight = 120; var maxObstacleHeight = 220; // Road var road = LK.getAsset('road', { anchorX: 0, anchorY: 0, x: 0, y: groundY }); game.addChild(road); // Car var car = new Car(); car.x = 400; // Move the car visually closer to the road (simulate being on the road, not floating above) car.y = groundY + 20; // 20px below the road top, adjust as needed for best look game.addChild(car); // --- Slow effect state --- var carIsSlowed = false; var carSlowTicks = 0; var carSlowDuration = 120; // 2 seconds at 60fps var carSlowSpeedFactor = 0.45; // 45% speed when slowed // Obstacles var obstacles = []; var nextObstacleX = 2048 + 400; // Heart collectibles var heartCollectibles = []; var nextHeartX = 2048 + 1200; // Start further out // Gold heart collectibles var goldHeartCollectibles = []; var goldHeartActive = false; var goldHeartTimer = 0; var goldHeartDuration = 8 * 60; // 8 seconds at 60fps var goldHeartDisplay = null; var goldHeartDisplayLives = 0; // --- Coin system --- // Persistent coin count (load from storage, default 0) var coins = typeof storage.coins === "number" ? storage.coins : 0; // Coin collectibles var coinCollectibles = []; // Coin display var coinTxt = new Text2('', { size: 90, fill: 0xffe066 }); coinTxt.anchor.set(1, 1); coinTxt.x = -40; coinTxt.y = -40; LK.gui.bottomRight.addChild(coinTxt); function updateCoinDisplay() { coinTxt.setText("Jeton: " + coins); } updateCoinDisplay(); // Score var score = 0; var lastScore = 0; var lives = 3; // Start with 3 lives var maxLives = 3; // This can grow up to 9 as hearts are collected var scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Lives display var livesTxt = null; var goldHeartDisplay = null; function updateLivesDisplay() { if (!gameStarted) { // Hide lives display on start screen if (livesTxt) livesTxt.setText(""); if (goldHeartDisplay) goldHeartDisplay.setText(""); return; } if (!livesTxt) { livesTxt = new Text2('', { size: 90, fill: 0xff4444 }); livesTxt.anchor.set(0, 1); // Shift a bit more left to avoid coin overlap livesTxt.x = 10; livesTxt.y = -40; LK.gui.bottomLeft.addChild(livesTxt); } if (goldHeartActive) { // Hide normal lives, show gold hearts if (!goldHeartDisplay) { goldHeartDisplay = new Text2('', { size: 76, fill: 0xffd700 }); goldHeartDisplay.anchor.set(0, 1); goldHeartDisplay.x = 18; goldHeartDisplay.y = -40; LK.gui.bottomLeft.addChild(goldHeartDisplay); } var hearts = ''; // Always cap display to 9 hearts maximum to prevent overflow var displayMax = maxLives > 9 ? 9 : maxLives; var displayLives = goldHeartDisplayLives > 9 ? 9 : goldHeartDisplayLives; for (var i = 0; i < displayLives; i++) hearts += '❤ '; for (var i = displayLives; i < displayMax; i++) hearts += '♡ '; goldHeartDisplay.setText("Altın Can: " + hearts.trim()); livesTxt.setText(""); // Hide normal } else { // Show normal lives, hide gold if (goldHeartDisplay) goldHeartDisplay.setText(""); var hearts = ''; var displayMax = maxLives > 9 ? 9 : maxLives; for (var i = 0; i < lives && i < 9; i++) hearts += '❤ '; for (var i = lives; i < displayMax; i++) hearts += '♡ '; livesTxt.setText("Can: " + hearts.trim()); } } updateLivesDisplay(); // Last score display var lastScoreTxt = new Text2('', { size: 70, fill: 0xFFD700 }); lastScoreTxt.anchor.set(0.5, 0); lastScoreTxt.y = scoreTxt.height + 10; LK.gui.top.addChild(lastScoreTxt); // Highest score display (below leaderboard) var highestScore = storage.highestScore || 0; var highestScoreTxt = new Text2('', { size: 70, fill: 0x00ffcc }); highestScoreTxt.anchor.set(0.5, 0); highestScoreTxt.y = lastScoreTxt.y + lastScoreTxt.height + 10; LK.gui.top.addChild(highestScoreTxt); // Difficulty var ticksSinceStart = 0; // --- Start Screen Overlay --- var gameStarted = false; var startOverlay = new Container(); var startBg = LK.getAsset('road', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 2732 }); startBg.alpha = 0.82; startOverlay.addChild(startBg); // Add a visual image to the start overlay var startVisual = LK.getAsset('car', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 700, scaleX: 2.2, scaleY: 2.2 }); startOverlay.addChild(startVisual); var startText = new Text2("Araba Koşusu", { size: 180, fill: 0xffffff }); startText.anchor.set(0.5, 0.5); startText.x = 2048 / 2; startText.y = 900; startOverlay.addChild(startText); var tapText = new Text2("Başlamak için ekrana dokun", { size: 90, fill: 0xffd700 }); tapText.anchor.set(0.5, 0.5); tapText.x = 2048 / 2; tapText.y = 1200; startOverlay.addChild(tapText); // --- Garage Button --- var garageBtn = new Text2("Garaj", { size: 100, fill: 0x00bfff }); garageBtn.anchor.set(0.5, 0.5); // Place below the start text, centered garageBtn.x = 2048 / 2; garageBtn.y = 1400; garageBtn.interactive = true; garageBtn.buttonMode = true; // --- Garage Overlay --- var garageOverlay = new Container(); garageOverlay.visible = false; // Add a semi-transparent background var garageBg = LK.getAsset('road', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 2732 }); garageBg.alpha = 0.92; garageOverlay.addChild(garageBg); // Garage title var garageTitle = new Text2("Garaj", { size: 160, fill: 0xffffff }); garageTitle.anchor.set(0.5, 0.5); garageTitle.x = 2048 / 2; garageTitle.y = 400; garageOverlay.addChild(garageTitle); // --- Car selection state --- // Persistent unlock state for cars (all unlocked) var unlockedCars = { car: true, araba2: true, araba3: true }; var selectedCarId = storage.selectedCarId || 'car'; // Show araba (main car) character var araba1 = LK.getAsset('car', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 - 500, y: 1000, scaleX: 1.2, scaleY: 1.2 }); garageOverlay.addChild(araba1); // Show araba2 character var araba2 = LK.getAsset('araba2', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 1000, scaleX: 1.2, scaleY: 1.2 }); garageOverlay.addChild(araba2); // Show araba3 character var araba3 = LK.getAsset('araba3', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 + 500, y: 1000, scaleX: 1.2, scaleY: 1.2 }); garageOverlay.addChild(araba3); // --- Car unlock/lock overlays and buy buttons --- var araba2LockTxt = null; var araba2BuyBtn = null; var araba3LockTxt = null; var araba3BuyBtn = null; function updateCarLockStates() { // All cars are unlocked, so remove any lock/buy overlays if present if (araba2LockTxt) { garageOverlay.removeChild(araba2LockTxt); araba2LockTxt = null; } if (araba2BuyBtn) { garageOverlay.removeChild(araba2BuyBtn); araba2BuyBtn = null; } if (araba3LockTxt) { garageOverlay.removeChild(araba3LockTxt); araba3LockTxt = null; } if (araba3BuyBtn) { garageOverlay.removeChild(araba3BuyBtn); araba3BuyBtn = null; } } updateCarLockStates(); // --- Car selection highlight --- var araba1Border = new Container(); var araba2Border = new Container(); var araba3Border = new Container(); function updateCarSelectionHighlight() { // Remove all children first araba1Border.removeChildren(); araba2Border.removeChildren(); araba3Border.removeChildren(); // Draw a simple border using a colored rectangle asset if (selectedCarId === 'car') { var border = LK.getAsset('road', { anchorX: 0.5, anchorY: 0.5, width: araba1.width + 30, height: araba1.height + 30, x: araba1.x, y: araba1.y }); border.alpha = 0.18; border.tint = 0x00ff00; araba1Border.addChild(border); } if (selectedCarId === 'araba2') { var border = LK.getAsset('road', { anchorX: 0.5, anchorY: 0.5, width: araba2.width + 30, height: araba2.height + 30, x: araba2.x, y: araba2.y }); border.alpha = 0.18; border.tint = 0x00ff00; araba2Border.addChild(border); } if (selectedCarId === 'araba3') { var border = LK.getAsset('road', { anchorX: 0.5, anchorY: 0.5, width: araba3.width + 30, height: araba3.height + 30, x: araba3.x, y: araba3.y }); border.alpha = 0.18; border.tint = 0x00ff00; araba3Border.addChild(border); } } araba1Border.x = 0; araba1Border.y = 0; araba2Border.x = 0; araba2Border.y = 0; araba3Border.x = 0; araba3Border.y = 0; garageOverlay.addChild(araba1Border); garageOverlay.addChild(araba2Border); garageOverlay.addChild(araba3Border); updateCarSelectionHighlight(); // --- Car selection interaction --- araba1.interactive = true; araba1.buttonMode = true; araba1.down = function (x, y, obj) { selectedCarId = 'car'; storage.selectedCarId = selectedCarId; updateCarSelectionHighlight(); }; araba2.interactive = true; araba2.buttonMode = true; araba2.down = function (x, y, obj) { if (unlockedCars.araba2) { selectedCarId = 'araba2'; storage.selectedCarId = selectedCarId; updateCarSelectionHighlight(); } }; araba3.interactive = true; araba3.buttonMode = true; araba3.down = function (x, y, obj) { if (unlockedCars.araba3) { selectedCarId = 'araba3'; storage.selectedCarId = selectedCarId; updateCarSelectionHighlight(); } }; // Add a close button to return to start screen var closeGarageBtn = new Text2("Kapat", { size: 90, fill: 0xff4444 }); closeGarageBtn.anchor.set(0.5, 0.5); closeGarageBtn.x = 2048 / 2; closeGarageBtn.y = 1800; closeGarageBtn.interactive = true; closeGarageBtn.buttonMode = true; closeGarageBtn.down = function (x, y, obj) { garageOverlay.visible = false; startOverlay.visible = true; }; garageOverlay.addChild(closeGarageBtn); game.addChild(garageOverlay); garageBtn.down = function (x, y, obj) { // Only allow navigation if still on start screen if (!gameStarted) { startOverlay.visible = false; garageOverlay.visible = true; } }; startOverlay.addChild(garageBtn); game.addChild(startOverlay); // Play 'arkaplan' music on the start screen overlay LK.playMusic('arkaplan'); // Touch handler: start game if on start screen and not tapping garage, otherwise jump game.down = function (x, y, obj) { if (!gameStarted) { // If garage overlay is visible, ignore all taps except closeGarageBtn (handled by its own .down) if (garageOverlay.visible) { return; } // If tap is on the garage button, let its own .down handle it // Use global coordinates for tap and garageBtn var btnGlobalX = garageBtn.x; var btnGlobalY = garageBtn.y; var btnBounds = { x: btnGlobalX - garageBtn.width * garageBtn.anchor.x, y: btnGlobalY - garageBtn.height * garageBtn.anchor.y, width: garageBtn.width, height: garageBtn.height }; if (x >= btnBounds.x && x <= btnBounds.x + btnBounds.width && y >= btnBounds.y && y <= btnBounds.y + btnBounds.height) { // Let garageBtn.down handle it, do NOT start the game return; } // Otherwise, start the game ONLY if not tapping garage button if (!garageOverlay.visible) { startOverlay.visible = false; gameStarted = true; // --- Set car asset based on selection --- if (selectedCarId === 'araba2' && unlockedCars.araba2) { // Remove old car asset if (car.children.length > 0) car.removeChild(car.children[0]); var carBody = car.attachAsset('araba2', { anchorX: 0.5, anchorY: 1 }); car.bodyWidth = carBody.width; car.bodyHeight = carBody.height; // Fix araba2 to be visually on top of the road car.y = groundY + 5; // Adjust as needed for best look } else if (selectedCarId === 'araba3' && unlockedCars.araba3) { // Remove old car asset if (car.children.length > 0) car.removeChild(car.children[0]); var carBody = car.attachAsset('araba3', { anchorX: 0.5, anchorY: 1 }); car.bodyWidth = carBody.width; car.bodyHeight = carBody.height; // Fix araba3 to be visually on top of the road car.y = groundY + 10; // Adjust as needed for best look } else { // Remove old car asset if (car.children.length > 0) car.removeChild(car.children[0]); var carBody = car.attachAsset('car', { anchorX: 0.5, anchorY: 1 }); car.bodyWidth = carBody.width; car.bodyHeight = carBody.height; } updateLivesDisplay(); // Stop menu music when game starts LK.stopMusic(); return; } } car.jump(); }; // --- Bulut (cloud) background logic --- // Only define bulutlar array here, spawn logic will be in game.update after gameStarted if (typeof bulutlar === "undefined") { var bulutlar = []; } // Main update loop game.update = function () { if (!gameStarted) return; // --- Bulut (cloud) background logic (spawn/update only after gameStarted) --- if (bulutlar.length === 0) { // Spawn initial clouds for (var i = 0; i < 4; i++) { var bulut = new Bulut(); bulut.x = 400 + i * 500 + Math.random() * 200; bulut.y = 400 + Math.random() * 600; bulut.speed = 1.2 + Math.random() * 1.8; game.addChild(bulut); bulutlar.push(bulut); } } for (var i = bulutlar.length - 1; i >= 0; i--) { var bulut = bulutlar[i]; bulut.update(); if (bulut.x < -bulut.width / 2) { bulut.destroy(); bulutlar.splice(i, 1); } } // Spawn new bulut if needed if (bulutlar.length < 4) { var bulut = new Bulut(); bulut.x = 2048 + bulut.width / 2 + Math.random() * 200; bulut.y = 300 + Math.random() * 800; bulut.speed = 1.2 + Math.random() * 1.8; game.addChild(bulut); bulutlar.push(bulut); } ticksSinceStart++; // Increase game speed smoothly over time for gradual acceleration if (gameSpeed < maxGameSpeed) { // Accelerate slowly at first, then faster as time goes on // The divisor controls how quickly speed ramps up (higher = slower ramp) var speedup = ticksSinceStart / 60 * 0.18; // 0.18 px/frame/sec, adjust for feel gameSpeed = initialGameSpeed + speedup; if (gameSpeed > maxGameSpeed) gameSpeed = maxGameSpeed; } // --- Car slow effect logic --- if (carIsSlowed) { carSlowTicks++; if (carSlowTicks >= carSlowDuration) { carIsSlowed = false; carSlowTicks = 0; } } var effectiveGameSpeed = carIsSlowed ? gameSpeed * carSlowSpeedFactor : gameSpeed; // Update car car.update(); // Update obstacles for (var i = obstacles.length - 1; i >= 0; i--) { var obs = obstacles[i]; // If car is slowed, move obstacles at slowed speed for 2 seconds if (carIsSlowed) { var speed = gameSpeed * carSlowSpeedFactor; obs.x -= speed - (typeof effectiveGameSpeed !== "undefined" ? effectiveGameSpeed : gameSpeed); } obs.update(); // Remove if off screen if (obs.x < -200) { obs.destroy(); obstacles.splice(i, 1); continue; } // Collision detection (AABB) var carLeft = car.x - car.bodyWidth / 2 + 20; var carRight = car.x + car.bodyWidth / 2 - 20; var carTop = car.y - car.bodyHeight; var carBottom = car.y; var obsLeft = obs.x - obs.bodyWidth / 2; var obsRight = obs.x + obs.bodyWidth / 2; var obsTop = obs.y - obs.bodyHeight; var obsBottom = obs.y; var intersect = !(carRight < obsLeft || carLeft > obsRight || carBottom < obsTop + 10 || carTop > obsBottom - 10); if (intersect) { if (goldHeartActive) { // No damage, just animate obstacle tumble and remove after if (typeof tween !== "undefined" && typeof tween.create === "function") { // Animate: rotate 1.5 turns, fall down tween.create(obs, { rotation: obs.rotation + Math.PI * 3, y: obs.y + 400, alpha: 0.2 }, 700, { easing: "easeInCubic" }).then(function () { obs.destroy(); }); } else { obs.destroy(); } obstacles.splice(i, 1); continue; } // Play 'engellenmek' sound on collision LK.getSound('engellenmek').play(); // Removed LK.effects.flashScreen(0xff0000, 800); lives--; updateLivesDisplay(); // --- Trigger car slow effect for 2 seconds --- carIsSlowed = true; carSlowTicks = 0; if (lives <= 0) { // Stop car movement and animate tilt to indicate a crash car.vy = 0; car.isJumping = false; // Animate car tilt to crash angle (0.35 radians) smoothly if (typeof tween !== "undefined" && typeof tween.create === "function") { tween.create(car.children[0], { rotation: 0.35 }, 320, { easing: "easeOutCubic" }); } else { car.children[0].rotation = 0.35; } LK.setScore(score); // Ensure latest score is set lastScore = score; lastScoreTxt.setText("Son Skor: " + lastScore); // Update highest score if needed if (score > highestScore) { highestScore = score; storage.highestScore = highestScore; highestScoreTxt.setText("En Yüksek Skor: " + highestScore); } else { // Always show the stored value if not beaten highestScoreTxt.setText("En Yüksek Skor: " + (storage.highestScore || highestScore)); } LK.showGameOver(); return; } else { // Animate obstacle tumble and falling to ground, then remove after if (typeof tween !== "undefined" && typeof tween.create === "function") { tween.create(obs, { rotation: obs.rotation + Math.PI * 3, y: groundY + obs.height, alpha: 0.2 }, 700, { easing: "easeInCubic" }).then(function () { obs.destroy(); }); } else { obs.destroy(); } obstacles.splice(i, 1); continue; } } // Score: passed obstacle if (!obs.passed && obs.x + obs.bodyWidth / 2 < car.x - car.bodyWidth / 2) { obs.passed = true; score++; scoreTxt.setText(score); } } // --- Heart collectibles update and collision --- for (var i = heartCollectibles.length - 1; i >= 0; i--) { var heart = heartCollectibles[i]; // If car is slowed, move hearts at slowed speed for 2 seconds if (carIsSlowed) { var speed = gameSpeed * carSlowSpeedFactor; heart.x -= speed - (typeof effectiveGameSpeed !== "undefined" ? effectiveGameSpeed : gameSpeed); } heart.update(); // Remove if off screen if (heart.x < -200) { heart.destroy(); heartCollectibles.splice(i, 1); continue; } // Collision detection (AABB) var carLeft = car.x - car.bodyWidth / 2 + 20; var carRight = car.x + car.bodyWidth / 2 - 20; var carTop = car.y - car.bodyHeight; var carBottom = car.y; var heartLeft = heart.x - heart.bodyWidth / 2; var heartRight = heart.x + heart.bodyWidth / 2; var heartTop = heart.y - heart.bodyHeight; var heartBottom = heart.y; var intersect = !(carRight < heartLeft || carLeft > heartRight || carBottom < heartTop + 10 || carTop > heartBottom - 10); if (intersect) { if (!goldHeartActive) { // Always increase lives, up to a hard cap of 9 if (lives < 9) { lives++; // If we go above maxLives, update maxLives to match (so display shows more hearts) if (lives > maxLives) maxLives = lives; updateLivesDisplay(); } } // Remove the heart collectible heart.destroy(); heartCollectibles.splice(i, 1); continue; } } // --- Gold heart collectibles update and collision --- for (var i = goldHeartCollectibles.length - 1; i >= 0; i--) { var goldHeart = goldHeartCollectibles[i]; goldHeart.update(); if (goldHeart.x < -200) { goldHeart.destroy(); goldHeartCollectibles.splice(i, 1); continue; } // Collision detection (AABB) var carLeft = car.x - car.bodyWidth / 2 + 20; var carRight = car.x + car.bodyWidth / 2 - 20; var carTop = car.y - car.bodyHeight; var carBottom = car.y; var goldLeft = goldHeart.x - goldHeart.bodyWidth / 2; var goldRight = goldHeart.x + goldHeart.bodyWidth / 2; var goldTop = goldHeart.y - goldHeart.bodyHeight; var goldBottom = goldHeart.y; var intersect = !(carRight < goldLeft || carLeft > goldRight || carBottom < goldTop + 10 || carTop > goldBottom - 10); if (intersect) { // Activate gold heart mode goldHeartActive = true; goldHeartTimer = 0; // Fill lives to max when gold heart is collected lives = maxLives; // Cap goldHeartDisplayLives to 9 to prevent overflow goldHeartDisplayLives = lives > 9 ? 9 : lives; updateLivesDisplay(); // Remove the gold heart collectible goldHeart.destroy(); goldHeartCollectibles.splice(i, 1); continue; } } // --- Coin collectibles update and collision --- for (var i = coinCollectibles.length - 1; i >= 0; i--) { var coin = coinCollectibles[i]; coin.update(); if (coin.x < -200) { coin.destroy(); coinCollectibles.splice(i, 1); continue; } // Collision detection (AABB) var carLeft = car.x - car.bodyWidth / 2 + 20; var carRight = car.x + car.bodyWidth / 2 - 20; var carTop = car.y - car.bodyHeight; var carBottom = car.y; var coinLeft = coin.x - coin.bodyWidth / 2; var coinRight = coin.x + coin.bodyWidth / 2; var coinTop = coin.y - coin.bodyHeight; var coinBottom = coin.y; var intersect = !(carRight < coinLeft || carLeft > coinRight || carBottom < coinTop + 10 || carTop > coinBottom - 10); if (intersect) { // Add coin, persistently coins++; storage.coins = coins; updateCoinDisplay(); coin.destroy(); coinCollectibles.splice(i, 1); continue; } } // Gold heart timer logic if (goldHeartActive) { goldHeartTimer++; if (goldHeartTimer >= goldHeartDuration) { goldHeartActive = false; goldHeartTimer = 0; updateLivesDisplay(); } } // Spawn new obstacles if (obstacles.length === 0 || obstacles.length > 0 && obstacles[obstacles.length - 1].x < 2048 - getNextGap()) { var obs = new Obstacle(); obs.x = 2048 + 100; obs.y = groundY; game.addChild(obs); obstacles.push(obs); } // --- Spawn heart collectibles and coins occasionally --- if ((heartCollectibles.length === 0 || heartCollectibles.length > 0 && heartCollectibles[heartCollectibles.length - 1].x < 2048 - getNextHeartGap()) && goldHeartCollectibles.length === 0 // Only one gold heart at a time ) { var rand = Math.random(); if (rand < 0.02) { // 2% chance: spawn gold heart var goldHeart = new GoldHeartCollectible(); goldHeart.x = 2048 + 200 + Math.floor(Math.random() * 600); goldHeart.y = groundY - 180 - Math.floor(Math.random() * 200); game.addChild(goldHeart); goldHeartCollectibles.push(goldHeart); } else if (rand < 0.44) { // 39% chance: spawn normal heart (reduced by 1%) var heart = new HeartCollectible(); heart.x = 2048 + 200 + Math.floor(Math.random() * 600); heart.y = groundY - 180 - Math.floor(Math.random() * 200); game.addChild(heart); heartCollectibles.push(heart); } else if (rand < 1.00) { // 56% chance: spawn coin (increased by 15%) var coin = new CoinCollectible(); coin.x = 2048 + 200 + Math.floor(Math.random() * 600); coin.y = groundY - 180 - Math.floor(Math.random() * 200); game.addChild(coin); coinCollectibles.push(coin); } } }; // Helper: get next gap (randomized, gets smaller as speed increases) function getNextGap() { var minGap = obstacleMinGap + 400 - Math.floor((gameSpeed - initialGameSpeed) * 10); var maxGap = obstacleMaxGap + 600 - Math.floor((gameSpeed - initialGameSpeed) * 12); if (minGap < 720) minGap = 720; if (maxGap < 1000) maxGap = 1000; // 45% chance to make a much wider gap, otherwise normal if (Math.random() < 0.45) { // Extra wide gap (make even wider) var extraMin = maxGap + 700; var extraMax = maxGap + 1600; return extraMin + Math.floor(Math.random() * (extraMax - extraMin)); } else { // Normal gap return minGap + Math.floor(Math.random() * (maxGap - minGap)); } } // Helper: get next heart collectible gap (randomized, not too frequent) function getNextHeartGap() { // Hearts are less frequent than obstacles var minGap = 1200; var maxGap = 2200; return minGap + Math.floor(Math.random() * (maxGap - minGap)); } // Reset score and lives on game start LK.setScore(0); score = 0; // On game start, reset maxLives to 3, but if lives was higher, keep it (up to 9) if (lives > 3) { maxLives = lives > 9 ? 9 : lives; } else { maxLives = 3; } lives = maxLives; scoreTxt.setText(score); lastScoreTxt.setText(""); updateLivesDisplay(); heartCollectibles = []; goldHeartCollectibles = []; coinCollectibles = []; goldHeartActive = false; goldHeartTimer = 0; goldHeartDisplayLives = 0; updateCoinDisplay(); if ((storage.highestScore || highestScore) > 0) { highestScoreTxt.setText("En Yüksek Skor: " + (storage.highestScore || highestScore)); } else { highestScoreTxt.setText(""); } // Show leaderboard button in the top GUI, right side var leaderboardBtn = new Text2("🏆", { size: 110, fill: 0xffff00 }); leaderboardBtn.anchor.set(1, 0); // right-top leaderboardBtn.x = -40; // offset from right edge leaderboardBtn.y = 0; leaderboardBtn.interactive = true; leaderboardBtn.buttonMode = true; leaderboardBtn.down = function (x, y, obj) { LK.showLeaderboard(); }; LK.gui.topRight.addChild(leaderboardBtn);
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highestScore: 0
});
/****
* Classes
****/
// Bulut (Cloud) background object class
var Bulut = Container.expand(function () {
var self = Container.call(this);
var bulutImg = self.attachAsset('bulut', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = bulutImg.width;
self.height = bulutImg.height;
// Set a random speed for parallax effect
self.speed = 2 + Math.random() * 2;
self.update = function () {
self.x -= self.speed;
};
return self;
});
// Car class
var Car = Container.expand(function () {
var self = Container.call(this);
// Car body
var carBody = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 1
});
// Physics
self.vy = 0; // vertical velocity
self.isJumping = false;
// Car size for collision
self.bodyWidth = carBody.width;
self.bodyHeight = carBody.height;
// Update method
self.update = function () {
// Gravity and jump physics
self.y += self.vy;
if (self.y < groundY) {
self.vy += gravity;
if (self.y > groundY) {
self.y = groundY;
self.vy = 0;
self.isJumping = false;
}
} else if (self.y > groundY) {
self.y = groundY;
self.vy = 0;
self.isJumping = false;
}
};
// Jump method
self.jump = function () {
if (!self.isJumping && self.y >= groundY) {
self.vy = jumpVelocity;
self.isJumping = true;
LK.getSound('jump').play();
// Tilt the car slightly when jumping, then return to normal after 300ms
if (typeof tween !== "undefined" && typeof tween.create === "function") {
// Animate tilt to -0.25
var tiltTween = tween.create(carBody, {
rotation: -0.25
}, 120, {
easing: "easeOutCubic"
});
tiltTween.then(function () {
// Animate back to 0
var resetTween = tween.create(carBody, {
rotation: 0
}, 180, {
easing: "easeInCubic"
});
// No need to do anything after reset
});
} else {
carBody.rotation = -0.25;
// Fallback: reset after 300ms
LK.setTimeout(function () {
carBody.rotation = 0;
}, 300);
}
}
};
return self;
});
// Coin collectible class
var CoinCollectible = Container.expand(function () {
var self = Container.call(this);
// Use a yellow coin emoji for collectible
var coinTxt = new Text2('🪙', {
size: 110,
fill: 0xffe066 // gold/yellow
});
coinTxt.anchor.set(0.5, 1);
self.addChild(coinTxt);
self.width = coinTxt.width;
self.height = coinTxt.height;
// For collision
self.bodyWidth = coinTxt.width;
self.bodyHeight = coinTxt.height;
self.update = function () {
var speed = typeof effectiveGameSpeed !== "undefined" ? effectiveGameSpeed : gameSpeed;
self.x -= speed;
};
return self;
});
// Gold heart collectible class
var GoldHeartCollectible = Container.expand(function () {
var self = Container.call(this);
// Use a text heart for collectible, gold color
var goldHeartTxt = new Text2('❤', {
size: 120,
fill: 0xffd700 // gold
});
goldHeartTxt.anchor.set(0.5, 1);
self.addChild(goldHeartTxt);
self.width = goldHeartTxt.width;
self.height = goldHeartTxt.height;
// For collision
self.bodyWidth = goldHeartTxt.width;
self.bodyHeight = goldHeartTxt.height;
self.update = function () {
var speed = typeof effectiveGameSpeed !== "undefined" ? effectiveGameSpeed : gameSpeed;
self.x -= speed;
};
return self;
});
// Heart collectible class
var HeartCollectible = Container.expand(function () {
var self = Container.call(this);
// Use a text heart for collectible
var heartTxt = new Text2('❤', {
size: 92,
fill: 0xff69b4
});
heartTxt.anchor.set(0.5, 1);
self.addChild(heartTxt);
self.width = heartTxt.width;
self.height = heartTxt.height;
// For collision
self.bodyWidth = heartTxt.width;
self.bodyHeight = heartTxt.height;
// Update method
self.update = function () {
// Use effectiveGameSpeed if defined, else fallback to gameSpeed
var speed = typeof effectiveGameSpeed !== "undefined" ? effectiveGameSpeed : gameSpeed;
self.x -= speed;
};
return self;
});
// Obstacle class
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obs = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 1
});
self.width = obs.width;
self.height = obs.height;
// For collision
self.bodyWidth = obs.width;
self.bodyHeight = obs.height;
// Update method
self.update = function () {
// Use effectiveGameSpeed if defined, else fallback to gameSpeed
var speed = typeof effectiveGameSpeed !== "undefined" ? effectiveGameSpeed : gameSpeed;
self.x -= speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87ceeb // sky blue
});
/****
* Game Code
****/
// Jump sound
// Road: dark gray box
// Obstacle: red box
// Wheel: black ellipse
// Car: blue box
// Constants
// Left-right moving obstacle asset
var groundY = 2200; // y position of the road top
var gravity = 4.5;
var jumpVelocity = -80;
var initialGameSpeed = 24;
var maxGameSpeed = 60;
var gameSpeed = initialGameSpeed;
var obstacleMinGap = 500;
var obstacleMaxGap = 1100;
var obstacleMinY = groundY;
var obstacleMaxY = groundY;
var minObstacleHeight = 120;
var maxObstacleHeight = 220;
// Road
var road = LK.getAsset('road', {
anchorX: 0,
anchorY: 0,
x: 0,
y: groundY
});
game.addChild(road);
// Car
var car = new Car();
car.x = 400;
// Move the car visually closer to the road (simulate being on the road, not floating above)
car.y = groundY + 20; // 20px below the road top, adjust as needed for best look
game.addChild(car);
// --- Slow effect state ---
var carIsSlowed = false;
var carSlowTicks = 0;
var carSlowDuration = 120; // 2 seconds at 60fps
var carSlowSpeedFactor = 0.45; // 45% speed when slowed
// Obstacles
var obstacles = [];
var nextObstacleX = 2048 + 400;
// Heart collectibles
var heartCollectibles = [];
var nextHeartX = 2048 + 1200; // Start further out
// Gold heart collectibles
var goldHeartCollectibles = [];
var goldHeartActive = false;
var goldHeartTimer = 0;
var goldHeartDuration = 8 * 60; // 8 seconds at 60fps
var goldHeartDisplay = null;
var goldHeartDisplayLives = 0;
// --- Coin system ---
// Persistent coin count (load from storage, default 0)
var coins = typeof storage.coins === "number" ? storage.coins : 0;
// Coin collectibles
var coinCollectibles = [];
// Coin display
var coinTxt = new Text2('', {
size: 90,
fill: 0xffe066
});
coinTxt.anchor.set(1, 1);
coinTxt.x = -40;
coinTxt.y = -40;
LK.gui.bottomRight.addChild(coinTxt);
function updateCoinDisplay() {
coinTxt.setText("Jeton: " + coins);
}
updateCoinDisplay();
// Score
var score = 0;
var lastScore = 0;
var lives = 3; // Start with 3 lives
var maxLives = 3; // This can grow up to 9 as hearts are collected
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Lives display
var livesTxt = null;
var goldHeartDisplay = null;
function updateLivesDisplay() {
if (!gameStarted) {
// Hide lives display on start screen
if (livesTxt) livesTxt.setText("");
if (goldHeartDisplay) goldHeartDisplay.setText("");
return;
}
if (!livesTxt) {
livesTxt = new Text2('', {
size: 90,
fill: 0xff4444
});
livesTxt.anchor.set(0, 1);
// Shift a bit more left to avoid coin overlap
livesTxt.x = 10;
livesTxt.y = -40;
LK.gui.bottomLeft.addChild(livesTxt);
}
if (goldHeartActive) {
// Hide normal lives, show gold hearts
if (!goldHeartDisplay) {
goldHeartDisplay = new Text2('', {
size: 76,
fill: 0xffd700
});
goldHeartDisplay.anchor.set(0, 1);
goldHeartDisplay.x = 18;
goldHeartDisplay.y = -40;
LK.gui.bottomLeft.addChild(goldHeartDisplay);
}
var hearts = '';
// Always cap display to 9 hearts maximum to prevent overflow
var displayMax = maxLives > 9 ? 9 : maxLives;
var displayLives = goldHeartDisplayLives > 9 ? 9 : goldHeartDisplayLives;
for (var i = 0; i < displayLives; i++) hearts += '❤ ';
for (var i = displayLives; i < displayMax; i++) hearts += '♡ ';
goldHeartDisplay.setText("Altın Can: " + hearts.trim());
livesTxt.setText(""); // Hide normal
} else {
// Show normal lives, hide gold
if (goldHeartDisplay) goldHeartDisplay.setText("");
var hearts = '';
var displayMax = maxLives > 9 ? 9 : maxLives;
for (var i = 0; i < lives && i < 9; i++) hearts += '❤ ';
for (var i = lives; i < displayMax; i++) hearts += '♡ ';
livesTxt.setText("Can: " + hearts.trim());
}
}
updateLivesDisplay();
// Last score display
var lastScoreTxt = new Text2('', {
size: 70,
fill: 0xFFD700
});
lastScoreTxt.anchor.set(0.5, 0);
lastScoreTxt.y = scoreTxt.height + 10;
LK.gui.top.addChild(lastScoreTxt);
// Highest score display (below leaderboard)
var highestScore = storage.highestScore || 0;
var highestScoreTxt = new Text2('', {
size: 70,
fill: 0x00ffcc
});
highestScoreTxt.anchor.set(0.5, 0);
highestScoreTxt.y = lastScoreTxt.y + lastScoreTxt.height + 10;
LK.gui.top.addChild(highestScoreTxt);
// Difficulty
var ticksSinceStart = 0;
// --- Start Screen Overlay ---
var gameStarted = false;
var startOverlay = new Container();
var startBg = LK.getAsset('road', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
});
startBg.alpha = 0.82;
startOverlay.addChild(startBg);
// Add a visual image to the start overlay
var startVisual = LK.getAsset('car', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 700,
scaleX: 2.2,
scaleY: 2.2
});
startOverlay.addChild(startVisual);
var startText = new Text2("Araba Koşusu", {
size: 180,
fill: 0xffffff
});
startText.anchor.set(0.5, 0.5);
startText.x = 2048 / 2;
startText.y = 900;
startOverlay.addChild(startText);
var tapText = new Text2("Başlamak için ekrana dokun", {
size: 90,
fill: 0xffd700
});
tapText.anchor.set(0.5, 0.5);
tapText.x = 2048 / 2;
tapText.y = 1200;
startOverlay.addChild(tapText);
// --- Garage Button ---
var garageBtn = new Text2("Garaj", {
size: 100,
fill: 0x00bfff
});
garageBtn.anchor.set(0.5, 0.5);
// Place below the start text, centered
garageBtn.x = 2048 / 2;
garageBtn.y = 1400;
garageBtn.interactive = true;
garageBtn.buttonMode = true;
// --- Garage Overlay ---
var garageOverlay = new Container();
garageOverlay.visible = false;
// Add a semi-transparent background
var garageBg = LK.getAsset('road', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
});
garageBg.alpha = 0.92;
garageOverlay.addChild(garageBg);
// Garage title
var garageTitle = new Text2("Garaj", {
size: 160,
fill: 0xffffff
});
garageTitle.anchor.set(0.5, 0.5);
garageTitle.x = 2048 / 2;
garageTitle.y = 400;
garageOverlay.addChild(garageTitle);
// --- Car selection state ---
// Persistent unlock state for cars (all unlocked)
var unlockedCars = {
car: true,
araba2: true,
araba3: true
};
var selectedCarId = storage.selectedCarId || 'car';
// Show araba (main car) character
var araba1 = LK.getAsset('car', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 500,
y: 1000,
scaleX: 1.2,
scaleY: 1.2
});
garageOverlay.addChild(araba1);
// Show araba2 character
var araba2 = LK.getAsset('araba2', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 1000,
scaleX: 1.2,
scaleY: 1.2
});
garageOverlay.addChild(araba2);
// Show araba3 character
var araba3 = LK.getAsset('araba3', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 + 500,
y: 1000,
scaleX: 1.2,
scaleY: 1.2
});
garageOverlay.addChild(araba3);
// --- Car unlock/lock overlays and buy buttons ---
var araba2LockTxt = null;
var araba2BuyBtn = null;
var araba3LockTxt = null;
var araba3BuyBtn = null;
function updateCarLockStates() {
// All cars are unlocked, so remove any lock/buy overlays if present
if (araba2LockTxt) {
garageOverlay.removeChild(araba2LockTxt);
araba2LockTxt = null;
}
if (araba2BuyBtn) {
garageOverlay.removeChild(araba2BuyBtn);
araba2BuyBtn = null;
}
if (araba3LockTxt) {
garageOverlay.removeChild(araba3LockTxt);
araba3LockTxt = null;
}
if (araba3BuyBtn) {
garageOverlay.removeChild(araba3BuyBtn);
araba3BuyBtn = null;
}
}
updateCarLockStates();
// --- Car selection highlight ---
var araba1Border = new Container();
var araba2Border = new Container();
var araba3Border = new Container();
function updateCarSelectionHighlight() {
// Remove all children first
araba1Border.removeChildren();
araba2Border.removeChildren();
araba3Border.removeChildren();
// Draw a simple border using a colored rectangle asset
if (selectedCarId === 'car') {
var border = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
width: araba1.width + 30,
height: araba1.height + 30,
x: araba1.x,
y: araba1.y
});
border.alpha = 0.18;
border.tint = 0x00ff00;
araba1Border.addChild(border);
}
if (selectedCarId === 'araba2') {
var border = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
width: araba2.width + 30,
height: araba2.height + 30,
x: araba2.x,
y: araba2.y
});
border.alpha = 0.18;
border.tint = 0x00ff00;
araba2Border.addChild(border);
}
if (selectedCarId === 'araba3') {
var border = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
width: araba3.width + 30,
height: araba3.height + 30,
x: araba3.x,
y: araba3.y
});
border.alpha = 0.18;
border.tint = 0x00ff00;
araba3Border.addChild(border);
}
}
araba1Border.x = 0;
araba1Border.y = 0;
araba2Border.x = 0;
araba2Border.y = 0;
araba3Border.x = 0;
araba3Border.y = 0;
garageOverlay.addChild(araba1Border);
garageOverlay.addChild(araba2Border);
garageOverlay.addChild(araba3Border);
updateCarSelectionHighlight();
// --- Car selection interaction ---
araba1.interactive = true;
araba1.buttonMode = true;
araba1.down = function (x, y, obj) {
selectedCarId = 'car';
storage.selectedCarId = selectedCarId;
updateCarSelectionHighlight();
};
araba2.interactive = true;
araba2.buttonMode = true;
araba2.down = function (x, y, obj) {
if (unlockedCars.araba2) {
selectedCarId = 'araba2';
storage.selectedCarId = selectedCarId;
updateCarSelectionHighlight();
}
};
araba3.interactive = true;
araba3.buttonMode = true;
araba3.down = function (x, y, obj) {
if (unlockedCars.araba3) {
selectedCarId = 'araba3';
storage.selectedCarId = selectedCarId;
updateCarSelectionHighlight();
}
};
// Add a close button to return to start screen
var closeGarageBtn = new Text2("Kapat", {
size: 90,
fill: 0xff4444
});
closeGarageBtn.anchor.set(0.5, 0.5);
closeGarageBtn.x = 2048 / 2;
closeGarageBtn.y = 1800;
closeGarageBtn.interactive = true;
closeGarageBtn.buttonMode = true;
closeGarageBtn.down = function (x, y, obj) {
garageOverlay.visible = false;
startOverlay.visible = true;
};
garageOverlay.addChild(closeGarageBtn);
game.addChild(garageOverlay);
garageBtn.down = function (x, y, obj) {
// Only allow navigation if still on start screen
if (!gameStarted) {
startOverlay.visible = false;
garageOverlay.visible = true;
}
};
startOverlay.addChild(garageBtn);
game.addChild(startOverlay);
// Play 'arkaplan' music on the start screen overlay
LK.playMusic('arkaplan');
// Touch handler: start game if on start screen and not tapping garage, otherwise jump
game.down = function (x, y, obj) {
if (!gameStarted) {
// If garage overlay is visible, ignore all taps except closeGarageBtn (handled by its own .down)
if (garageOverlay.visible) {
return;
}
// If tap is on the garage button, let its own .down handle it
// Use global coordinates for tap and garageBtn
var btnGlobalX = garageBtn.x;
var btnGlobalY = garageBtn.y;
var btnBounds = {
x: btnGlobalX - garageBtn.width * garageBtn.anchor.x,
y: btnGlobalY - garageBtn.height * garageBtn.anchor.y,
width: garageBtn.width,
height: garageBtn.height
};
if (x >= btnBounds.x && x <= btnBounds.x + btnBounds.width && y >= btnBounds.y && y <= btnBounds.y + btnBounds.height) {
// Let garageBtn.down handle it, do NOT start the game
return;
}
// Otherwise, start the game ONLY if not tapping garage button
if (!garageOverlay.visible) {
startOverlay.visible = false;
gameStarted = true;
// --- Set car asset based on selection ---
if (selectedCarId === 'araba2' && unlockedCars.araba2) {
// Remove old car asset
if (car.children.length > 0) car.removeChild(car.children[0]);
var carBody = car.attachAsset('araba2', {
anchorX: 0.5,
anchorY: 1
});
car.bodyWidth = carBody.width;
car.bodyHeight = carBody.height;
// Fix araba2 to be visually on top of the road
car.y = groundY + 5; // Adjust as needed for best look
} else if (selectedCarId === 'araba3' && unlockedCars.araba3) {
// Remove old car asset
if (car.children.length > 0) car.removeChild(car.children[0]);
var carBody = car.attachAsset('araba3', {
anchorX: 0.5,
anchorY: 1
});
car.bodyWidth = carBody.width;
car.bodyHeight = carBody.height;
// Fix araba3 to be visually on top of the road
car.y = groundY + 10; // Adjust as needed for best look
} else {
// Remove old car asset
if (car.children.length > 0) car.removeChild(car.children[0]);
var carBody = car.attachAsset('car', {
anchorX: 0.5,
anchorY: 1
});
car.bodyWidth = carBody.width;
car.bodyHeight = carBody.height;
}
updateLivesDisplay();
// Stop menu music when game starts
LK.stopMusic();
return;
}
}
car.jump();
};
// --- Bulut (cloud) background logic ---
// Only define bulutlar array here, spawn logic will be in game.update after gameStarted
if (typeof bulutlar === "undefined") {
var bulutlar = [];
}
// Main update loop
game.update = function () {
if (!gameStarted) return;
// --- Bulut (cloud) background logic (spawn/update only after gameStarted) ---
if (bulutlar.length === 0) {
// Spawn initial clouds
for (var i = 0; i < 4; i++) {
var bulut = new Bulut();
bulut.x = 400 + i * 500 + Math.random() * 200;
bulut.y = 400 + Math.random() * 600;
bulut.speed = 1.2 + Math.random() * 1.8;
game.addChild(bulut);
bulutlar.push(bulut);
}
}
for (var i = bulutlar.length - 1; i >= 0; i--) {
var bulut = bulutlar[i];
bulut.update();
if (bulut.x < -bulut.width / 2) {
bulut.destroy();
bulutlar.splice(i, 1);
}
}
// Spawn new bulut if needed
if (bulutlar.length < 4) {
var bulut = new Bulut();
bulut.x = 2048 + bulut.width / 2 + Math.random() * 200;
bulut.y = 300 + Math.random() * 800;
bulut.speed = 1.2 + Math.random() * 1.8;
game.addChild(bulut);
bulutlar.push(bulut);
}
ticksSinceStart++;
// Increase game speed smoothly over time for gradual acceleration
if (gameSpeed < maxGameSpeed) {
// Accelerate slowly at first, then faster as time goes on
// The divisor controls how quickly speed ramps up (higher = slower ramp)
var speedup = ticksSinceStart / 60 * 0.18; // 0.18 px/frame/sec, adjust for feel
gameSpeed = initialGameSpeed + speedup;
if (gameSpeed > maxGameSpeed) gameSpeed = maxGameSpeed;
}
// --- Car slow effect logic ---
if (carIsSlowed) {
carSlowTicks++;
if (carSlowTicks >= carSlowDuration) {
carIsSlowed = false;
carSlowTicks = 0;
}
}
var effectiveGameSpeed = carIsSlowed ? gameSpeed * carSlowSpeedFactor : gameSpeed;
// Update car
car.update();
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
// If car is slowed, move obstacles at slowed speed for 2 seconds
if (carIsSlowed) {
var speed = gameSpeed * carSlowSpeedFactor;
obs.x -= speed - (typeof effectiveGameSpeed !== "undefined" ? effectiveGameSpeed : gameSpeed);
}
obs.update();
// Remove if off screen
if (obs.x < -200) {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
// Collision detection (AABB)
var carLeft = car.x - car.bodyWidth / 2 + 20;
var carRight = car.x + car.bodyWidth / 2 - 20;
var carTop = car.y - car.bodyHeight;
var carBottom = car.y;
var obsLeft = obs.x - obs.bodyWidth / 2;
var obsRight = obs.x + obs.bodyWidth / 2;
var obsTop = obs.y - obs.bodyHeight;
var obsBottom = obs.y;
var intersect = !(carRight < obsLeft || carLeft > obsRight || carBottom < obsTop + 10 || carTop > obsBottom - 10);
if (intersect) {
if (goldHeartActive) {
// No damage, just animate obstacle tumble and remove after
if (typeof tween !== "undefined" && typeof tween.create === "function") {
// Animate: rotate 1.5 turns, fall down
tween.create(obs, {
rotation: obs.rotation + Math.PI * 3,
y: obs.y + 400,
alpha: 0.2
}, 700, {
easing: "easeInCubic"
}).then(function () {
obs.destroy();
});
} else {
obs.destroy();
}
obstacles.splice(i, 1);
continue;
}
// Play 'engellenmek' sound on collision
LK.getSound('engellenmek').play();
// Removed LK.effects.flashScreen(0xff0000, 800);
lives--;
updateLivesDisplay();
// --- Trigger car slow effect for 2 seconds ---
carIsSlowed = true;
carSlowTicks = 0;
if (lives <= 0) {
// Stop car movement and animate tilt to indicate a crash
car.vy = 0;
car.isJumping = false;
// Animate car tilt to crash angle (0.35 radians) smoothly
if (typeof tween !== "undefined" && typeof tween.create === "function") {
tween.create(car.children[0], {
rotation: 0.35
}, 320, {
easing: "easeOutCubic"
});
} else {
car.children[0].rotation = 0.35;
}
LK.setScore(score); // Ensure latest score is set
lastScore = score;
lastScoreTxt.setText("Son Skor: " + lastScore);
// Update highest score if needed
if (score > highestScore) {
highestScore = score;
storage.highestScore = highestScore;
highestScoreTxt.setText("En Yüksek Skor: " + highestScore);
} else {
// Always show the stored value if not beaten
highestScoreTxt.setText("En Yüksek Skor: " + (storage.highestScore || highestScore));
}
LK.showGameOver();
return;
} else {
// Animate obstacle tumble and falling to ground, then remove after
if (typeof tween !== "undefined" && typeof tween.create === "function") {
tween.create(obs, {
rotation: obs.rotation + Math.PI * 3,
y: groundY + obs.height,
alpha: 0.2
}, 700, {
easing: "easeInCubic"
}).then(function () {
obs.destroy();
});
} else {
obs.destroy();
}
obstacles.splice(i, 1);
continue;
}
}
// Score: passed obstacle
if (!obs.passed && obs.x + obs.bodyWidth / 2 < car.x - car.bodyWidth / 2) {
obs.passed = true;
score++;
scoreTxt.setText(score);
}
}
// --- Heart collectibles update and collision ---
for (var i = heartCollectibles.length - 1; i >= 0; i--) {
var heart = heartCollectibles[i];
// If car is slowed, move hearts at slowed speed for 2 seconds
if (carIsSlowed) {
var speed = gameSpeed * carSlowSpeedFactor;
heart.x -= speed - (typeof effectiveGameSpeed !== "undefined" ? effectiveGameSpeed : gameSpeed);
}
heart.update();
// Remove if off screen
if (heart.x < -200) {
heart.destroy();
heartCollectibles.splice(i, 1);
continue;
}
// Collision detection (AABB)
var carLeft = car.x - car.bodyWidth / 2 + 20;
var carRight = car.x + car.bodyWidth / 2 - 20;
var carTop = car.y - car.bodyHeight;
var carBottom = car.y;
var heartLeft = heart.x - heart.bodyWidth / 2;
var heartRight = heart.x + heart.bodyWidth / 2;
var heartTop = heart.y - heart.bodyHeight;
var heartBottom = heart.y;
var intersect = !(carRight < heartLeft || carLeft > heartRight || carBottom < heartTop + 10 || carTop > heartBottom - 10);
if (intersect) {
if (!goldHeartActive) {
// Always increase lives, up to a hard cap of 9
if (lives < 9) {
lives++;
// If we go above maxLives, update maxLives to match (so display shows more hearts)
if (lives > maxLives) maxLives = lives;
updateLivesDisplay();
}
}
// Remove the heart collectible
heart.destroy();
heartCollectibles.splice(i, 1);
continue;
}
}
// --- Gold heart collectibles update and collision ---
for (var i = goldHeartCollectibles.length - 1; i >= 0; i--) {
var goldHeart = goldHeartCollectibles[i];
goldHeart.update();
if (goldHeart.x < -200) {
goldHeart.destroy();
goldHeartCollectibles.splice(i, 1);
continue;
}
// Collision detection (AABB)
var carLeft = car.x - car.bodyWidth / 2 + 20;
var carRight = car.x + car.bodyWidth / 2 - 20;
var carTop = car.y - car.bodyHeight;
var carBottom = car.y;
var goldLeft = goldHeart.x - goldHeart.bodyWidth / 2;
var goldRight = goldHeart.x + goldHeart.bodyWidth / 2;
var goldTop = goldHeart.y - goldHeart.bodyHeight;
var goldBottom = goldHeart.y;
var intersect = !(carRight < goldLeft || carLeft > goldRight || carBottom < goldTop + 10 || carTop > goldBottom - 10);
if (intersect) {
// Activate gold heart mode
goldHeartActive = true;
goldHeartTimer = 0;
// Fill lives to max when gold heart is collected
lives = maxLives;
// Cap goldHeartDisplayLives to 9 to prevent overflow
goldHeartDisplayLives = lives > 9 ? 9 : lives;
updateLivesDisplay();
// Remove the gold heart collectible
goldHeart.destroy();
goldHeartCollectibles.splice(i, 1);
continue;
}
}
// --- Coin collectibles update and collision ---
for (var i = coinCollectibles.length - 1; i >= 0; i--) {
var coin = coinCollectibles[i];
coin.update();
if (coin.x < -200) {
coin.destroy();
coinCollectibles.splice(i, 1);
continue;
}
// Collision detection (AABB)
var carLeft = car.x - car.bodyWidth / 2 + 20;
var carRight = car.x + car.bodyWidth / 2 - 20;
var carTop = car.y - car.bodyHeight;
var carBottom = car.y;
var coinLeft = coin.x - coin.bodyWidth / 2;
var coinRight = coin.x + coin.bodyWidth / 2;
var coinTop = coin.y - coin.bodyHeight;
var coinBottom = coin.y;
var intersect = !(carRight < coinLeft || carLeft > coinRight || carBottom < coinTop + 10 || carTop > coinBottom - 10);
if (intersect) {
// Add coin, persistently
coins++;
storage.coins = coins;
updateCoinDisplay();
coin.destroy();
coinCollectibles.splice(i, 1);
continue;
}
}
// Gold heart timer logic
if (goldHeartActive) {
goldHeartTimer++;
if (goldHeartTimer >= goldHeartDuration) {
goldHeartActive = false;
goldHeartTimer = 0;
updateLivesDisplay();
}
}
// Spawn new obstacles
if (obstacles.length === 0 || obstacles.length > 0 && obstacles[obstacles.length - 1].x < 2048 - getNextGap()) {
var obs = new Obstacle();
obs.x = 2048 + 100;
obs.y = groundY;
game.addChild(obs);
obstacles.push(obs);
}
// --- Spawn heart collectibles and coins occasionally ---
if ((heartCollectibles.length === 0 || heartCollectibles.length > 0 && heartCollectibles[heartCollectibles.length - 1].x < 2048 - getNextHeartGap()) && goldHeartCollectibles.length === 0 // Only one gold heart at a time
) {
var rand = Math.random();
if (rand < 0.02) {
// 2% chance: spawn gold heart
var goldHeart = new GoldHeartCollectible();
goldHeart.x = 2048 + 200 + Math.floor(Math.random() * 600);
goldHeart.y = groundY - 180 - Math.floor(Math.random() * 200);
game.addChild(goldHeart);
goldHeartCollectibles.push(goldHeart);
} else if (rand < 0.44) {
// 39% chance: spawn normal heart (reduced by 1%)
var heart = new HeartCollectible();
heart.x = 2048 + 200 + Math.floor(Math.random() * 600);
heart.y = groundY - 180 - Math.floor(Math.random() * 200);
game.addChild(heart);
heartCollectibles.push(heart);
} else if (rand < 1.00) {
// 56% chance: spawn coin (increased by 15%)
var coin = new CoinCollectible();
coin.x = 2048 + 200 + Math.floor(Math.random() * 600);
coin.y = groundY - 180 - Math.floor(Math.random() * 200);
game.addChild(coin);
coinCollectibles.push(coin);
}
}
};
// Helper: get next gap (randomized, gets smaller as speed increases)
function getNextGap() {
var minGap = obstacleMinGap + 400 - Math.floor((gameSpeed - initialGameSpeed) * 10);
var maxGap = obstacleMaxGap + 600 - Math.floor((gameSpeed - initialGameSpeed) * 12);
if (minGap < 720) minGap = 720;
if (maxGap < 1000) maxGap = 1000;
// 45% chance to make a much wider gap, otherwise normal
if (Math.random() < 0.45) {
// Extra wide gap (make even wider)
var extraMin = maxGap + 700;
var extraMax = maxGap + 1600;
return extraMin + Math.floor(Math.random() * (extraMax - extraMin));
} else {
// Normal gap
return minGap + Math.floor(Math.random() * (maxGap - minGap));
}
}
// Helper: get next heart collectible gap (randomized, not too frequent)
function getNextHeartGap() {
// Hearts are less frequent than obstacles
var minGap = 1200;
var maxGap = 2200;
return minGap + Math.floor(Math.random() * (maxGap - minGap));
}
// Reset score and lives on game start
LK.setScore(0);
score = 0;
// On game start, reset maxLives to 3, but if lives was higher, keep it (up to 9)
if (lives > 3) {
maxLives = lives > 9 ? 9 : lives;
} else {
maxLives = 3;
}
lives = maxLives;
scoreTxt.setText(score);
lastScoreTxt.setText("");
updateLivesDisplay();
heartCollectibles = [];
goldHeartCollectibles = [];
coinCollectibles = [];
goldHeartActive = false;
goldHeartTimer = 0;
goldHeartDisplayLives = 0;
updateCoinDisplay();
if ((storage.highestScore || highestScore) > 0) {
highestScoreTxt.setText("En Yüksek Skor: " + (storage.highestScore || highestScore));
} else {
highestScoreTxt.setText("");
}
// Show leaderboard button in the top GUI, right side
var leaderboardBtn = new Text2("🏆", {
size: 110,
fill: 0xffff00
});
leaderboardBtn.anchor.set(1, 0); // right-top
leaderboardBtn.x = -40; // offset from right edge
leaderboardBtn.y = 0;
leaderboardBtn.interactive = true;
leaderboardBtn.buttonMode = true;
leaderboardBtn.down = function (x, y, obj) {
LK.showLeaderboard();
};
LK.gui.topRight.addChild(leaderboardBtn);
red car and driver. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
sarı renkli "Stop" tabelası. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
a human. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
altın para, berrak. In-Game asset. 2d. High contrast. No shadows
pixelart, cloud. In-Game asset. 2d. High contrast. No shadows
dark green car and driver. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
blue car and driver. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat