/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { level: 1, bestTime: 0 }); /**** * Classes ****/ var Car = Container.expand(function () { var self = Container.call(this); var carGraphics = self.attachAsset('playerCar', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.update = function () { // Player car does not move forward automatically anymore // Only horizontal movement is allowed, so no change to self.y here if (self.x < 0) { self.x = 2048; } else if (self.x > 2048) { self.x = 0; } }; return self; }); var EnemyCar = Container.expand(function () { var self = Container.call(this); // List of possible enemy car asset ids for variety var enemyCarAssetIds = ['enemyCar', 'enemyCar2', 'enemyCar3', 'enemyCar4']; // Pick a random car asset for this enemy var chosenAssetId = enemyCarAssetIds[Math.floor(Math.random() * enemyCarAssetIds.length)]; var enemyCarGraphics = self.attachAsset(chosenAssetId, { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.update = function () { // Enemy cars move down the screen (already correct) self.y += self.speed; if (self.x < 0) { self.x = 2048; } else if (self.x > 2048) { self.x = 0; } }; return self; }); // Powerup: Speed Boost var Powerup = Container.expand(function () { var self = Container.call(this); var powerupGraphics = self.attachAsset('powerupSpeed', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.9, scaleY: 0.9 }); self.speed = 10; self.type = "speed"; self.update = function () { self.y += self.speed; // Remove if off screen if (self.y > 2900) { self.destroy(); return true; } return false; }; return self; }); // Powerup: Shield var PowerupShield = Container.expand(function () { var self = Container.call(this); var powerupGraphics = self.attachAsset('powerupShield', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.9, scaleY: 0.9 }); self.speed = 10; self.type = "shield"; self.update = function () { self.y += self.speed; // Remove if off screen if (self.y > 2900) { self.destroy(); return true; } return false; }; return self; }); var Smoke = Container.expand(function () { var self = Container.call(this); var smokeGraphics = self.attachAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5 }); // Initialize with random properties for varied effect self.speedX = (Math.random() - 0.5) * 2; // Random horizontal drift self.speedY = 1 + Math.random() * 2; // Move backward speed self.initialDistance = 0; // Track distance traveled self.maxAlpha = 0.8 + Math.random() * 0.2; // Maximum density at start self.alpha = self.maxAlpha; // Start with maximum opacity self.fadingDistance = 40 + Math.random() * 40; // Distance at which smoke starts to fade self.growSpeed = 0.03 + Math.random() * 0.03; // Expansion rate // Update smoke particle behavior self.update = function () { self.x += self.speedX; self.y += self.speedY; // Move downward (backward from car perspective) // Update distance traveled self.initialDistance += self.speedY; // Start dense and become less dense as it moves backward self.alpha = Math.max(0, self.maxAlpha * (1 - self.initialDistance / (self.fadingDistance * 2))); self.scale.x += self.growSpeed; self.scale.y += self.growSpeed; // Remove smoke when it becomes too transparent if (self.alpha <= 0) { self.destroy(); return true; // Signal that this smoke has been destroyed } return false; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Shield powerup asset // Speed powerup asset // Create score text // Additional car images for variety var score = 0; var scoreTxt = new Text2('Score: 0', { size: 100, fill: 0xFFFFFF }); // Add smoke effects var smokeParticles = []; function createSmoke(x, y, amount) { for (var i = 0; i < amount; i++) { var smoke = new Smoke(); // Alternate between left and right sides of the car var side = i % 2 === 0 ? -1 : 1; // Position smoke on the left or right side of the car smoke.x = x + side * 100 + (Math.random() - 0.5) * 20; // Offset from car sides smoke.y = y + 200; // Position behind car (below car in screen coordinates) smokeParticles.push(smoke); game.addChild(smoke); } } scoreTxt.anchor.set(0.5, 0); scoreTxt.alpha = 0; LK.gui.top.addChild(scoreTxt); tween(scoreTxt, { alpha: 1 }, 800, { easing: "easeOutCubic" }); var road = LK.getAsset('road', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366 }); road.alpha = 0; road.scale.set(1.05, 1.05); game.addChild(road); // Create a second road image for seamless looping var road2 = LK.getAsset('road', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366 - 2732 // Place directly above the first road }); road2.alpha = 0; road2.scale.set(1.05, 1.05); game.addChild(road2); tween(road, { alpha: 1 }, 1000, { easing: "easeOutCubic" }); tween(road2, { alpha: 1 }, 1000, { easing: "easeOutCubic" }); tween(road.scale, { x: 1, y: 1 }, 900, { easing: "easeOutCubic" }); tween(road2.scale, { x: 1, y: 1 }, 900, { easing: "easeOutCubic" }); // Add road movement variables var roadScrollSpeed = 16; var roadInitialY = 2366; var roadImageHeight = 2732; // Play background music with fade in LK.playMusic('bgmusic', { fade: { start: 0, end: 0.8, duration: 1000 } }); // Display high score var highScore = storage.highScore || 0; var highScoreTxt = new Text2('High Score: ' + highScore, { size: 80, fill: 0x00a000, // Lighter green color fontWeight: 'bold' // Make the text bold }); highScoreTxt.anchor.set(0.5, 0); highScoreTxt.y = 120; highScoreTxt.alpha = 0; LK.gui.top.addChild(highScoreTxt); tween(highScoreTxt, { alpha: 1 }, 1000, { easing: "easeOutCubic" }); var playerCar = game.addChild(new Car()); // Place player in left, center, or right lane randomly var laneXs = [512, 1024, 1536]; // 512 = left, 1024 = center, 1536 = right playerCar.x = laneXs[Math.floor(Math.random() * laneXs.length)]; playerCar.y = 2300; // Start near the bottom, adjusted for bigger car // Fade in the player car for a polished entrance playerCar.alpha = 0; playerCar.scale.set(1.2, 1.2); tween(playerCar, { alpha: 1 }, 600, { easing: "easeOutCubic" }); tween(playerCar.scale, { x: 1, y: 1 }, 400, { easing: "easeOutBack" }); var enemyCars = []; function spawnEnemyCar() { // Only allow enemy cars to spawn in the same vertical lines as the player (left, center, or right lane) // Lane Xs: left, center, right var laneXs = [512, 1024, 1536]; // Car height for vertical gap - increased multiplier for more space var carHeight = 660; var verticalGapMultiplier = 3.5; // Find the highest Y of any existing enemy car, or use -330 if none var topY = -330; for (var i = 0; i < enemyCars.length; i++) { if (enemyCars[i].y < topY) { topY = enemyCars[i].y; } } // Patterns: only left, only center, only right - never more than one at a time // to ensure player always has a path to pass through var patterns = [ // Only left lane [{ x: laneXs[0] }], // Only center lane [{ x: laneXs[1] }], // Only right lane [{ x: laneXs[2] }]]; // Keep track of which pattern to use next if (typeof spawnEnemyCar.patternIndex === "undefined") { spawnEnemyCar.patternIndex = 0; } // Shuffle patterns order every cycle for variety if (typeof spawnEnemyCar.patternOrder === "undefined" || spawnEnemyCar.patternOrder.length === 0) { var order = []; for (var i = 0; i < patterns.length; i++) { order.push(i); } // Fisher-Yates shuffle for (var i = order.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = order[i]; order[i] = order[j]; order[j] = temp; } spawnEnemyCar.patternOrder = order; spawnEnemyCar.patternIndex = 0; } var patternIdx = spawnEnemyCar.patternOrder[spawnEnemyCar.patternIndex]; var pattern = patterns[patternIdx]; spawnEnemyCar.patternIndex++; if (spawnEnemyCar.patternIndex >= spawnEnemyCar.patternOrder.length) { spawnEnemyCar.patternOrder = []; } // Place new pattern below the last one, with the specified gap var spawnY = topY; for (var i = 0; i < pattern.length; i++) { var carDef = pattern[i]; var enemyCar = new EnemyCar(); // Apply global speed bonus if any if (typeof globalEnemySpeedBonus !== "undefined") { enemyCar.speed += globalEnemySpeedBonus; } enemyCar.x = carDef.x; enemyCar.y = spawnY; enemyCars.push(enemyCar); game.addChild(enemyCar); spawnY -= carHeight * verticalGapMultiplier; } } game.update = function () { // Move both road images downward to simulate motion road.y += roadScrollSpeed; road2.y += roadScrollSpeed; // If a road image moves off the bottom, move it above the other for seamless looping if (road.y > roadInitialY + roadImageHeight / 2) { road.y = road2.y - roadImageHeight; } if (road2.y > roadInitialY + roadImageHeight / 2) { road2.y = road.y - roadImageHeight; } // No gradual movement needed; handled instantly in game.down // Only update playerCar for horizontal movement, not forward movement playerCar.update(); // Create smoke particles behind player car if (LK.ticks % 5 == 0) { createSmoke(playerCar.x, playerCar.y, 2); } // Update and manage smoke particles for (var i = smokeParticles.length - 1; i >= 0; i--) { var isDestroyed = smokeParticles[i].update(); if (isDestroyed) { smokeParticles.splice(i, 1); } } var _loop = function _loop() { enemyCar = enemyCars[i]; // Track previous position for scoring if (enemyCar.lastY === undefined) { enemyCar.lastY = enemyCar.y; enemyCar.scored = false; } enemyCar.update(); // No smoke for enemy cars // Score when car passes the player if (!enemyCar.scored && enemyCar.lastY < playerCar.y && enemyCar.y >= playerCar.y) { score += 10; scoreTxt.setText('Score: ' + score); // Increase speed every 50 points (much more) if (score % 50 === 0) { // Increase speed for player car playerCar.speed += 3; // Increase speed for all existing enemy cars for (var j = 0; j < enemyCars.length; j++) { enemyCars[j].speed += 3; } // Mark that all future enemy cars should also be faster if (typeof globalEnemySpeedBonus === "undefined") { globalEnemySpeedBonus = 0; } globalEnemySpeedBonus += 3; // Play gas sound effect LK.getSound('gas', { volume: 0.7 }).play(); } // Subtle pop animation for score text scoreTxt.scale.set(1.2, 1.2); tween(scoreTxt.scale, { x: 1, y: 1 }, 200, { easing: "easeOutBack" }); enemyCar.scored = true; } // Update last position enemyCar.lastY = enemyCar.y; if (enemyCar.y > 2832) { // If the enemy car goes off the screen enemyCar.destroy(); enemyCars.splice(i, 1); } if (playerCar.intersects(enemyCar)) { var _doShake = function doShake() { if (shakeIndex < shakeTimes) { playerCar.x = originalX + (shakeIndex % 2 === 0 ? shakeMagnitude : -shakeMagnitude); LK.setTimeout(function () { playerCar.x = originalX; shakeIndex++; _doShake(); }, shakeStep); } else { playerCar.x = originalX; // Update high score if needed if (score > highScore) { highScore = score; storage.highScore = highScore; highScoreTxt.setText('High Score: ' + highScore); // Subtle fade-in and pop effect for new high score highScoreTxt.alpha = 0.2; highScoreTxt.scale.set(1.3, 1.3); tween(highScoreTxt, { alpha: 1 }, 400, { easing: "easeOutCubic" }); tween(highScoreTxt.scale, { x: 1, y: 1 }, 300, { easing: "easeOutBack" }); } // Set the final score before game over LK.setScore(score); LK.showGameOver(); } }; // Subtle shake effect for player car originalX = playerCar.x; shakeTimes = 6; shakeMagnitude = 30; shakeDuration = 180; shakeStep = shakeDuration / shakeTimes; shakeIndex = 0; _doShake(); return { v: void 0 }; // Prevent double game over } }, enemyCar, originalX, shakeTimes, shakeMagnitude, shakeDuration, shakeStep, shakeIndex, _ret; for (var i = enemyCars.length - 1; i >= 0; i--) { _ret = _loop(); if (_ret) { return _ret.v; } } if (LK.ticks % 120 == 0) { // Spawn new enemy cars every 2 seconds to keep more space between them spawnEnemyCar(); } }; game.down = function (x, y, obj) { // 3 lanes: left (0-853), center (853-1195), right (1195-2048) if (x < 853) { playerCar.x = 512; // Left lane } else if (x < 1195) { playerCar.x = 1024; // Center lane } else { playerCar.x = 1536; // Right lane } }; game.up = function (x, y, obj) { // No action needed }; game.update = function () { // Move both road images downward to simulate motion road.y += roadScrollSpeed; road2.y += roadScrollSpeed; // If a road image moves off the bottom, move it above the other for seamless looping if (road.y > roadInitialY + roadImageHeight / 2) { road.y = road2.y - roadImageHeight; } if (road2.y > roadInitialY + roadImageHeight / 2) { road2.y = road.y - roadImageHeight; } // No gradual movement needed; handled instantly in game.down // Only update playerCar for horizontal movement, not forward movement playerCar.update(); // Create smoke particles behind player car if (LK.ticks % 5 == 0) { createSmoke(playerCar.x, playerCar.y, 2); } // Update and manage smoke particles for (var i = smokeParticles.length - 1; i >= 0; i--) { var isDestroyed = smokeParticles[i].update(); if (isDestroyed) { smokeParticles.splice(i, 1); } } // --- Powerup logic --- if (typeof powerups === "undefined") { powerups = []; } if (typeof playerShieldActive === "undefined") { playerShieldActive = false; playerShieldTimer = 0; } // Spawn powerups at intervals if (LK.ticks % 600 === 0) { // Every 10 seconds var which = Math.random() < 0.5 ? "speed" : "shield"; var laneXs = [512, 1024, 1536]; var px = laneXs[Math.floor(Math.random() * laneXs.length)]; var py = -100; var powerup; if (which === "speed") { powerup = new Powerup(); } else { powerup = new PowerupShield(); } powerup.x = px; powerup.y = py; powerups.push(powerup); game.addChild(powerup); } // Update powerups and check for collection for (var i = powerups.length - 1; i >= 0; i--) { var p = powerups[i]; var destroyed = p.update(); if (!destroyed && playerCar.intersects(p)) { // Powerup collected! if (p.type === "speed") { // Temporary speed boost playerCar.speed += 8; LK.setTimeout(function () { playerCar.speed -= 8; }, 3000); // Optional: flash effect LK.effects.flashObject(playerCar, 0x00bfff, 500); } else if (p.type === "shield") { playerShieldActive = true; playerShieldTimer = LK.ticks + 360; // 6 seconds at 60fps // Optional: flash effect LK.effects.flashObject(playerCar, 0xffd700, 500); } p.destroy(); powerups.splice(i, 1); } else if (destroyed) { powerups.splice(i, 1); } } // Handle shield timer if (playerShieldActive && LK.ticks > playerShieldTimer) { playerShieldActive = false; } var _loop = function _loop() { enemyCar = enemyCars[i]; // Track previous position for scoring if (enemyCar.lastY === undefined) { enemyCar.lastY = enemyCar.y; enemyCar.scored = false; } enemyCar.update(); // No smoke for enemy cars // Score when car passes the player if (!enemyCar.scored && enemyCar.lastY < playerCar.y && enemyCar.y >= playerCar.y) { score += 10; scoreTxt.setText('Score: ' + score); // Increase speed every 50 points (much more) if (score % 50 === 0) { // Increase speed for player car playerCar.speed += 3; // Increase speed for all existing enemy cars for (var j = 0; j < enemyCars.length; j++) { enemyCars[j].speed += 3; } // Mark that all future enemy cars should also be faster if (typeof globalEnemySpeedBonus === "undefined") { globalEnemySpeedBonus = 0; } globalEnemySpeedBonus += 3; // Play gas sound effect LK.getSound('gas', { volume: 0.7 }).play(); } // Subtle pop animation for score text scoreTxt.scale.set(1.2, 1.2); tween(scoreTxt.scale, { x: 1, y: 1 }, 200, { easing: "easeOutBack" }); enemyCar.scored = true; } // Update last position enemyCar.lastY = enemyCar.y; if (enemyCar.y > 2832) { // If the enemy car goes off the screen enemyCar.destroy(); enemyCars.splice(i, 1); } if (playerCar.intersects(enemyCar)) { if (playerShieldActive) { // Destroy enemy car and deactivate shield enemyCar.destroy(); enemyCars.splice(i, 1); playerShieldActive = false; LK.effects.flashObject(playerCar, 0xffd700, 400); return; } var _doShake = function doShake() { if (shakeIndex < shakeTimes) { playerCar.x = originalX + (shakeIndex % 2 === 0 ? shakeMagnitude : -shakeMagnitude); LK.setTimeout(function () { playerCar.x = originalX; shakeIndex++; _doShake(); }, shakeStep); } else { playerCar.x = originalX; // Update high score if needed if (score > highScore) { highScore = score; storage.highScore = highScore; highScoreTxt.setText('High Score: ' + highScore); // Subtle fade-in and pop effect for new high score highScoreTxt.alpha = 0.2; highScoreTxt.scale.set(1.3, 1.3); tween(highScoreTxt, { alpha: 1 }, 400, { easing: "easeOutCubic" }); tween(highScoreTxt.scale, { x: 1, y: 1 }, 300, { easing: "easeOutBack" }); } // Set the final score before game over LK.setScore(score); LK.showGameOver(); } }; // Subtle shake effect for player car originalX = playerCar.x; shakeTimes = 6; shakeMagnitude = 30; shakeDuration = 180; shakeStep = shakeDuration / shakeTimes; shakeIndex = 0; _doShake(); return { v: void 0 }; // Prevent double game over } }, enemyCar, originalX, shakeTimes, shakeMagnitude, shakeDuration, shakeStep, shakeIndex, _ret; for (var i = enemyCars.length - 1; i >= 0; i--) { _ret = _loop(); if (_ret) { return _ret.v; } } if (LK.ticks % 120 == 0) { // Spawn new enemy cars every 2 seconds to keep more space between them spawnEnemyCar(); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
level: 1,
bestTime: 0
});
/****
* Classes
****/
var Car = Container.expand(function () {
var self = Container.call(this);
var carGraphics = self.attachAsset('playerCar', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
// Player car does not move forward automatically anymore
// Only horizontal movement is allowed, so no change to self.y here
if (self.x < 0) {
self.x = 2048;
} else if (self.x > 2048) {
self.x = 0;
}
};
return self;
});
var EnemyCar = Container.expand(function () {
var self = Container.call(this);
// List of possible enemy car asset ids for variety
var enemyCarAssetIds = ['enemyCar', 'enemyCar2', 'enemyCar3', 'enemyCar4'];
// Pick a random car asset for this enemy
var chosenAssetId = enemyCarAssetIds[Math.floor(Math.random() * enemyCarAssetIds.length)];
var enemyCarGraphics = self.attachAsset(chosenAssetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
// Enemy cars move down the screen (already correct)
self.y += self.speed;
if (self.x < 0) {
self.x = 2048;
} else if (self.x > 2048) {
self.x = 0;
}
};
return self;
});
// Powerup: Speed Boost
var Powerup = Container.expand(function () {
var self = Container.call(this);
var powerupGraphics = self.attachAsset('powerupSpeed', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.9,
scaleY: 0.9
});
self.speed = 10;
self.type = "speed";
self.update = function () {
self.y += self.speed;
// Remove if off screen
if (self.y > 2900) {
self.destroy();
return true;
}
return false;
};
return self;
});
// Powerup: Shield
var PowerupShield = Container.expand(function () {
var self = Container.call(this);
var powerupGraphics = self.attachAsset('powerupShield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.9,
scaleY: 0.9
});
self.speed = 10;
self.type = "shield";
self.update = function () {
self.y += self.speed;
// Remove if off screen
if (self.y > 2900) {
self.destroy();
return true;
}
return false;
};
return self;
});
var Smoke = Container.expand(function () {
var self = Container.call(this);
var smokeGraphics = self.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
// Initialize with random properties for varied effect
self.speedX = (Math.random() - 0.5) * 2; // Random horizontal drift
self.speedY = 1 + Math.random() * 2; // Move backward speed
self.initialDistance = 0; // Track distance traveled
self.maxAlpha = 0.8 + Math.random() * 0.2; // Maximum density at start
self.alpha = self.maxAlpha; // Start with maximum opacity
self.fadingDistance = 40 + Math.random() * 40; // Distance at which smoke starts to fade
self.growSpeed = 0.03 + Math.random() * 0.03; // Expansion rate
// Update smoke particle behavior
self.update = function () {
self.x += self.speedX;
self.y += self.speedY; // Move downward (backward from car perspective)
// Update distance traveled
self.initialDistance += self.speedY;
// Start dense and become less dense as it moves backward
self.alpha = Math.max(0, self.maxAlpha * (1 - self.initialDistance / (self.fadingDistance * 2)));
self.scale.x += self.growSpeed;
self.scale.y += self.growSpeed;
// Remove smoke when it becomes too transparent
if (self.alpha <= 0) {
self.destroy();
return true; // Signal that this smoke has been destroyed
}
return false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Shield powerup asset
// Speed powerup asset
// Create score text
// Additional car images for variety
var score = 0;
var scoreTxt = new Text2('Score: 0', {
size: 100,
fill: 0xFFFFFF
});
// Add smoke effects
var smokeParticles = [];
function createSmoke(x, y, amount) {
for (var i = 0; i < amount; i++) {
var smoke = new Smoke();
// Alternate between left and right sides of the car
var side = i % 2 === 0 ? -1 : 1;
// Position smoke on the left or right side of the car
smoke.x = x + side * 100 + (Math.random() - 0.5) * 20; // Offset from car sides
smoke.y = y + 200; // Position behind car (below car in screen coordinates)
smokeParticles.push(smoke);
game.addChild(smoke);
}
}
scoreTxt.anchor.set(0.5, 0);
scoreTxt.alpha = 0;
LK.gui.top.addChild(scoreTxt);
tween(scoreTxt, {
alpha: 1
}, 800, {
easing: "easeOutCubic"
});
var road = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366
});
road.alpha = 0;
road.scale.set(1.05, 1.05);
game.addChild(road);
// Create a second road image for seamless looping
var road2 = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366 - 2732 // Place directly above the first road
});
road2.alpha = 0;
road2.scale.set(1.05, 1.05);
game.addChild(road2);
tween(road, {
alpha: 1
}, 1000, {
easing: "easeOutCubic"
});
tween(road2, {
alpha: 1
}, 1000, {
easing: "easeOutCubic"
});
tween(road.scale, {
x: 1,
y: 1
}, 900, {
easing: "easeOutCubic"
});
tween(road2.scale, {
x: 1,
y: 1
}, 900, {
easing: "easeOutCubic"
});
// Add road movement variables
var roadScrollSpeed = 16;
var roadInitialY = 2366;
var roadImageHeight = 2732;
// Play background music with fade in
LK.playMusic('bgmusic', {
fade: {
start: 0,
end: 0.8,
duration: 1000
}
});
// Display high score
var highScore = storage.highScore || 0;
var highScoreTxt = new Text2('High Score: ' + highScore, {
size: 80,
fill: 0x00a000,
// Lighter green color
fontWeight: 'bold' // Make the text bold
});
highScoreTxt.anchor.set(0.5, 0);
highScoreTxt.y = 120;
highScoreTxt.alpha = 0;
LK.gui.top.addChild(highScoreTxt);
tween(highScoreTxt, {
alpha: 1
}, 1000, {
easing: "easeOutCubic"
});
var playerCar = game.addChild(new Car());
// Place player in left, center, or right lane randomly
var laneXs = [512, 1024, 1536]; // 512 = left, 1024 = center, 1536 = right
playerCar.x = laneXs[Math.floor(Math.random() * laneXs.length)];
playerCar.y = 2300; // Start near the bottom, adjusted for bigger car
// Fade in the player car for a polished entrance
playerCar.alpha = 0;
playerCar.scale.set(1.2, 1.2);
tween(playerCar, {
alpha: 1
}, 600, {
easing: "easeOutCubic"
});
tween(playerCar.scale, {
x: 1,
y: 1
}, 400, {
easing: "easeOutBack"
});
var enemyCars = [];
function spawnEnemyCar() {
// Only allow enemy cars to spawn in the same vertical lines as the player (left, center, or right lane)
// Lane Xs: left, center, right
var laneXs = [512, 1024, 1536];
// Car height for vertical gap - increased multiplier for more space
var carHeight = 660;
var verticalGapMultiplier = 3.5;
// Find the highest Y of any existing enemy car, or use -330 if none
var topY = -330;
for (var i = 0; i < enemyCars.length; i++) {
if (enemyCars[i].y < topY) {
topY = enemyCars[i].y;
}
}
// Patterns: only left, only center, only right - never more than one at a time
// to ensure player always has a path to pass through
var patterns = [
// Only left lane
[{
x: laneXs[0]
}],
// Only center lane
[{
x: laneXs[1]
}],
// Only right lane
[{
x: laneXs[2]
}]];
// Keep track of which pattern to use next
if (typeof spawnEnemyCar.patternIndex === "undefined") {
spawnEnemyCar.patternIndex = 0;
}
// Shuffle patterns order every cycle for variety
if (typeof spawnEnemyCar.patternOrder === "undefined" || spawnEnemyCar.patternOrder.length === 0) {
var order = [];
for (var i = 0; i < patterns.length; i++) {
order.push(i);
}
// Fisher-Yates shuffle
for (var i = order.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = order[i];
order[i] = order[j];
order[j] = temp;
}
spawnEnemyCar.patternOrder = order;
spawnEnemyCar.patternIndex = 0;
}
var patternIdx = spawnEnemyCar.patternOrder[spawnEnemyCar.patternIndex];
var pattern = patterns[patternIdx];
spawnEnemyCar.patternIndex++;
if (spawnEnemyCar.patternIndex >= spawnEnemyCar.patternOrder.length) {
spawnEnemyCar.patternOrder = [];
}
// Place new pattern below the last one, with the specified gap
var spawnY = topY;
for (var i = 0; i < pattern.length; i++) {
var carDef = pattern[i];
var enemyCar = new EnemyCar();
// Apply global speed bonus if any
if (typeof globalEnemySpeedBonus !== "undefined") {
enemyCar.speed += globalEnemySpeedBonus;
}
enemyCar.x = carDef.x;
enemyCar.y = spawnY;
enemyCars.push(enemyCar);
game.addChild(enemyCar);
spawnY -= carHeight * verticalGapMultiplier;
}
}
game.update = function () {
// Move both road images downward to simulate motion
road.y += roadScrollSpeed;
road2.y += roadScrollSpeed;
// If a road image moves off the bottom, move it above the other for seamless looping
if (road.y > roadInitialY + roadImageHeight / 2) {
road.y = road2.y - roadImageHeight;
}
if (road2.y > roadInitialY + roadImageHeight / 2) {
road2.y = road.y - roadImageHeight;
}
// No gradual movement needed; handled instantly in game.down
// Only update playerCar for horizontal movement, not forward movement
playerCar.update();
// Create smoke particles behind player car
if (LK.ticks % 5 == 0) {
createSmoke(playerCar.x, playerCar.y, 2);
}
// Update and manage smoke particles
for (var i = smokeParticles.length - 1; i >= 0; i--) {
var isDestroyed = smokeParticles[i].update();
if (isDestroyed) {
smokeParticles.splice(i, 1);
}
}
var _loop = function _loop() {
enemyCar = enemyCars[i]; // Track previous position for scoring
if (enemyCar.lastY === undefined) {
enemyCar.lastY = enemyCar.y;
enemyCar.scored = false;
}
enemyCar.update();
// No smoke for enemy cars
// Score when car passes the player
if (!enemyCar.scored && enemyCar.lastY < playerCar.y && enemyCar.y >= playerCar.y) {
score += 10;
scoreTxt.setText('Score: ' + score);
// Increase speed every 50 points (much more)
if (score % 50 === 0) {
// Increase speed for player car
playerCar.speed += 3;
// Increase speed for all existing enemy cars
for (var j = 0; j < enemyCars.length; j++) {
enemyCars[j].speed += 3;
}
// Mark that all future enemy cars should also be faster
if (typeof globalEnemySpeedBonus === "undefined") {
globalEnemySpeedBonus = 0;
}
globalEnemySpeedBonus += 3;
// Play gas sound effect
LK.getSound('gas', {
volume: 0.7
}).play();
}
// Subtle pop animation for score text
scoreTxt.scale.set(1.2, 1.2);
tween(scoreTxt.scale, {
x: 1,
y: 1
}, 200, {
easing: "easeOutBack"
});
enemyCar.scored = true;
}
// Update last position
enemyCar.lastY = enemyCar.y;
if (enemyCar.y > 2832) {
// If the enemy car goes off the screen
enemyCar.destroy();
enemyCars.splice(i, 1);
}
if (playerCar.intersects(enemyCar)) {
var _doShake = function doShake() {
if (shakeIndex < shakeTimes) {
playerCar.x = originalX + (shakeIndex % 2 === 0 ? shakeMagnitude : -shakeMagnitude);
LK.setTimeout(function () {
playerCar.x = originalX;
shakeIndex++;
_doShake();
}, shakeStep);
} else {
playerCar.x = originalX;
// Update high score if needed
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreTxt.setText('High Score: ' + highScore);
// Subtle fade-in and pop effect for new high score
highScoreTxt.alpha = 0.2;
highScoreTxt.scale.set(1.3, 1.3);
tween(highScoreTxt, {
alpha: 1
}, 400, {
easing: "easeOutCubic"
});
tween(highScoreTxt.scale, {
x: 1,
y: 1
}, 300, {
easing: "easeOutBack"
});
}
// Set the final score before game over
LK.setScore(score);
LK.showGameOver();
}
};
// Subtle shake effect for player car
originalX = playerCar.x;
shakeTimes = 6;
shakeMagnitude = 30;
shakeDuration = 180;
shakeStep = shakeDuration / shakeTimes;
shakeIndex = 0;
_doShake();
return {
v: void 0
}; // Prevent double game over
}
},
enemyCar,
originalX,
shakeTimes,
shakeMagnitude,
shakeDuration,
shakeStep,
shakeIndex,
_ret;
for (var i = enemyCars.length - 1; i >= 0; i--) {
_ret = _loop();
if (_ret) {
return _ret.v;
}
}
if (LK.ticks % 120 == 0) {
// Spawn new enemy cars every 2 seconds to keep more space between them
spawnEnemyCar();
}
};
game.down = function (x, y, obj) {
// 3 lanes: left (0-853), center (853-1195), right (1195-2048)
if (x < 853) {
playerCar.x = 512; // Left lane
} else if (x < 1195) {
playerCar.x = 1024; // Center lane
} else {
playerCar.x = 1536; // Right lane
}
};
game.up = function (x, y, obj) {
// No action needed
};
game.update = function () {
// Move both road images downward to simulate motion
road.y += roadScrollSpeed;
road2.y += roadScrollSpeed;
// If a road image moves off the bottom, move it above the other for seamless looping
if (road.y > roadInitialY + roadImageHeight / 2) {
road.y = road2.y - roadImageHeight;
}
if (road2.y > roadInitialY + roadImageHeight / 2) {
road2.y = road.y - roadImageHeight;
}
// No gradual movement needed; handled instantly in game.down
// Only update playerCar for horizontal movement, not forward movement
playerCar.update();
// Create smoke particles behind player car
if (LK.ticks % 5 == 0) {
createSmoke(playerCar.x, playerCar.y, 2);
}
// Update and manage smoke particles
for (var i = smokeParticles.length - 1; i >= 0; i--) {
var isDestroyed = smokeParticles[i].update();
if (isDestroyed) {
smokeParticles.splice(i, 1);
}
}
// --- Powerup logic ---
if (typeof powerups === "undefined") {
powerups = [];
}
if (typeof playerShieldActive === "undefined") {
playerShieldActive = false;
playerShieldTimer = 0;
}
// Spawn powerups at intervals
if (LK.ticks % 600 === 0) {
// Every 10 seconds
var which = Math.random() < 0.5 ? "speed" : "shield";
var laneXs = [512, 1024, 1536];
var px = laneXs[Math.floor(Math.random() * laneXs.length)];
var py = -100;
var powerup;
if (which === "speed") {
powerup = new Powerup();
} else {
powerup = new PowerupShield();
}
powerup.x = px;
powerup.y = py;
powerups.push(powerup);
game.addChild(powerup);
}
// Update powerups and check for collection
for (var i = powerups.length - 1; i >= 0; i--) {
var p = powerups[i];
var destroyed = p.update();
if (!destroyed && playerCar.intersects(p)) {
// Powerup collected!
if (p.type === "speed") {
// Temporary speed boost
playerCar.speed += 8;
LK.setTimeout(function () {
playerCar.speed -= 8;
}, 3000);
// Optional: flash effect
LK.effects.flashObject(playerCar, 0x00bfff, 500);
} else if (p.type === "shield") {
playerShieldActive = true;
playerShieldTimer = LK.ticks + 360; // 6 seconds at 60fps
// Optional: flash effect
LK.effects.flashObject(playerCar, 0xffd700, 500);
}
p.destroy();
powerups.splice(i, 1);
} else if (destroyed) {
powerups.splice(i, 1);
}
}
// Handle shield timer
if (playerShieldActive && LK.ticks > playerShieldTimer) {
playerShieldActive = false;
}
var _loop = function _loop() {
enemyCar = enemyCars[i]; // Track previous position for scoring
if (enemyCar.lastY === undefined) {
enemyCar.lastY = enemyCar.y;
enemyCar.scored = false;
}
enemyCar.update();
// No smoke for enemy cars
// Score when car passes the player
if (!enemyCar.scored && enemyCar.lastY < playerCar.y && enemyCar.y >= playerCar.y) {
score += 10;
scoreTxt.setText('Score: ' + score);
// Increase speed every 50 points (much more)
if (score % 50 === 0) {
// Increase speed for player car
playerCar.speed += 3;
// Increase speed for all existing enemy cars
for (var j = 0; j < enemyCars.length; j++) {
enemyCars[j].speed += 3;
}
// Mark that all future enemy cars should also be faster
if (typeof globalEnemySpeedBonus === "undefined") {
globalEnemySpeedBonus = 0;
}
globalEnemySpeedBonus += 3;
// Play gas sound effect
LK.getSound('gas', {
volume: 0.7
}).play();
}
// Subtle pop animation for score text
scoreTxt.scale.set(1.2, 1.2);
tween(scoreTxt.scale, {
x: 1,
y: 1
}, 200, {
easing: "easeOutBack"
});
enemyCar.scored = true;
}
// Update last position
enemyCar.lastY = enemyCar.y;
if (enemyCar.y > 2832) {
// If the enemy car goes off the screen
enemyCar.destroy();
enemyCars.splice(i, 1);
}
if (playerCar.intersects(enemyCar)) {
if (playerShieldActive) {
// Destroy enemy car and deactivate shield
enemyCar.destroy();
enemyCars.splice(i, 1);
playerShieldActive = false;
LK.effects.flashObject(playerCar, 0xffd700, 400);
return;
}
var _doShake = function doShake() {
if (shakeIndex < shakeTimes) {
playerCar.x = originalX + (shakeIndex % 2 === 0 ? shakeMagnitude : -shakeMagnitude);
LK.setTimeout(function () {
playerCar.x = originalX;
shakeIndex++;
_doShake();
}, shakeStep);
} else {
playerCar.x = originalX;
// Update high score if needed
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreTxt.setText('High Score: ' + highScore);
// Subtle fade-in and pop effect for new high score
highScoreTxt.alpha = 0.2;
highScoreTxt.scale.set(1.3, 1.3);
tween(highScoreTxt, {
alpha: 1
}, 400, {
easing: "easeOutCubic"
});
tween(highScoreTxt.scale, {
x: 1,
y: 1
}, 300, {
easing: "easeOutBack"
});
}
// Set the final score before game over
LK.setScore(score);
LK.showGameOver();
}
};
// Subtle shake effect for player car
originalX = playerCar.x;
shakeTimes = 6;
shakeMagnitude = 30;
shakeDuration = 180;
shakeStep = shakeDuration / shakeTimes;
shakeIndex = 0;
_doShake();
return {
v: void 0
}; // Prevent double game over
}
},
enemyCar,
originalX,
shakeTimes,
shakeMagnitude,
shakeDuration,
shakeStep,
shakeIndex,
_ret;
for (var i = enemyCars.length - 1; i >= 0; i--) {
_ret = _loop();
if (_ret) {
return _ret.v;
}
}
if (LK.ticks % 120 == 0) {
// Spawn new enemy cars every 2 seconds to keep more space between them
spawnEnemyCar();
}
};