/****
* Plugins
****/
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Cactus class for cacti along the road
var Cactus = Container.expand(function () {
var self = Container.call(this);
// Randomly pick one of the cactus assets
var cactusTypes = ['cactus', 'cactus1', 'cactus2'];
var cactusAsset = cactusTypes[Math.floor(Math.random() * cactusTypes.length)];
var cactusGraphics = self.attachAsset(cactusAsset, {
anchorX: 0.5,
anchorY: 1
});
// Optionally randomize scale for variety
var scale = 0.7 + Math.random() * 0.6;
cactusGraphics.scale.x = scale;
cactusGraphics.scale.y = scale;
// Fallen state
self.fallen = false;
// Animate falling over
self.fallOver = function () {
if (self.fallen) return;
self.fallen = true;
// Animate rotation to -90 or +90 deg depending on which side of road
var targetRotation = self.x < 2048 / 2 ? -Math.PI / 2 : Math.PI / 2;
var startRotation = self.rotation;
var duration = 30; // frames (0.5s)
var frame = 0;
self._fallAnim = function () {
frame++;
self.rotation = startRotation + (targetRotation - startRotation) * (frame / duration);
if (frame < duration) {
LK.setTimeout(self._fallAnim, 1000 / 60);
} else {
self.rotation = targetRotation;
}
};
self._fallAnim();
};
return self;
});
var Car = Container.expand(function () {
var self = Container.call(this);
self.projectMovement = function (vector) {
var angle = -Math.PI / 4;
var cosAngle = Math.cos(angle);
var sinAngle = Math.sin(angle);
return {
x: vector.x * cosAngle - vector.y * sinAngle,
y: vector.x * sinAngle + vector.y * cosAngle
};
};
var carGraphics = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
self.ORIGINAL_SPEED = 2;
self.speed = self.ORIGINAL_SPEED;
self.direction = 0;
self.momentum = {
x: 0,
y: 0
};
self._move_migrated = function () {
var momentumModifier = 0.1;
self.speed *= 1.01;
if (self.direction === 0) {
self.momentum.x += self.speed * momentumModifier;
} else {
self.momentum.y -= self.speed * momentumModifier;
}
var projectedMovement = self.projectMovement(self.momentum);
self.x += projectedMovement.x;
self.y += projectedMovement.y;
var nonTravelMomentum;
if (self.direction === 0) {
self.momentum.x *= 0.98;
self.momentum.y *= 0.95;
nonTravelMomentum = self.momentum.y;
} else {
self.momentum.x *= 0.95;
self.momentum.y *= 0.98;
nonTravelMomentum = self.momentum.x;
}
self.nonTravelMomentum = nonTravelMomentum;
};
self.changeDirection = function () {
self.direction = self.direction === 0 ? 1 : 0;
self.speed = self.ORIGINAL_SPEED;
carGraphics.scale.x *= -1;
var skidSound = LK.getSound('Skid');
skidSound.play();
skidSound.on('end', function () {
var engineSound = LK.getSound('Engine');
engineSound.stop();
engineSound.play({
loop: true
});
});
};
});
var Driver = Container.expand(function () {
var self = Container.call(this);
self.x = +1500;
self.y = +1500;
var driverGraphics = self.attachAsset('driver', {
anchorX: 0.5,
anchorY: 0.5
});
});
var Particle = Container.expand(function () {
var self = Container.call(this);
var particleGraphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
particleGraphics.rotation = Math.PI / 4;
self.lifetime = 100;
self.tick = function () {
if (--self.lifetime <= 0) {
self.destroy();
}
};
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// cornerbutton, musicbutton, and centercircle assets removed
// Background image asset (replace id with your background image asset id)
game.calculateDistanceToPoint = function (point, segmentStart, segmentEnd) {
var A = point.x - segmentStart.x;
var B = point.y - segmentStart.y;
var C = segmentEnd.x - segmentStart.x;
var D = segmentEnd.y - segmentStart.y;
var dot = A * C + B * D;
var len_sq = C * C + D * D;
var param = -1;
if (len_sq != 0) {
param = dot / len_sq;
}
var xx, yy;
if (param < 0) {
xx = segmentStart.x;
yy = segmentStart.y;
} else if (param > 1) {
xx = segmentEnd.x;
yy = segmentEnd.y;
} else {
xx = segmentStart.x + param * C;
yy = segmentStart.y + param * D;
}
var dx = point.x - xx;
var dy = point.y - yy;
return Math.sqrt(dx * dx + dy * dy);
};
game.addRoadSegment = function () {
var lastSegment = roadSegments[roadSegments.length - 1];
zigzag = !zigzag;
var segment = roadContainer.attachAsset('roadSegment', {
anchorX: 0.5
});
segment.width = segmentWidth;
segmentWidth = Math.max(350, segmentWidth - 15);
segment.height = i === 1 ? 3000 : Math.floor(Math.random() * (4000 - 1200 + 1)) + 1200;
segment.rotation = zigzag ? -Math.PI - Math.PI / 4 : -Math.PI + Math.PI / 4;
segment.y = currentY;
segment.x = currentX;
var adjustedHeight = segment.height - segmentWidth / 2;
currentY += adjustedHeight * Math.cos(segment.rotation);
currentX -= adjustedHeight * Math.sin(segment.rotation);
segment.shadow = roadContainer.attachAsset('roadSegmentShadow', {
anchorX: 0.5
});
segment.shadow.width = segment.width;
segment.shadow.height = segment.height;
segment.shadow.rotation = segment.rotation;
segment.shadow.x = segment.x;
segment.shadow.y = segment.y + 50;
segment.shadow.alpha = 1;
segment.used = false;
roadSegments.push(segment);
roadContainer.addChildAt(segment.shadow, 0);
roadContainer.addChild(segment);
// Always add cacti outside the new road segment
// Place 1-2 cacti per segment, randomize side and offset
var numCacti = 1 + Math.floor(Math.random() * 2);
for (var j = 0; j < numCacti; j++) {
var cactus = new Cactus();
// Place cactus at a random position along the segment edge
var t = Math.random() * 0.8 + 0.1; // avoid very ends
var side = Math.random() < 0.5 ? -1 : 1; // left or right
// Calculate position along the segment
var dx = Math.sin(segment.rotation) * (segment.height * t);
var dy = -Math.cos(segment.rotation) * (segment.height * t);
// Offset from road edge, OUTSIDE the road
var cactusWidth = cactus.width || 100;
var cactusExtra = 100 + Math.random() * 100; // extra offset to ensure cactus is outside
var edgeOffset = segment.width / 2 + cactusWidth / 2 + cactusExtra;
var ex = Math.cos(segment.rotation) * edgeOffset * side;
var ey = Math.sin(segment.rotation) * edgeOffset * side;
cactus.x = segment.x + dx + ex;
cactus.y = segment.y + dy + ey;
cactusContainer.addChild(cactus);
cacti.push(cactus);
}
};
game.setBackgroundColor(0xc39977);
var particles = [];
LK.playMusic('backgroundMusic');
// Add a background layer behind all gameplay elements
var backgroundLayer = game.addChild(new Container());
var background = backgroundLayer.attachAsset('backgroundImage', {
anchorX: 0,
anchorY: 0
});
background.width = 2048;
background.height = 2732;
background.alpha = 1;
var mainContainer = game.addChild(new Container());
var engineSound = LK.getSound('Engine');
engineSound.stop();
engineSound.play({
loop: true
});
var roadContainer = mainContainer.addChild(new Container());
var cactusContainer = mainContainer.addChild(new Container());
var cacti = [];
var roadSegments = [];
var segmentLength = Math.floor(Math.random() * (1000 - 200 + 1)) + 200;
var segmentWidth = 1200;
var currentX = 2048 / 2;
var currentY = 2732 / 2;
var zigzag = true;
for (var i = 1; i <= 15; i++) {
game.addRoadSegment();
}
// (Cactus placement now handled in addRoadSegment)
var scoreText = new Text2('0', {
size: 150,
fill: 0xFFFFFF,
weight: '800',
dropShadow: true,
dropShadowColor: '#373330',
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 6,
dropShadowDistance: 6
});
scoreText.anchor.set(0, 0);
LK.gui.top.addChild(scoreText);
// Import storage plugin
// Clear high score data only once using a persistent flag
if (!storage.highScoreClearedOnce) {
storage.highScore = 0;
storage.highScoreClearedOnce = true;
}
// High score text, smaller and below the main score
var highScore = storage.highScore || 0;
var highScoreText = new Text2('High: ' + highScore, {
size: 60,
fill: 0xFFFFFF,
weight: '400',
dropShadow: true,
dropShadowColor: '#373330',
dropShadowBlur: 2,
dropShadowAngle: Math.PI / 6,
dropShadowDistance: 3
});
highScoreText.anchor.set(0, 0);
// Position just below the scoreText (scoreText is 150px tall, add a little margin)
highScoreText.y = scoreText.height + 10;
LK.gui.top.addChild(highScoreText);
var notificationTexts = ["I'm trying.", "We can't change what's done.", "Be loyal to what matters.", "I gave you all I had.", "I tried, in the end. I did.", "You, sir, are a fish.", "LENNNNAAAAYYYY!", "Vengeance is an idiot's game.", "We're more ghosts than people.", "Oh, and a quarter. Don't forget the quarter.", "Yeah, I got TB.", "I guess I... I'm afraid."];
var usedNotificationTexts = [];
function getRandomNotificationText() {
if (notificationTexts.length === 0) {
notificationTexts = usedNotificationTexts.splice(0, usedNotificationTexts.length);
}
var index = Math.floor(Math.random() * notificationTexts.length);
var text = notificationTexts.splice(index, 1)[0];
usedNotificationTexts.push(text);
return text;
}
var notificationText = new Text2(getRandomNotificationText(), {
size: 60,
fill: 0xFFFFFF,
weight: '400',
align: 'center',
stroke: '#000000',
strokeThickness: 8
});
notificationText.anchor.set(0, 4);
notificationText.x -= 350;
LK.gui.bottom.addChild(notificationText);
var car = mainContainer.addChild(new Car());
car.x = 2048 / 2;
car.y = 2732 / 2;
var driver = LK.gui.addChild(new Driver());
driver.x = 250;
driver.y = 1800;
var isGameOver = false;
var score = 0;
var closestSegment = null;
game.on('down', function (x, y, obj) {
car.changeDirection();
var engineSound = LK.getSound('Engine');
engineSound.stop();
engineSound.play({
loop: true
});
});
// Removed background music restart on retry to ensure music always loops without restart
LK.on('tick', function () {
car._move_migrated();
var carIsOnRoad = false;
var carPosition = {
x: car.x,
y: car.y
};
var currentClosestSegment = null;
var currentClosestDistance = Infinity;
roadSegments.forEach(function (segment) {
var segmentStart = {
x: segment.x + Math.sin(segment.rotation) * 100,
y: segment.y - Math.cos(segment.rotation) * 100
};
var segmentEnd = {
x: segment.x - Math.sin(segment.rotation) * (segment.height - segment.width / 2),
y: segment.y + Math.cos(segment.rotation) * (segment.height - segment.width / 2)
};
var distance = game.calculateDistanceToPoint(carPosition, segmentStart, segmentEnd);
if (distance < currentClosestDistance) {
currentClosestDistance = distance;
currentClosestSegment = segment;
}
if (distance < segment.width / 2 - 50) {
carIsOnRoad = true;
}
});
// --- Cactus collision detection and fall over ---
var _loop = function _loop() {
cactus = cacti[i]; // Only trigger once per cactus
if (!cactus.fallen) {
// Use bounding box intersection
dx = Math.abs(car.x - cactus.x);
dy = Math.abs(car.y - cactus.y); // Use a reasonable collision box (car is ~400x310, cactus ~80x180)
if (dx < 120 && dy < 140) {
cactus.fallOver();
LK.getSound('Crash').play();
score++;
scoreText.setText(score.toString());
// Balloon effect when score reaches exactly 3
// (Balloon effect removed)
// Update high score if needed
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreText.setText('High: ' + highScore);
}
}
}
},
cactus,
dx,
dy,
balloon,
startY,
duration,
frame;
for (var i = 0; i < cacti.length; i++) {
_loop();
}
// ---
if (closestSegment !== currentClosestSegment && !currentClosestSegment.used) {
closestSegment = currentClosestSegment;
closestSegment.used = true;
score++;
car.ORIGINAL_SPEED += 0.1;
scoreText.setText(score.toString());
// Update high score if needed
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreText.setText('High: ' + highScore);
}
notificationText.setText(getRandomNotificationText());
}
if (!carIsOnRoad) {
// Play crash sound only once
if (!car.crashPlayed) {
car.crashPlayed = true;
LK.getSound('Crash').play();
// Stop the car's movement
car.speed = 0;
car.momentum.x = 0;
car.momentum.y = 0;
LK.setTimeout(function () {
LK.showGameOver();
// Removed background music restart after game over to ensure music always loops without restart
}, 1000);
}
} else {
// Reset crashPlayed if back on road (optional, in case of future logic)
car.crashPlayed = false;
}
var particleOffsets = [{
x: 20,
y: 140
}, {
x: 20 + 100,
y: 140 - 100
}, {
x: 20 - 150,
y: 140 - 150
}, {
x: 20 - 150 + 100,
y: 140 - 150 - 100
}];
for (var i = 0; i < particleOffsets.length; i++) {
var alphaValue = Math.max(0, Math.min(1, Math.abs(car.nonTravelMomentum) / 5 - 0.5));
if (alphaValue > 0) {
var particle = new Particle();
particle.alpha = alphaValue;
var noiseX = (Math.random() - 0.5) * 10;
var noiseY = (Math.random() - 0.5) * 10;
particle.x = car.x + (car.direction === 0 ? -1 : 1) * particleOffsets[i].x + noiseX;
particle.y = car.y + particleOffsets[i].y + noiseY;
mainContainer.addChildAt(particle, 1);
particles.push(particle);
}
}
particles.forEach(function (particle, index) {
particle.tick();
if (particle.lifetime <= 0) {
particles.splice(index, 1);
}
});
var carLocalPosition = game.toLocal(car.position, car.parent);
var offsetX = (2048 / 2 - carLocalPosition.x) / 20;
var offsetY = (2732 - 450 - carLocalPosition.y) / 20;
mainContainer.x += offsetX;
mainContainer.y += offsetY;
for (var i = roadSegments.length - 1; i >= 0; i--) {
var segmentGlobalPosition = game.toLocal(roadSegments[i].position, roadSegments[i].parent);
if (segmentGlobalPosition.y - roadSegments[i].height > 2732 * 2) {
roadSegments[i].shadow.destroy();
roadSegments[i].destroy();
roadSegments.splice(i, 1);
game.addRoadSegment();
}
}
}); /****
* Plugins
****/
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Cactus class for cacti along the road
var Cactus = Container.expand(function () {
var self = Container.call(this);
// Randomly pick one of the cactus assets
var cactusTypes = ['cactus', 'cactus1', 'cactus2'];
var cactusAsset = cactusTypes[Math.floor(Math.random() * cactusTypes.length)];
var cactusGraphics = self.attachAsset(cactusAsset, {
anchorX: 0.5,
anchorY: 1
});
// Optionally randomize scale for variety
var scale = 0.7 + Math.random() * 0.6;
cactusGraphics.scale.x = scale;
cactusGraphics.scale.y = scale;
// Fallen state
self.fallen = false;
// Animate falling over
self.fallOver = function () {
if (self.fallen) return;
self.fallen = true;
// Animate rotation to -90 or +90 deg depending on which side of road
var targetRotation = self.x < 2048 / 2 ? -Math.PI / 2 : Math.PI / 2;
var startRotation = self.rotation;
var duration = 30; // frames (0.5s)
var frame = 0;
self._fallAnim = function () {
frame++;
self.rotation = startRotation + (targetRotation - startRotation) * (frame / duration);
if (frame < duration) {
LK.setTimeout(self._fallAnim, 1000 / 60);
} else {
self.rotation = targetRotation;
}
};
self._fallAnim();
};
return self;
});
var Car = Container.expand(function () {
var self = Container.call(this);
self.projectMovement = function (vector) {
var angle = -Math.PI / 4;
var cosAngle = Math.cos(angle);
var sinAngle = Math.sin(angle);
return {
x: vector.x * cosAngle - vector.y * sinAngle,
y: vector.x * sinAngle + vector.y * cosAngle
};
};
var carGraphics = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
self.ORIGINAL_SPEED = 2;
self.speed = self.ORIGINAL_SPEED;
self.direction = 0;
self.momentum = {
x: 0,
y: 0
};
self._move_migrated = function () {
var momentumModifier = 0.1;
self.speed *= 1.01;
if (self.direction === 0) {
self.momentum.x += self.speed * momentumModifier;
} else {
self.momentum.y -= self.speed * momentumModifier;
}
var projectedMovement = self.projectMovement(self.momentum);
self.x += projectedMovement.x;
self.y += projectedMovement.y;
var nonTravelMomentum;
if (self.direction === 0) {
self.momentum.x *= 0.98;
self.momentum.y *= 0.95;
nonTravelMomentum = self.momentum.y;
} else {
self.momentum.x *= 0.95;
self.momentum.y *= 0.98;
nonTravelMomentum = self.momentum.x;
}
self.nonTravelMomentum = nonTravelMomentum;
};
self.changeDirection = function () {
self.direction = self.direction === 0 ? 1 : 0;
self.speed = self.ORIGINAL_SPEED;
carGraphics.scale.x *= -1;
var skidSound = LK.getSound('Skid');
skidSound.play();
skidSound.on('end', function () {
var engineSound = LK.getSound('Engine');
engineSound.stop();
engineSound.play({
loop: true
});
});
};
});
var Driver = Container.expand(function () {
var self = Container.call(this);
self.x = +1500;
self.y = +1500;
var driverGraphics = self.attachAsset('driver', {
anchorX: 0.5,
anchorY: 0.5
});
});
var Particle = Container.expand(function () {
var self = Container.call(this);
var particleGraphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
particleGraphics.rotation = Math.PI / 4;
self.lifetime = 100;
self.tick = function () {
if (--self.lifetime <= 0) {
self.destroy();
}
};
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// cornerbutton, musicbutton, and centercircle assets removed
// Background image asset (replace id with your background image asset id)
game.calculateDistanceToPoint = function (point, segmentStart, segmentEnd) {
var A = point.x - segmentStart.x;
var B = point.y - segmentStart.y;
var C = segmentEnd.x - segmentStart.x;
var D = segmentEnd.y - segmentStart.y;
var dot = A * C + B * D;
var len_sq = C * C + D * D;
var param = -1;
if (len_sq != 0) {
param = dot / len_sq;
}
var xx, yy;
if (param < 0) {
xx = segmentStart.x;
yy = segmentStart.y;
} else if (param > 1) {
xx = segmentEnd.x;
yy = segmentEnd.y;
} else {
xx = segmentStart.x + param * C;
yy = segmentStart.y + param * D;
}
var dx = point.x - xx;
var dy = point.y - yy;
return Math.sqrt(dx * dx + dy * dy);
};
game.addRoadSegment = function () {
var lastSegment = roadSegments[roadSegments.length - 1];
zigzag = !zigzag;
var segment = roadContainer.attachAsset('roadSegment', {
anchorX: 0.5
});
segment.width = segmentWidth;
segmentWidth = Math.max(350, segmentWidth - 15);
segment.height = i === 1 ? 3000 : Math.floor(Math.random() * (4000 - 1200 + 1)) + 1200;
segment.rotation = zigzag ? -Math.PI - Math.PI / 4 : -Math.PI + Math.PI / 4;
segment.y = currentY;
segment.x = currentX;
var adjustedHeight = segment.height - segmentWidth / 2;
currentY += adjustedHeight * Math.cos(segment.rotation);
currentX -= adjustedHeight * Math.sin(segment.rotation);
segment.shadow = roadContainer.attachAsset('roadSegmentShadow', {
anchorX: 0.5
});
segment.shadow.width = segment.width;
segment.shadow.height = segment.height;
segment.shadow.rotation = segment.rotation;
segment.shadow.x = segment.x;
segment.shadow.y = segment.y + 50;
segment.shadow.alpha = 1;
segment.used = false;
roadSegments.push(segment);
roadContainer.addChildAt(segment.shadow, 0);
roadContainer.addChild(segment);
// Always add cacti outside the new road segment
// Place 1-2 cacti per segment, randomize side and offset
var numCacti = 1 + Math.floor(Math.random() * 2);
for (var j = 0; j < numCacti; j++) {
var cactus = new Cactus();
// Place cactus at a random position along the segment edge
var t = Math.random() * 0.8 + 0.1; // avoid very ends
var side = Math.random() < 0.5 ? -1 : 1; // left or right
// Calculate position along the segment
var dx = Math.sin(segment.rotation) * (segment.height * t);
var dy = -Math.cos(segment.rotation) * (segment.height * t);
// Offset from road edge, OUTSIDE the road
var cactusWidth = cactus.width || 100;
var cactusExtra = 100 + Math.random() * 100; // extra offset to ensure cactus is outside
var edgeOffset = segment.width / 2 + cactusWidth / 2 + cactusExtra;
var ex = Math.cos(segment.rotation) * edgeOffset * side;
var ey = Math.sin(segment.rotation) * edgeOffset * side;
cactus.x = segment.x + dx + ex;
cactus.y = segment.y + dy + ey;
cactusContainer.addChild(cactus);
cacti.push(cactus);
}
};
game.setBackgroundColor(0xc39977);
var particles = [];
LK.playMusic('backgroundMusic');
// Add a background layer behind all gameplay elements
var backgroundLayer = game.addChild(new Container());
var background = backgroundLayer.attachAsset('backgroundImage', {
anchorX: 0,
anchorY: 0
});
background.width = 2048;
background.height = 2732;
background.alpha = 1;
var mainContainer = game.addChild(new Container());
var engineSound = LK.getSound('Engine');
engineSound.stop();
engineSound.play({
loop: true
});
var roadContainer = mainContainer.addChild(new Container());
var cactusContainer = mainContainer.addChild(new Container());
var cacti = [];
var roadSegments = [];
var segmentLength = Math.floor(Math.random() * (1000 - 200 + 1)) + 200;
var segmentWidth = 1200;
var currentX = 2048 / 2;
var currentY = 2732 / 2;
var zigzag = true;
for (var i = 1; i <= 15; i++) {
game.addRoadSegment();
}
// (Cactus placement now handled in addRoadSegment)
var scoreText = new Text2('0', {
size: 150,
fill: 0xFFFFFF,
weight: '800',
dropShadow: true,
dropShadowColor: '#373330',
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 6,
dropShadowDistance: 6
});
scoreText.anchor.set(0, 0);
LK.gui.top.addChild(scoreText);
// Import storage plugin
// Clear high score data only once using a persistent flag
if (!storage.highScoreClearedOnce) {
storage.highScore = 0;
storage.highScoreClearedOnce = true;
}
// High score text, smaller and below the main score
var highScore = storage.highScore || 0;
var highScoreText = new Text2('High: ' + highScore, {
size: 60,
fill: 0xFFFFFF,
weight: '400',
dropShadow: true,
dropShadowColor: '#373330',
dropShadowBlur: 2,
dropShadowAngle: Math.PI / 6,
dropShadowDistance: 3
});
highScoreText.anchor.set(0, 0);
// Position just below the scoreText (scoreText is 150px tall, add a little margin)
highScoreText.y = scoreText.height + 10;
LK.gui.top.addChild(highScoreText);
var notificationTexts = ["I'm trying.", "We can't change what's done.", "Be loyal to what matters.", "I gave you all I had.", "I tried, in the end. I did.", "You, sir, are a fish.", "LENNNNAAAAYYYY!", "Vengeance is an idiot's game.", "We're more ghosts than people.", "Oh, and a quarter. Don't forget the quarter.", "Yeah, I got TB.", "I guess I... I'm afraid."];
var usedNotificationTexts = [];
function getRandomNotificationText() {
if (notificationTexts.length === 0) {
notificationTexts = usedNotificationTexts.splice(0, usedNotificationTexts.length);
}
var index = Math.floor(Math.random() * notificationTexts.length);
var text = notificationTexts.splice(index, 1)[0];
usedNotificationTexts.push(text);
return text;
}
var notificationText = new Text2(getRandomNotificationText(), {
size: 60,
fill: 0xFFFFFF,
weight: '400',
align: 'center',
stroke: '#000000',
strokeThickness: 8
});
notificationText.anchor.set(0, 4);
notificationText.x -= 350;
LK.gui.bottom.addChild(notificationText);
var car = mainContainer.addChild(new Car());
car.x = 2048 / 2;
car.y = 2732 / 2;
var driver = LK.gui.addChild(new Driver());
driver.x = 250;
driver.y = 1800;
var isGameOver = false;
var score = 0;
var closestSegment = null;
game.on('down', function (x, y, obj) {
car.changeDirection();
var engineSound = LK.getSound('Engine');
engineSound.stop();
engineSound.play({
loop: true
});
});
// Removed background music restart on retry to ensure music always loops without restart
LK.on('tick', function () {
car._move_migrated();
var carIsOnRoad = false;
var carPosition = {
x: car.x,
y: car.y
};
var currentClosestSegment = null;
var currentClosestDistance = Infinity;
roadSegments.forEach(function (segment) {
var segmentStart = {
x: segment.x + Math.sin(segment.rotation) * 100,
y: segment.y - Math.cos(segment.rotation) * 100
};
var segmentEnd = {
x: segment.x - Math.sin(segment.rotation) * (segment.height - segment.width / 2),
y: segment.y + Math.cos(segment.rotation) * (segment.height - segment.width / 2)
};
var distance = game.calculateDistanceToPoint(carPosition, segmentStart, segmentEnd);
if (distance < currentClosestDistance) {
currentClosestDistance = distance;
currentClosestSegment = segment;
}
if (distance < segment.width / 2 - 50) {
carIsOnRoad = true;
}
});
// --- Cactus collision detection and fall over ---
var _loop = function _loop() {
cactus = cacti[i]; // Only trigger once per cactus
if (!cactus.fallen) {
// Use bounding box intersection
dx = Math.abs(car.x - cactus.x);
dy = Math.abs(car.y - cactus.y); // Use a reasonable collision box (car is ~400x310, cactus ~80x180)
if (dx < 120 && dy < 140) {
cactus.fallOver();
LK.getSound('Crash').play();
score++;
scoreText.setText(score.toString());
// Balloon effect when score reaches exactly 3
// (Balloon effect removed)
// Update high score if needed
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreText.setText('High: ' + highScore);
}
}
}
},
cactus,
dx,
dy,
balloon,
startY,
duration,
frame;
for (var i = 0; i < cacti.length; i++) {
_loop();
}
// ---
if (closestSegment !== currentClosestSegment && !currentClosestSegment.used) {
closestSegment = currentClosestSegment;
closestSegment.used = true;
score++;
car.ORIGINAL_SPEED += 0.1;
scoreText.setText(score.toString());
// Update high score if needed
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreText.setText('High: ' + highScore);
}
notificationText.setText(getRandomNotificationText());
}
if (!carIsOnRoad) {
// Play crash sound only once
if (!car.crashPlayed) {
car.crashPlayed = true;
LK.getSound('Crash').play();
// Stop the car's movement
car.speed = 0;
car.momentum.x = 0;
car.momentum.y = 0;
LK.setTimeout(function () {
LK.showGameOver();
// Removed background music restart after game over to ensure music always loops without restart
}, 1000);
}
} else {
// Reset crashPlayed if back on road (optional, in case of future logic)
car.crashPlayed = false;
}
var particleOffsets = [{
x: 20,
y: 140
}, {
x: 20 + 100,
y: 140 - 100
}, {
x: 20 - 150,
y: 140 - 150
}, {
x: 20 - 150 + 100,
y: 140 - 150 - 100
}];
for (var i = 0; i < particleOffsets.length; i++) {
var alphaValue = Math.max(0, Math.min(1, Math.abs(car.nonTravelMomentum) / 5 - 0.5));
if (alphaValue > 0) {
var particle = new Particle();
particle.alpha = alphaValue;
var noiseX = (Math.random() - 0.5) * 10;
var noiseY = (Math.random() - 0.5) * 10;
particle.x = car.x + (car.direction === 0 ? -1 : 1) * particleOffsets[i].x + noiseX;
particle.y = car.y + particleOffsets[i].y + noiseY;
mainContainer.addChildAt(particle, 1);
particles.push(particle);
}
}
particles.forEach(function (particle, index) {
particle.tick();
if (particle.lifetime <= 0) {
particles.splice(index, 1);
}
});
var carLocalPosition = game.toLocal(car.position, car.parent);
var offsetX = (2048 / 2 - carLocalPosition.x) / 20;
var offsetY = (2732 - 450 - carLocalPosition.y) / 20;
mainContainer.x += offsetX;
mainContainer.y += offsetY;
for (var i = roadSegments.length - 1; i >= 0; i--) {
var segmentGlobalPosition = game.toLocal(roadSegments[i].position, roadSegments[i].parent);
if (segmentGlobalPosition.y - roadSegments[i].height > 2732 * 2) {
roadSegments[i].shadow.destroy();
roadSegments[i].destroy();
roadSegments.splice(i, 1);
game.addRoadSegment();
}
}
});