/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Enemy Car (multiple types)
var EnemyCar = Container.expand(function () {
var self = Container.call(this);
// Randomly pick a type: 0 = normal, 1 = wide, 2 = fast, 3 = obstacle
var type = Math.floor(Math.random() * 4);
self.type = type;
var assetId = 'enemyCar';
var color = 0xe53935;
var width = 180;
var height = 320;
if (type === 1) {
// wide car
width = 260;
color = 0x8e24aa;
} else if (type === 2) {
// fast car
color = 0x43a047;
} else if (type === 3) {
// obstacle (barrier)
assetId = 'obstacle';
width = 220;
height = 120;
color = 0xffb300;
}
var car = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
car.width = width;
car.height = height;
car.tint = color;
self.width = width;
self.height = height;
self.speed = 0;
// For collision
self.lane = 0;
return self;
});
// Lane Mark
var LaneMark = Container.expand(function () {
var self = Container.call(this);
var mark = self.attachAsset('laneMark', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = mark.width;
self.height = mark.height;
self.speed = 0;
return self;
});
// Player Car
var PlayerCar = Container.expand(function (color) {
var self = Container.call(this);
var car = self.attachAsset('playerCar', {
anchorX: 0.5,
anchorY: 0.5
});
if (color !== undefined) {
car.tint = color;
}
// For collision box, use car asset size
self.width = car.width;
self.height = car.height;
// For touch drag
self.isDragging = false;
return self;
});
// Road Edge
var RoadEdge = Container.expand(function () {
var self = Container.call(this);
var edge = self.attachAsset('roadEdge', {
anchorX: 0,
anchorY: 0
});
self.width = edge.width;
self.height = edge.height;
self.speed = 0;
return self;
});
// Tree (composed of trunk and leaves)
var Tree = Container.expand(function () {
var self = Container.call(this);
var trunk = self.attachAsset('treeTrunk', {
anchorX: 0.5,
anchorY: 1
});
trunk.y = 0;
var leaves = self.attachAsset('treeLeaves', {
anchorX: 0.5,
anchorY: 1
});
leaves.y = -trunk.height + 10;
self.width = leaves.width;
self.height = trunk.height + leaves.height;
self.speed = 0;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// tree leaves
// tree trunk
// road edge
// Road background (gray)
// Road lane
// Fuel can
// Enemy car
// Car (player)
// Road background
var roadBg = LK.getAsset('roadBg', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(roadBg);
// Road edges
var leftEdge = new RoadEdge();
leftEdge.x = ROAD_LEFT - leftEdge.width;
leftEdge.y = 0;
game.addChild(leftEdge);
var rightEdge = new RoadEdge();
rightEdge.x = ROAD_RIGHT;
rightEdge.y = 0;
game.addChild(rightEdge);
// Trees setup
var treesLeft = [];
var treesRight = [];
for (var i = 0; i < 8; i++) {
var tL = new Tree();
tL.x = ROAD_LEFT - 80;
tL.y = i * 400 + 100;
tL.speed = 0;
treesLeft.push(tL);
game.addChild(tL);
var tR = new Tree();
tR.x = ROAD_RIGHT + 80;
tR.y = i * 400 + 300;
tR.speed = 0;
treesRight.push(tR);
game.addChild(tR);
}
// Lane setup
var NUM_LANES = 4;
var LANE_WIDTH = 350;
var ROAD_WIDTH = NUM_LANES * LANE_WIDTH;
var ROAD_LEFT = (2048 - ROAD_WIDTH) / 2;
var ROAD_RIGHT = ROAD_LEFT + ROAD_WIDTH;
// Lane marks
var laneMarks = [];
for (var i = 1; i < NUM_LANES; i++) {
for (var j = 0; j < 8; j++) {
var mark = new LaneMark();
mark.x = ROAD_LEFT + i * LANE_WIDTH;
mark.y = j * 400;
mark.speed = 0;
laneMarks.push(mark);
game.addChild(mark);
}
}
// --- Car Selection Menu ---
var carOptions = [{
id: 'playerCar',
color: 0x2196f3
}, {
id: 'playerCar',
color: 0xffe082
}, {
id: 'playerCar',
color: 0x43a047
}];
var selectedCarIndex = 0;
var carMenuGroup = new Container();
var carMenuBg = LK.getAsset('roadBg', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
carMenuBg.alpha = 0.92;
carMenuGroup.addChild(carMenuBg);
var carMenuTitle = new Text2("Arabanı Seç!", {
size: 160,
fill: "#fff"
});
carMenuTitle.anchor.set(0.5, 0);
carMenuTitle.x = 2048 / 2;
carMenuTitle.y = 200;
carMenuGroup.addChild(carMenuTitle);
var carMenuCars = [];
for (var i = 0; i < carOptions.length; i++) {
var carAsset = LK.getAsset(carOptions[i].id, {
anchorX: 0.5,
anchorY: 0.5
});
carAsset.x = 2048 / 2 + (i - 1) * 400;
carAsset.y = 1000;
carAsset.width = 220;
carAsset.height = 400;
carAsset.tint = carOptions[i].color;
carMenuGroup.addChild(carAsset);
carMenuCars.push(carAsset);
}
function updateCarMenuHighlight() {
for (var i = 0; i < carMenuCars.length; i++) {
carMenuCars[i].alpha = i === selectedCarIndex ? 1 : 0.5;
carMenuCars[i].scaleX = carMenuCars[i].scaleY = i === selectedCarIndex ? 1.2 : 1;
}
}
updateCarMenuHighlight();
var leftBtn = new Text2("<", {
size: 200,
fill: "#fff"
});
leftBtn.anchor.set(0.5, 0.5);
leftBtn.x = 2048 / 2 - 500;
leftBtn.y = 1000;
carMenuGroup.addChild(leftBtn);
var rightBtn = new Text2(">", {
size: 200,
fill: "#fff"
});
rightBtn.anchor.set(0.5, 0.5);
rightBtn.x = 2048 / 2 + 500;
rightBtn.y = 1000;
carMenuGroup.addChild(rightBtn);
var startBtn = new Text2("Başla!", {
size: 160,
fill: 0x00E676
});
startBtn.anchor.set(0.5, 0.5);
startBtn.x = 2048 / 2;
startBtn.y = 1800;
carMenuGroup.addChild(startBtn);
game.addChild(carMenuGroup);
var player = null;
function startGameWithSelectedCar() {
if (player) player.destroy();
// Remove all existing enemies before starting
if (enemyCars && enemyCars.length) {
for (var i = 0; i < enemyCars.length; i++) {
if (enemyCars[i]) enemyCars[i].destroy();
}
}
enemyCars = [];
player = new PlayerCar(carOptions[selectedCarIndex].color);
player.x = 2048 / 2;
player.y = 2732 - 400;
game.addChild(player);
carMenuGroup.visible = false;
gameOver = false;
score = 0;
scoreTxt.setText(score);
speed = 22;
maxSpeed = 44;
ticksSinceLastEnemy = 0;
}
carMenuGroup.down = function (x, y, obj) {
// Left button
if (x > leftBtn.x - 120 && x < leftBtn.x + 120 && y > leftBtn.y - 120 && y < leftBtn.y + 120) {
selectedCarIndex = (selectedCarIndex + carOptions.length - 1) % carOptions.length;
updateCarMenuHighlight();
return;
}
// Right button
if (x > rightBtn.x - 120 && x < rightBtn.x + 120 && y > rightBtn.y - 120 && y < rightBtn.y + 120) {
selectedCarIndex = (selectedCarIndex + 1) % carOptions.length;
updateCarMenuHighlight();
return;
}
// Start button
if (x > startBtn.x - 300 && x < startBtn.x + 300 && y > startBtn.y - 120 && y < startBtn.y + 120) {
startGameWithSelectedCar();
return;
}
};
carMenuGroup.visible = true;
// --- End Car Selection Menu ---
// Score
var score = 0;
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Game state
var enemyCars = [];
var speed = 22; // Initial speed (pixels per frame)
var ticksSinceLastEnemy = 0;
var gameOver = false;
// Touch controls
var dragStartX = 0;
var dragStartPlayerX = 0;
var dragActive = false;
// Touch area for left/right controls (full screen)
game.down = function (x, y, obj) {
// Only allow drag if touch is on lower half of screen
if (y > 1200 && player) {
dragActive = true;
dragStartX = x;
dragStartPlayerX = player.x;
// Play engine sound if not already playing
if (!game._engineSound) {
var engineSound = LK.getSound('car_engine');
engineSound.loop = true;
engineSound.play();
game._engineSound = engineSound;
}
}
};
game.move = function (x, y, obj) {
if (dragActive) {
var dx = x - dragStartX;
var newX = dragStartPlayerX + dx;
// Clamp to road
var minX = ROAD_LEFT + player.width / 2;
var maxX = ROAD_RIGHT - player.width / 2;
if (newX < minX) newX = minX;
if (newX > maxX) newX = maxX;
player.x = newX;
}
};
game.up = function (x, y, obj) {
dragActive = false;
// Stop engine sound if playing
if (game._engineSound) {
game._engineSound.stop();
game._engineSound = null;
}
};
// Helper: spawn enemy car
function spawnEnemyCar() {
// Decide direction: normal (top-down) or reverse (bottom-up)
// Start spawning reverse cars after score > 20, with increasing chance
var spawnReverse = false;
var reverseChance = 0;
if (score > 20) {
reverseChance = Math.min((score - 20) * 2, 40); // up to 40% chance
if (Math.random() * 100 < reverseChance) {
spawnReverse = true;
}
}
var enemy = new EnemyCar();
// Random lane
var lane = Math.floor(Math.random() * NUM_LANES);
enemy.lane = lane;
enemy.x = ROAD_LEFT + LANE_WIDTH / 2 + lane * LANE_WIDTH;
if (!spawnReverse) {
// Normal direction (top-down)
enemy.y = -200;
// Speed: base + random, vary by type
if (enemy.type === 2) {
// fast car
enemy.speed = speed + 8 + Math.floor(Math.random() * 6);
} else if (enemy.type === 1) {
// wide car
enemy.speed = speed - 2 + Math.floor(Math.random() * 3);
} else if (enemy.type === 3) {
// obstacle
enemy.speed = speed - 4 + Math.floor(Math.random() * 2);
} else {
enemy.speed = speed + Math.floor(Math.random() * 6);
}
enemy.direction = 1; // downwards
} else {
// Reverse direction (bottom-up)
enemy.y = 2732 + 200;
// Speed: base + random, but negative (upwards)
if (enemy.type === 2) {
enemy.speed = -(speed + 8 + Math.floor(Math.random() * 6));
} else if (enemy.type === 1) {
enemy.speed = -(speed - 2 + Math.floor(Math.random() * 3));
} else if (enemy.type === 3) {
enemy.speed = -(speed - 4 + Math.floor(Math.random() * 2));
} else {
enemy.speed = -(speed + Math.floor(Math.random() * 6));
}
enemy.direction = -1; // upwards
// Optionally, visually flip the car (not required, but can be done with car.rotation = Math.PI)
if (enemy.children && enemy.children.length > 0) {
enemy.children[0].rotation = Math.PI;
}
}
enemyCars.push(enemy);
game.addChild(enemy);
}
// Helper: game over
function triggerGameOver() {
if (!gameOver) {
gameOver = true;
// Play crash sound
LK.getSound('car_crash').play();
// Car shake effect
if (player) {
var origX = player.x;
var origY = player.y;
// Defensive: only tween if player is defined, not destroyed, and tween is a function
if (typeof tween === "function" && player && typeof player.x === "number" && typeof player.y === "number" && (!player.destroyed || typeof player.destroyed === "undefined")) {
try {
tween(player).to({
x: origX - 40
}, 60).to({
x: origX + 40
}, 60).to({
x: origX - 25
}, 50).to({
x: origX + 25
}, 50).to({
x: origX
}, 40).start();
} catch (e) {
// Defensive: ignore tween errors
}
}
}
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
// Show car selection menu again after game over popup
LK.setTimeout(function () {
carMenuGroup.visible = true;
}, 1200);
}
}
// Main game loop
game.update = function () {
if (gameOver) return;
// Speed control: accelerate if holding, decelerate if not
var minSpeed = 14;
var maxSpeed = 44;
var accel = 0.25;
var decel = 0.4;
if (dragActive) {
if (speed < maxSpeed) speed += accel;
} else {
if (speed > minSpeed) speed -= decel;
if (speed < minSpeed) speed = minSpeed;
}
// Move lane marks (simulate road movement)
for (var i = 0; i < laneMarks.length; i++) {
var mark = laneMarks[i];
mark.y += speed;
if (mark.y > 2732) {
mark.y -= 3200;
}
}
// Move trees (simulate road movement)
for (var i = 0; i < treesLeft.length; i++) {
var tL = treesLeft[i];
tL.y += speed;
if (tL.y > 2900) {
tL.y -= 3200;
}
var tR = treesRight[i];
tR.y += speed;
if (tR.y > 2900) {
tR.y -= 3200;
}
}
// Spawn enemy cars
ticksSinceLastEnemy++;
// Difficulty: spawn more frequently and allow more than one at high score
var baseInterval = 60 - Math.floor(score / 10);
var enemyInterval = Math.max(10, baseInterval);
if (ticksSinceLastEnemy >= enemyInterval) {
// At higher scores, spawn 2 cars at once
var carsToSpawn = 1 + Math.floor(score / 60);
for (var i = 0; i < carsToSpawn; i++) {
spawnEnemyCar();
}
ticksSinceLastEnemy = 0;
}
// Increase max speed as score increases
maxSpeed = 44 + Math.floor(score / 10) * 2;
if (maxSpeed > 70) maxSpeed = 70;
// Move enemy cars
for (var i = enemyCars.length - 1; i >= 0; i--) {
var enemy = enemyCars[i];
enemy.y += enemy.speed;
// Remove if off screen (top or bottom, depending on direction)
var offScreen = false;
if (enemy.direction === -1) {
// Reverse car: remove if above -300
if (enemy.y < -300) offScreen = true;
} else {
// Normal car: remove if below 2900
if (enemy.y > 2900) offScreen = true;
}
if (offScreen) {
enemy.destroy();
enemyCars.splice(i, 1);
// Score for passing car (not for obstacles)
if (enemy.type !== 3) {
score += 1;
scoreTxt.setText(score);
}
// Increase max speed every 10 points
if (score % 10 === 0 && maxSpeed < 60) {
maxSpeed += 2;
}
continue;
}
// Collision with player
if (enemy.intersects(player)) {
triggerGameOver();
return;
}
}
};
// Initial UI
scoreTxt.setText(score); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Enemy Car (multiple types)
var EnemyCar = Container.expand(function () {
var self = Container.call(this);
// Randomly pick a type: 0 = normal, 1 = wide, 2 = fast, 3 = obstacle
var type = Math.floor(Math.random() * 4);
self.type = type;
var assetId = 'enemyCar';
var color = 0xe53935;
var width = 180;
var height = 320;
if (type === 1) {
// wide car
width = 260;
color = 0x8e24aa;
} else if (type === 2) {
// fast car
color = 0x43a047;
} else if (type === 3) {
// obstacle (barrier)
assetId = 'obstacle';
width = 220;
height = 120;
color = 0xffb300;
}
var car = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
car.width = width;
car.height = height;
car.tint = color;
self.width = width;
self.height = height;
self.speed = 0;
// For collision
self.lane = 0;
return self;
});
// Lane Mark
var LaneMark = Container.expand(function () {
var self = Container.call(this);
var mark = self.attachAsset('laneMark', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = mark.width;
self.height = mark.height;
self.speed = 0;
return self;
});
// Player Car
var PlayerCar = Container.expand(function (color) {
var self = Container.call(this);
var car = self.attachAsset('playerCar', {
anchorX: 0.5,
anchorY: 0.5
});
if (color !== undefined) {
car.tint = color;
}
// For collision box, use car asset size
self.width = car.width;
self.height = car.height;
// For touch drag
self.isDragging = false;
return self;
});
// Road Edge
var RoadEdge = Container.expand(function () {
var self = Container.call(this);
var edge = self.attachAsset('roadEdge', {
anchorX: 0,
anchorY: 0
});
self.width = edge.width;
self.height = edge.height;
self.speed = 0;
return self;
});
// Tree (composed of trunk and leaves)
var Tree = Container.expand(function () {
var self = Container.call(this);
var trunk = self.attachAsset('treeTrunk', {
anchorX: 0.5,
anchorY: 1
});
trunk.y = 0;
var leaves = self.attachAsset('treeLeaves', {
anchorX: 0.5,
anchorY: 1
});
leaves.y = -trunk.height + 10;
self.width = leaves.width;
self.height = trunk.height + leaves.height;
self.speed = 0;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// tree leaves
// tree trunk
// road edge
// Road background (gray)
// Road lane
// Fuel can
// Enemy car
// Car (player)
// Road background
var roadBg = LK.getAsset('roadBg', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(roadBg);
// Road edges
var leftEdge = new RoadEdge();
leftEdge.x = ROAD_LEFT - leftEdge.width;
leftEdge.y = 0;
game.addChild(leftEdge);
var rightEdge = new RoadEdge();
rightEdge.x = ROAD_RIGHT;
rightEdge.y = 0;
game.addChild(rightEdge);
// Trees setup
var treesLeft = [];
var treesRight = [];
for (var i = 0; i < 8; i++) {
var tL = new Tree();
tL.x = ROAD_LEFT - 80;
tL.y = i * 400 + 100;
tL.speed = 0;
treesLeft.push(tL);
game.addChild(tL);
var tR = new Tree();
tR.x = ROAD_RIGHT + 80;
tR.y = i * 400 + 300;
tR.speed = 0;
treesRight.push(tR);
game.addChild(tR);
}
// Lane setup
var NUM_LANES = 4;
var LANE_WIDTH = 350;
var ROAD_WIDTH = NUM_LANES * LANE_WIDTH;
var ROAD_LEFT = (2048 - ROAD_WIDTH) / 2;
var ROAD_RIGHT = ROAD_LEFT + ROAD_WIDTH;
// Lane marks
var laneMarks = [];
for (var i = 1; i < NUM_LANES; i++) {
for (var j = 0; j < 8; j++) {
var mark = new LaneMark();
mark.x = ROAD_LEFT + i * LANE_WIDTH;
mark.y = j * 400;
mark.speed = 0;
laneMarks.push(mark);
game.addChild(mark);
}
}
// --- Car Selection Menu ---
var carOptions = [{
id: 'playerCar',
color: 0x2196f3
}, {
id: 'playerCar',
color: 0xffe082
}, {
id: 'playerCar',
color: 0x43a047
}];
var selectedCarIndex = 0;
var carMenuGroup = new Container();
var carMenuBg = LK.getAsset('roadBg', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
carMenuBg.alpha = 0.92;
carMenuGroup.addChild(carMenuBg);
var carMenuTitle = new Text2("Arabanı Seç!", {
size: 160,
fill: "#fff"
});
carMenuTitle.anchor.set(0.5, 0);
carMenuTitle.x = 2048 / 2;
carMenuTitle.y = 200;
carMenuGroup.addChild(carMenuTitle);
var carMenuCars = [];
for (var i = 0; i < carOptions.length; i++) {
var carAsset = LK.getAsset(carOptions[i].id, {
anchorX: 0.5,
anchorY: 0.5
});
carAsset.x = 2048 / 2 + (i - 1) * 400;
carAsset.y = 1000;
carAsset.width = 220;
carAsset.height = 400;
carAsset.tint = carOptions[i].color;
carMenuGroup.addChild(carAsset);
carMenuCars.push(carAsset);
}
function updateCarMenuHighlight() {
for (var i = 0; i < carMenuCars.length; i++) {
carMenuCars[i].alpha = i === selectedCarIndex ? 1 : 0.5;
carMenuCars[i].scaleX = carMenuCars[i].scaleY = i === selectedCarIndex ? 1.2 : 1;
}
}
updateCarMenuHighlight();
var leftBtn = new Text2("<", {
size: 200,
fill: "#fff"
});
leftBtn.anchor.set(0.5, 0.5);
leftBtn.x = 2048 / 2 - 500;
leftBtn.y = 1000;
carMenuGroup.addChild(leftBtn);
var rightBtn = new Text2(">", {
size: 200,
fill: "#fff"
});
rightBtn.anchor.set(0.5, 0.5);
rightBtn.x = 2048 / 2 + 500;
rightBtn.y = 1000;
carMenuGroup.addChild(rightBtn);
var startBtn = new Text2("Başla!", {
size: 160,
fill: 0x00E676
});
startBtn.anchor.set(0.5, 0.5);
startBtn.x = 2048 / 2;
startBtn.y = 1800;
carMenuGroup.addChild(startBtn);
game.addChild(carMenuGroup);
var player = null;
function startGameWithSelectedCar() {
if (player) player.destroy();
// Remove all existing enemies before starting
if (enemyCars && enemyCars.length) {
for (var i = 0; i < enemyCars.length; i++) {
if (enemyCars[i]) enemyCars[i].destroy();
}
}
enemyCars = [];
player = new PlayerCar(carOptions[selectedCarIndex].color);
player.x = 2048 / 2;
player.y = 2732 - 400;
game.addChild(player);
carMenuGroup.visible = false;
gameOver = false;
score = 0;
scoreTxt.setText(score);
speed = 22;
maxSpeed = 44;
ticksSinceLastEnemy = 0;
}
carMenuGroup.down = function (x, y, obj) {
// Left button
if (x > leftBtn.x - 120 && x < leftBtn.x + 120 && y > leftBtn.y - 120 && y < leftBtn.y + 120) {
selectedCarIndex = (selectedCarIndex + carOptions.length - 1) % carOptions.length;
updateCarMenuHighlight();
return;
}
// Right button
if (x > rightBtn.x - 120 && x < rightBtn.x + 120 && y > rightBtn.y - 120 && y < rightBtn.y + 120) {
selectedCarIndex = (selectedCarIndex + 1) % carOptions.length;
updateCarMenuHighlight();
return;
}
// Start button
if (x > startBtn.x - 300 && x < startBtn.x + 300 && y > startBtn.y - 120 && y < startBtn.y + 120) {
startGameWithSelectedCar();
return;
}
};
carMenuGroup.visible = true;
// --- End Car Selection Menu ---
// Score
var score = 0;
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Game state
var enemyCars = [];
var speed = 22; // Initial speed (pixels per frame)
var ticksSinceLastEnemy = 0;
var gameOver = false;
// Touch controls
var dragStartX = 0;
var dragStartPlayerX = 0;
var dragActive = false;
// Touch area for left/right controls (full screen)
game.down = function (x, y, obj) {
// Only allow drag if touch is on lower half of screen
if (y > 1200 && player) {
dragActive = true;
dragStartX = x;
dragStartPlayerX = player.x;
// Play engine sound if not already playing
if (!game._engineSound) {
var engineSound = LK.getSound('car_engine');
engineSound.loop = true;
engineSound.play();
game._engineSound = engineSound;
}
}
};
game.move = function (x, y, obj) {
if (dragActive) {
var dx = x - dragStartX;
var newX = dragStartPlayerX + dx;
// Clamp to road
var minX = ROAD_LEFT + player.width / 2;
var maxX = ROAD_RIGHT - player.width / 2;
if (newX < minX) newX = minX;
if (newX > maxX) newX = maxX;
player.x = newX;
}
};
game.up = function (x, y, obj) {
dragActive = false;
// Stop engine sound if playing
if (game._engineSound) {
game._engineSound.stop();
game._engineSound = null;
}
};
// Helper: spawn enemy car
function spawnEnemyCar() {
// Decide direction: normal (top-down) or reverse (bottom-up)
// Start spawning reverse cars after score > 20, with increasing chance
var spawnReverse = false;
var reverseChance = 0;
if (score > 20) {
reverseChance = Math.min((score - 20) * 2, 40); // up to 40% chance
if (Math.random() * 100 < reverseChance) {
spawnReverse = true;
}
}
var enemy = new EnemyCar();
// Random lane
var lane = Math.floor(Math.random() * NUM_LANES);
enemy.lane = lane;
enemy.x = ROAD_LEFT + LANE_WIDTH / 2 + lane * LANE_WIDTH;
if (!spawnReverse) {
// Normal direction (top-down)
enemy.y = -200;
// Speed: base + random, vary by type
if (enemy.type === 2) {
// fast car
enemy.speed = speed + 8 + Math.floor(Math.random() * 6);
} else if (enemy.type === 1) {
// wide car
enemy.speed = speed - 2 + Math.floor(Math.random() * 3);
} else if (enemy.type === 3) {
// obstacle
enemy.speed = speed - 4 + Math.floor(Math.random() * 2);
} else {
enemy.speed = speed + Math.floor(Math.random() * 6);
}
enemy.direction = 1; // downwards
} else {
// Reverse direction (bottom-up)
enemy.y = 2732 + 200;
// Speed: base + random, but negative (upwards)
if (enemy.type === 2) {
enemy.speed = -(speed + 8 + Math.floor(Math.random() * 6));
} else if (enemy.type === 1) {
enemy.speed = -(speed - 2 + Math.floor(Math.random() * 3));
} else if (enemy.type === 3) {
enemy.speed = -(speed - 4 + Math.floor(Math.random() * 2));
} else {
enemy.speed = -(speed + Math.floor(Math.random() * 6));
}
enemy.direction = -1; // upwards
// Optionally, visually flip the car (not required, but can be done with car.rotation = Math.PI)
if (enemy.children && enemy.children.length > 0) {
enemy.children[0].rotation = Math.PI;
}
}
enemyCars.push(enemy);
game.addChild(enemy);
}
// Helper: game over
function triggerGameOver() {
if (!gameOver) {
gameOver = true;
// Play crash sound
LK.getSound('car_crash').play();
// Car shake effect
if (player) {
var origX = player.x;
var origY = player.y;
// Defensive: only tween if player is defined, not destroyed, and tween is a function
if (typeof tween === "function" && player && typeof player.x === "number" && typeof player.y === "number" && (!player.destroyed || typeof player.destroyed === "undefined")) {
try {
tween(player).to({
x: origX - 40
}, 60).to({
x: origX + 40
}, 60).to({
x: origX - 25
}, 50).to({
x: origX + 25
}, 50).to({
x: origX
}, 40).start();
} catch (e) {
// Defensive: ignore tween errors
}
}
}
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
// Show car selection menu again after game over popup
LK.setTimeout(function () {
carMenuGroup.visible = true;
}, 1200);
}
}
// Main game loop
game.update = function () {
if (gameOver) return;
// Speed control: accelerate if holding, decelerate if not
var minSpeed = 14;
var maxSpeed = 44;
var accel = 0.25;
var decel = 0.4;
if (dragActive) {
if (speed < maxSpeed) speed += accel;
} else {
if (speed > minSpeed) speed -= decel;
if (speed < minSpeed) speed = minSpeed;
}
// Move lane marks (simulate road movement)
for (var i = 0; i < laneMarks.length; i++) {
var mark = laneMarks[i];
mark.y += speed;
if (mark.y > 2732) {
mark.y -= 3200;
}
}
// Move trees (simulate road movement)
for (var i = 0; i < treesLeft.length; i++) {
var tL = treesLeft[i];
tL.y += speed;
if (tL.y > 2900) {
tL.y -= 3200;
}
var tR = treesRight[i];
tR.y += speed;
if (tR.y > 2900) {
tR.y -= 3200;
}
}
// Spawn enemy cars
ticksSinceLastEnemy++;
// Difficulty: spawn more frequently and allow more than one at high score
var baseInterval = 60 - Math.floor(score / 10);
var enemyInterval = Math.max(10, baseInterval);
if (ticksSinceLastEnemy >= enemyInterval) {
// At higher scores, spawn 2 cars at once
var carsToSpawn = 1 + Math.floor(score / 60);
for (var i = 0; i < carsToSpawn; i++) {
spawnEnemyCar();
}
ticksSinceLastEnemy = 0;
}
// Increase max speed as score increases
maxSpeed = 44 + Math.floor(score / 10) * 2;
if (maxSpeed > 70) maxSpeed = 70;
// Move enemy cars
for (var i = enemyCars.length - 1; i >= 0; i--) {
var enemy = enemyCars[i];
enemy.y += enemy.speed;
// Remove if off screen (top or bottom, depending on direction)
var offScreen = false;
if (enemy.direction === -1) {
// Reverse car: remove if above -300
if (enemy.y < -300) offScreen = true;
} else {
// Normal car: remove if below 2900
if (enemy.y > 2900) offScreen = true;
}
if (offScreen) {
enemy.destroy();
enemyCars.splice(i, 1);
// Score for passing car (not for obstacles)
if (enemy.type !== 3) {
score += 1;
scoreTxt.setText(score);
}
// Increase max speed every 10 points
if (score % 10 === 0 && maxSpeed < 60) {
maxSpeed += 2;
}
continue;
}
// Collision with player
if (enemy.intersects(player)) {
triggerGameOver();
return;
}
}
};
// Initial UI
scoreTxt.setText(score);