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