/****
* 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: 1.5,
scaleY: 1.5
});
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: 1.5,
scaleY: 1.5
});
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"
});
// Add shadow under player car
var playerCarShadow = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 0.35,
x: 0,
y: 0,
alpha: 0.25,
tint: 0x000000
});
game.addChild(playerCarShadow);
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;
// --- Add shadow under enemy car for polish ---
var enemyCarShadow = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.1,
scaleY: 0.32,
x: enemyCar.x,
y: enemyCar.y + 180,
alpha: 0.18,
tint: 0x000000
});
enemyCar._shadow = enemyCarShadow;
game.addChild(enemyCarShadow);
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
// Update shadow position if exists
if (enemyCar._shadow) {
enemyCar._shadow.x = enemyCar.x;
enemyCar._shadow.y = enemyCar.y + 180;
enemyCar._shadow.alpha = 0.18 + 0.07 * Math.abs(Math.sin(LK.ticks * 0.08));
}
// 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
if (enemyCar._shadow) {
// Fade out shadow before destroying
(function (shadow) {
tween(shadow, {
alpha: 0
}, 200, {
easing: "easeOutCubic",
complete: function complete() {
shadow.destroy();
}
});
})(enemyCar._shadow);
}
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();
if (typeof playerCarShadow !== "undefined" && playerCarShadow) {
playerCarShadow.x = playerCar.x;
playerCarShadow.y = playerCar.y + 180;
playerCarShadow.alpha = 0.18 + 0.07 * Math.abs(Math.sin(LK.ticks * 0.08));
}
// 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);
}
// Powerup pop/fade effect
var pop = LK.getAsset(p.type === "speed" ? "powerupSpeed" : "powerupShield", {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.2,
scaleY: 2.2,
x: p.x,
y: p.y,
alpha: 1
});
game.addChild(pop);
tween(pop.scale, {
x: 2.7,
y: 2.7
}, 350, {
easing: "easeOutCubic"
});
tween(pop, {
alpha: 0
}, 350, {
easing: "easeOutCubic",
complete: function complete() {
pop.destroy();
}
});
p.destroy();
powerups.splice(i, 1);
} else if (destroyed) {
powerups.splice(i, 1);
}
}
// Handle shield timer
if (playerShieldActive && LK.ticks > playerShieldTimer) {
playerShieldActive = false;
// Shield lost pop/fade effect
if (typeof playerShieldSprite !== "undefined" && playerShieldSprite) {
var pop = LK.getAsset('powerupShield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: playerShieldSprite.scale.x,
scaleY: playerShieldSprite.scale.y,
x: playerShieldSprite.x,
y: playerShieldSprite.y,
alpha: 0.5
});
game.addChild(pop);
tween(pop.scale, {
x: pop.scale.x * 1.4,
y: pop.scale.y * 1.4
}, 350, {
easing: "easeOutCubic"
});
tween(pop, {
alpha: 0
}, 350, {
easing: "easeOutCubic",
complete: function complete() {
pop.destroy();
}
});
}
}
// Show shield visual effect if shield is active
if (playerShieldActive) {
if (typeof playerShieldSprite === "undefined" || !playerShieldSprite) {
playerShieldSprite = LK.getAsset('powerupShield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.2,
scaleY: 2.2,
alpha: 0.5
});
playerShieldSprite.y = playerCar.y;
playerShieldSprite.x = playerCar.x;
game.addChild(playerShieldSprite);
} else {
playerShieldSprite.x = playerCar.x;
playerShieldSprite.y = playerCar.y;
playerShieldSprite.alpha = 0.5 + 0.2 * Math.sin(LK.ticks * 0.2);
playerShieldSprite.scale.x = 2.2 + 0.1 * Math.sin(LK.ticks * 0.15);
playerShieldSprite.scale.y = 2.2 + 0.1 * Math.sin(LK.ticks * 0.15);
}
} else if (typeof playerShieldSprite !== "undefined" && playerShieldSprite) {
playerShieldSprite.destroy();
playerShieldSprite = null;
}
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;
// Increase road scroll speed as well
roadScrollSpeed += 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
// Subtle pop/fade effect for enemy car destroyed by going off screen
var pop = LK.getAsset('enemyCar', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: enemyCar.scale ? enemyCar.scale.x : 1,
scaleY: enemyCar.scale ? enemyCar.scale.y : 1,
x: enemyCar.x,
y: enemyCar.y,
alpha: 1
});
game.addChild(pop);
tween(pop.scale, {
x: pop.scale.x * 1.2,
y: pop.scale.y * 1.2
}, 250, {
easing: "easeOutCubic"
});
tween(pop, {
alpha: 0
}, 250, {
easing: "easeOutCubic",
complete: function complete() {
pop.destroy();
}
});
if (enemyCar._shadow) {
(function (shadow) {
tween(shadow, {
alpha: 0
}, 200, {
easing: "easeOutCubic",
complete: function complete() {
shadow.destroy();
}
});
})(enemyCar._shadow);
}
enemyCar.destroy();
enemyCars.splice(i, 1);
}
if (playerCar.intersects(enemyCar)) {
if (playerShieldActive) {
// Destroy enemy car and deactivate shield
// Subtle pop/fade effect for enemy car destroyed by shield
var pop = LK.getAsset('enemyCar', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: enemyCar.scale ? enemyCar.scale.x : 1,
scaleY: enemyCar.scale ? enemyCar.scale.y : 1,
x: enemyCar.x,
y: enemyCar.y,
alpha: 1
});
game.addChild(pop);
tween(pop.scale, {
x: pop.scale.x * 1.5,
y: pop.scale.y * 1.5
}, 350, {
easing: "easeOutCubic"
});
tween(pop, {
alpha: 0
}, 350, {
easing: "easeOutCubic",
complete: function complete() {
pop.destroy();
}
});
if (enemyCar._shadow) {
(function (shadow) {
tween(shadow, {
alpha: 0
}, 200, {
easing: "easeOutCubic",
complete: function complete() {
shadow.destroy();
}
});
})(enemyCar._shadow);
}
enemyCar.destroy();
enemyCars.splice(i, 1);
playerShieldActive = false;
LK.effects.flashObject(playerCar, 0xffd700, 400);
// Shield block pop/fade effect
if (typeof playerShieldSprite !== "undefined" && playerShieldSprite) {
var pop = LK.getAsset('powerupShield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: playerShieldSprite.scale.x,
scaleY: playerShieldSprite.scale.y,
x: playerShieldSprite.x,
y: playerShieldSprite.y,
alpha: 0.5
});
game.addChild(pop);
tween(pop.scale, {
x: pop.scale.x * 1.4,
y: pop.scale.y * 1.4
}, 350, {
easing: "easeOutCubic"
});
tween(pop, {
alpha: 0
}, 350, {
easing: "easeOutCubic",
complete: function complete() {
pop.destroy();
}
});
}
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);
// Reset all speed variables to initial values on game over
playerCar.speed = 8;
roadScrollSpeed = 16;
globalEnemySpeedBonus = 0;
for (var j = 0; j < enemyCars.length; j++) {
enemyCars[j].speed = 8;
}
// Player car destroyed pop/fade effect
var pop = LK.getAsset('playerCar', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: playerCar.scale.x,
scaleY: playerCar.scale.y,
x: playerCar.x,
y: playerCar.y,
alpha: 1
});
game.addChild(pop);
tween(pop.scale, {
x: pop.scale.x * 1.5,
y: pop.scale.y * 1.5
}, 350, {
easing: "easeOutCubic"
});
tween(pop, {
alpha: 0
}, 350, {
easing: "easeOutCubic",
complete: function complete() {
pop.destroy();
}
});
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: 1.5,
scaleY: 1.5
});
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: 1.5,
scaleY: 1.5
});
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"
});
// Add shadow under player car
var playerCarShadow = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 0.35,
x: 0,
y: 0,
alpha: 0.25,
tint: 0x000000
});
game.addChild(playerCarShadow);
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;
// --- Add shadow under enemy car for polish ---
var enemyCarShadow = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.1,
scaleY: 0.32,
x: enemyCar.x,
y: enemyCar.y + 180,
alpha: 0.18,
tint: 0x000000
});
enemyCar._shadow = enemyCarShadow;
game.addChild(enemyCarShadow);
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
// Update shadow position if exists
if (enemyCar._shadow) {
enemyCar._shadow.x = enemyCar.x;
enemyCar._shadow.y = enemyCar.y + 180;
enemyCar._shadow.alpha = 0.18 + 0.07 * Math.abs(Math.sin(LK.ticks * 0.08));
}
// 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
if (enemyCar._shadow) {
// Fade out shadow before destroying
(function (shadow) {
tween(shadow, {
alpha: 0
}, 200, {
easing: "easeOutCubic",
complete: function complete() {
shadow.destroy();
}
});
})(enemyCar._shadow);
}
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();
if (typeof playerCarShadow !== "undefined" && playerCarShadow) {
playerCarShadow.x = playerCar.x;
playerCarShadow.y = playerCar.y + 180;
playerCarShadow.alpha = 0.18 + 0.07 * Math.abs(Math.sin(LK.ticks * 0.08));
}
// 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);
}
// Powerup pop/fade effect
var pop = LK.getAsset(p.type === "speed" ? "powerupSpeed" : "powerupShield", {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.2,
scaleY: 2.2,
x: p.x,
y: p.y,
alpha: 1
});
game.addChild(pop);
tween(pop.scale, {
x: 2.7,
y: 2.7
}, 350, {
easing: "easeOutCubic"
});
tween(pop, {
alpha: 0
}, 350, {
easing: "easeOutCubic",
complete: function complete() {
pop.destroy();
}
});
p.destroy();
powerups.splice(i, 1);
} else if (destroyed) {
powerups.splice(i, 1);
}
}
// Handle shield timer
if (playerShieldActive && LK.ticks > playerShieldTimer) {
playerShieldActive = false;
// Shield lost pop/fade effect
if (typeof playerShieldSprite !== "undefined" && playerShieldSprite) {
var pop = LK.getAsset('powerupShield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: playerShieldSprite.scale.x,
scaleY: playerShieldSprite.scale.y,
x: playerShieldSprite.x,
y: playerShieldSprite.y,
alpha: 0.5
});
game.addChild(pop);
tween(pop.scale, {
x: pop.scale.x * 1.4,
y: pop.scale.y * 1.4
}, 350, {
easing: "easeOutCubic"
});
tween(pop, {
alpha: 0
}, 350, {
easing: "easeOutCubic",
complete: function complete() {
pop.destroy();
}
});
}
}
// Show shield visual effect if shield is active
if (playerShieldActive) {
if (typeof playerShieldSprite === "undefined" || !playerShieldSprite) {
playerShieldSprite = LK.getAsset('powerupShield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.2,
scaleY: 2.2,
alpha: 0.5
});
playerShieldSprite.y = playerCar.y;
playerShieldSprite.x = playerCar.x;
game.addChild(playerShieldSprite);
} else {
playerShieldSprite.x = playerCar.x;
playerShieldSprite.y = playerCar.y;
playerShieldSprite.alpha = 0.5 + 0.2 * Math.sin(LK.ticks * 0.2);
playerShieldSprite.scale.x = 2.2 + 0.1 * Math.sin(LK.ticks * 0.15);
playerShieldSprite.scale.y = 2.2 + 0.1 * Math.sin(LK.ticks * 0.15);
}
} else if (typeof playerShieldSprite !== "undefined" && playerShieldSprite) {
playerShieldSprite.destroy();
playerShieldSprite = null;
}
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;
// Increase road scroll speed as well
roadScrollSpeed += 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
// Subtle pop/fade effect for enemy car destroyed by going off screen
var pop = LK.getAsset('enemyCar', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: enemyCar.scale ? enemyCar.scale.x : 1,
scaleY: enemyCar.scale ? enemyCar.scale.y : 1,
x: enemyCar.x,
y: enemyCar.y,
alpha: 1
});
game.addChild(pop);
tween(pop.scale, {
x: pop.scale.x * 1.2,
y: pop.scale.y * 1.2
}, 250, {
easing: "easeOutCubic"
});
tween(pop, {
alpha: 0
}, 250, {
easing: "easeOutCubic",
complete: function complete() {
pop.destroy();
}
});
if (enemyCar._shadow) {
(function (shadow) {
tween(shadow, {
alpha: 0
}, 200, {
easing: "easeOutCubic",
complete: function complete() {
shadow.destroy();
}
});
})(enemyCar._shadow);
}
enemyCar.destroy();
enemyCars.splice(i, 1);
}
if (playerCar.intersects(enemyCar)) {
if (playerShieldActive) {
// Destroy enemy car and deactivate shield
// Subtle pop/fade effect for enemy car destroyed by shield
var pop = LK.getAsset('enemyCar', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: enemyCar.scale ? enemyCar.scale.x : 1,
scaleY: enemyCar.scale ? enemyCar.scale.y : 1,
x: enemyCar.x,
y: enemyCar.y,
alpha: 1
});
game.addChild(pop);
tween(pop.scale, {
x: pop.scale.x * 1.5,
y: pop.scale.y * 1.5
}, 350, {
easing: "easeOutCubic"
});
tween(pop, {
alpha: 0
}, 350, {
easing: "easeOutCubic",
complete: function complete() {
pop.destroy();
}
});
if (enemyCar._shadow) {
(function (shadow) {
tween(shadow, {
alpha: 0
}, 200, {
easing: "easeOutCubic",
complete: function complete() {
shadow.destroy();
}
});
})(enemyCar._shadow);
}
enemyCar.destroy();
enemyCars.splice(i, 1);
playerShieldActive = false;
LK.effects.flashObject(playerCar, 0xffd700, 400);
// Shield block pop/fade effect
if (typeof playerShieldSprite !== "undefined" && playerShieldSprite) {
var pop = LK.getAsset('powerupShield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: playerShieldSprite.scale.x,
scaleY: playerShieldSprite.scale.y,
x: playerShieldSprite.x,
y: playerShieldSprite.y,
alpha: 0.5
});
game.addChild(pop);
tween(pop.scale, {
x: pop.scale.x * 1.4,
y: pop.scale.y * 1.4
}, 350, {
easing: "easeOutCubic"
});
tween(pop, {
alpha: 0
}, 350, {
easing: "easeOutCubic",
complete: function complete() {
pop.destroy();
}
});
}
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);
// Reset all speed variables to initial values on game over
playerCar.speed = 8;
roadScrollSpeed = 16;
globalEnemySpeedBonus = 0;
for (var j = 0; j < enemyCars.length; j++) {
enemyCars[j].speed = 8;
}
// Player car destroyed pop/fade effect
var pop = LK.getAsset('playerCar', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: playerCar.scale.x,
scaleY: playerCar.scale.y,
x: playerCar.x,
y: playerCar.y,
alpha: 1
});
game.addChild(pop);
tween(pop.scale, {
x: pop.scale.x * 1.5,
y: pop.scale.y * 1.5
}, 350, {
easing: "easeOutCubic"
});
tween(pop, {
alpha: 0
}, 350, {
easing: "easeOutCubic",
complete: function complete() {
pop.destroy();
}
});
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();
}
};