/****
* 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();
});