/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Barrier = Container.expand(function () {
var self = Container.call(this);
var barrierGraphics = self.attachAsset('barrier', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.blocking = true;
self.update = function () {
self.y += self.speed + gameSpeed;
// Add sliding animation
barrierGraphics.x = Math.sin(LK.ticks * 0.05) * 20;
};
return self;
});
var CharacterButton = Container.expand(function (vehicleType, vehicleAsset) {
var self = Container.call(this);
self.vehicleType = vehicleType;
var buttonBg = self.attachAsset('startButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
var vehiclePreview = self.attachAsset(vehicleAsset, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
self.down = function (x, y, obj) {
buttonBg.alpha = 0.7;
selectedVehicle = self.vehicleType;
// Update all character buttons to show selection
updateCharacterButtons();
};
self.up = function (x, y, obj) {
buttonBg.alpha = 1.0;
};
self.setSelected = function (isSelected) {
if (isSelected) {
buttonBg.tint = 0x00ff00;
vehiclePreview.tint = 0x00ff00;
} else {
buttonBg.tint = 0xffffff;
vehiclePreview.tint = 0xffffff;
}
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed + gameSpeed;
// Ensure cone doesn't rotate
obstacleGraphics.rotation = 0;
};
return self;
});
var OilSpill = Container.expand(function () {
var self = Container.call(this);
var oilGraphics = self.attachAsset('oilspill', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.slippery = true;
self.update = function () {
self.y += self.speed + gameSpeed;
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerupGraphics = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
// Power-up stays stationary, only rotate for visual effect
powerupGraphics.rotation += 0.1;
};
return self;
});
var Spike = Container.expand(function () {
var self = Container.call(this);
var spikeGraphics = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.deadly = true;
self.update = function () {
self.y += self.speed + gameSpeed;
// Add pulsing effect
spikeGraphics.scaleY = 1 + Math.sin(LK.ticks * 0.1) * 0.1;
};
return self;
});
var StartButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('startButton', {
anchorX: 0.5,
anchorY: 0.5
});
// Text removed from button
self.down = function (x, y, obj) {
buttonGraphics.alpha = 0.7;
if (!gameStarted && characterSelectionVisible) {
startGame();
}
};
self.up = function (x, y, obj) {
buttonGraphics.alpha = 1.0;
};
return self;
});
var TrapHole = Container.expand(function () {
var self = Container.call(this);
var trapGraphics = self.attachAsset('trap', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.stunEffect = true;
self.update = function () {
self.y += self.speed + gameSpeed;
};
return self;
});
var Vehicle = Container.expand(function () {
var self = Container.call(this);
var vehicleGraphics = self.attachAsset(selectedVehicle, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0;
self.targetX = 0;
self.update = function () {
// Check if vehicle is stunned (cannot move)
if (vehicleStunned) {
// Vehicle cannot move at all when stunned
return;
}
// Smooth movement toward target position
var dx = self.targetX - self.x;
var moveSpeed = originalVehicleSpeed;
// Apply slowdown effect if active
if (vehicleSlowed) {
moveSpeed = originalVehicleSpeed * 0.3; // 70% slower
}
if (Math.abs(dx) > 2) {
self.x += dx * moveSpeed;
} else {
self.x = self.targetX;
}
// Keep vehicle within bounds horizontally
var halfWidth = vehicleGraphics.width / 2;
if (self.x - halfWidth < 200) {
self.x = 200 + halfWidth;
self.targetX = self.x;
}
if (self.x + halfWidth > 1848) {
self.x = 1848 - halfWidth;
self.targetX = self.x;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
// Add background
var background = game.addChild(LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5
}));
background.x = 1024;
background.y = 1208;
// Add white car background made with pixels
var whiteCar = game.addChild(LK.getAsset('whitecar', {
anchorX: 0.5,
anchorY: 0.5
}));
whiteCar.x = 1024;
whiteCar.y = 1366;
whiteCar.tint = 0xffffff;
whiteCar.scaleX = 2;
whiteCar.scaleY = 2;
var gameSpeed = 5;
var maxSpeed = 20;
var speedIncrement = 0.01;
var distanceTraveled = 0;
var obstacles = [];
var powerups = [];
var vehicle = null;
var dragNode = null;
var gameRunning = true;
var gameStarted = false;
var lives = 3;
var maxLives = 3;
var heartIcons = [];
var speedBoostActive = false;
var speedBoostEndTime = 0;
var originalSpeed = 5;
var vehicleStunned = false;
var vehicleStunnedEndTime = 0;
var vehicleSlowed = false;
var vehicleSlowedEndTime = 0;
var originalVehicleSpeed = 0.15;
var vehicleInvincible = false;
var vehicleInvincibleEndTime = 0;
var startButton = null;
var selectedVehicle = 'vehicle';
var characterButtons = [];
var characterSelectionVisible = false;
// Create score display
var scoreTxt = new Text2('0', {
size: 100,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
scoreTxt.visible = false;
LK.gui.top.addChild(scoreTxt);
// Create speed display
var speedTxt = new Text2('Speed: 5', {
size: 60,
fill: 0xFFFFFF
});
speedTxt.anchor.set(0, 0);
speedTxt.x = 50;
speedTxt.y = 50;
speedTxt.visible = false;
LK.gui.topLeft.addChild(speedTxt);
// Create heart icons for lives display
for (var h = 0; h < maxLives; h++) {
var heart = LK.getAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
heart.x = 1950 - h * 80; // Position hearts from right to left
heart.y = 80;
heart.visible = false;
heartIcons.push(heart);
LK.gui.bottomRight.addChild(heart);
}
// Create character selection button
startButton = game.addChild(new StartButton());
startButton.x = 1024;
startButton.y = 1600;
// Create speedrunner logo for character selection
var speedrunnerLogo = game.addChild(LK.getAsset('speedrunner', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3
}));
speedrunnerLogo.x = 1024;
speedrunnerLogo.y = 600;
speedrunnerLogo.visible = false;
// Create character selection buttons (initially hidden)
var vehicleTypes = ['vehicle', 'vehicle2', 'vehicle3', 'vehicle4', 'truck', 'motorbike', 'policecar'];
var vehicleAssets = ['vehicle', 'vehicle2', 'vehicle3', 'vehicle4', 'truck', 'motorbike', 'policecar'];
for (var c = 0; c < vehicleTypes.length; c++) {
var charButton = game.addChild(new CharacterButton(vehicleTypes[c], vehicleAssets[c]));
charButton.x = 200 + c * 280;
charButton.y = 1200;
charButton.visible = false;
characterButtons.push(charButton);
}
// Set default selection
characterButtons[0].setSelected(true);
// Create vehicle (initially hidden) - positioned higher
vehicle = game.addChild(new Vehicle());
vehicle.x = 1024;
vehicle.y = 2000;
vehicle.targetX = vehicle.x;
vehicle.visible = false;
// Show character selection
function showCharacterSelection() {
characterSelectionVisible = true;
// Show speedrunner logo
speedrunnerLogo.visible = true;
// Show character buttons
for (var c = 0; c < characterButtons.length; c++) {
characterButtons[c].visible = true;
}
}
// Show character selection immediately on game start
showCharacterSelection();
// Update character button selection states
function updateCharacterButtons() {
for (var c = 0; c < characterButtons.length; c++) {
characterButtons[c].setSelected(characterButtons[c].vehicleType === selectedVehicle);
}
}
// Start game function
function startGame() {
gameStarted = true;
gameRunning = true;
// Hide start menu
startButton.visible = false;
whiteCar.visible = false;
// Hide speedrunner logo
speedrunnerLogo.visible = false;
// Hide character selection
for (var c = 0; c < characterButtons.length; c++) {
characterButtons[c].visible = false;
}
characterSelectionVisible = false;
// Recreate vehicle with selected character
vehicle.destroy();
vehicle = game.addChild(new Vehicle());
vehicle.x = 1024;
vehicle.y = 2000;
vehicle.targetX = vehicle.x;
vehicle.visible = true;
// Show UI elements
scoreTxt.visible = true;
speedTxt.visible = true;
// Show heart icons
for (var h = 0; h < heartIcons.length; h++) {
heartIcons[h].visible = true;
}
// Play startup sound when game starts
LK.getSound('startup').play();
// Start playing music on loop
LK.playMusic('musica');
}
// Spawn obstacles
function spawnObstacle() {
if (!gameRunning) return;
var obstacle;
var obstacleType = Math.random();
if (obstacleType < 0.3) {
obstacle = new Obstacle();
} else if (obstacleType < 0.5) {
obstacle = new OilSpill();
} else if (obstacleType < 0.7) {
obstacle = new Spike();
} else if (obstacleType < 0.9) {
obstacle = new TrapHole();
} else {
obstacle = new Barrier();
}
// Random position within track bounds
obstacle.x = 300 + Math.random() * 1448;
obstacle.y = -100;
obstacles.push(obstacle);
game.addChild(obstacle);
}
// Update and clean up power-ups
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
// Check if power-up went off screen
if (powerup.y > 2800) {
powerup.destroy();
powerups.splice(i, 1);
continue;
}
// Check collision with vehicle
if (powerup.intersects(vehicle)) {
// Activate speed boost
speedBoostActive = true;
speedBoostEndTime = LK.ticks + 600; // 10 seconds at 60 FPS
originalSpeed = gameSpeed;
gameSpeed = Math.min(gameSpeed + 10, maxSpeed); // Increase speed by 10
LK.getSound('powerup').play();
// Flash screen gold
LK.effects.flashScreen(0xffd700, 500);
// Animate vehicle with golden tint and pulsing effect
tween(vehicle, {
tint: 0xffd700
}, {
duration: 200
});
// Remove the power-up
powerup.destroy();
powerups.splice(i, 1);
continue;
}
}
// Update and clean up track lines
function spawnPowerUp() {
if (!gameRunning) return;
var powerup = new PowerUp();
// Random position within track bounds
powerup.x = 300 + Math.random() * 1448;
powerup.y = -100;
powerups.push(powerup);
game.addChild(powerup);
}
// Handle touch input
function handleMove(x, y, obj) {
if (!gameRunning) return;
if (dragNode) {
// Convert touch position to vehicle target position
vehicle.targetX = x;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
if (!gameRunning) return;
dragNode = vehicle;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Main game loop
game.update = function () {
if (!gameStarted || !gameRunning) return;
// Increase speed over time
if (gameSpeed < maxSpeed) {
gameSpeed += speedIncrement;
}
// Update distance traveled
distanceTraveled += gameSpeed;
// Update score based on distance
var score = Math.floor(distanceTraveled / 10);
LK.setScore(score);
scoreTxt.setText(score);
speedTxt.setText('Speed: ' + Math.floor(gameSpeed));
// Check win condition at 7,500 points
if (score >= 7500) {
gameRunning = false;
LK.setTimeout(function () {
LK.showYouWin();
}, 500);
return;
}
// Check if speed boost expired
if (speedBoostActive && LK.ticks > speedBoostEndTime) {
speedBoostActive = false;
// Restore normal speed (but keep natural progression)
gameSpeed = Math.max(originalSpeed, 5 + distanceTraveled / 10000 * speedIncrement);
// Stop tween and restore normal vehicle color
tween.stop(vehicle);
vehicle.tint = 0xffffff;
}
// Check if vehicle stun expired
if (vehicleStunned && LK.ticks > vehicleStunnedEndTime) {
vehicleStunned = false;
// Stop tween and restore normal vehicle color
tween.stop(vehicle);
vehicle.tint = 0xffffff;
// Reset rotation to normal
vehicle.rotation = 0;
}
// Check if vehicle slowdown expired
if (vehicleSlowed && LK.ticks > vehicleSlowedEndTime) {
vehicleSlowed = false;
// Stop tween and restore normal vehicle color if not stunned
if (!vehicleStunned) {
tween.stop(vehicle);
vehicle.tint = 0xffffff;
}
}
// Check if vehicle invincibility expired
if (vehicleInvincible && LK.ticks > vehicleInvincibleEndTime) {
vehicleInvincible = false;
// Stop flashing effect
tween.stop(vehicle, {
alpha: true
});
vehicle.alpha = 1.0;
}
// Progressive obstacle spawning - gradually increase frequency and probability
var baseSpawnRate = Math.max(70 - Math.floor(gameSpeed * 2), 30); // Faster spawn rate
var progressMultiplier = Math.min(1 + distanceTraveled / 5000, 3); // Up to 3x more likely over time
var spawnProbability = Math.min(0.3 + distanceTraveled / 8000, 0.8); // Start at 30%, increase to 80%
if (LK.ticks % baseSpawnRate === 0) {
// Multiple obstacle spawning based on progress
var numObstacles = Math.floor(progressMultiplier);
for (var spawns = 0; spawns < numObstacles; spawns++) {
// Check if we already have maximum number of obstacles on screen
if (obstacles.length >= 5) {
break; // Stop spawning if we have 5 or more obstacles
}
if (Math.random() < spawnProbability) {
spawnObstacle();
}
}
}
// Spawn power-ups occasionally (15% chance when spawning obstacles)
if (LK.ticks % Math.max(60 - Math.floor(gameSpeed), 30) === 0 && Math.random() < 0.15) {
spawnPowerUp();
}
// Update and clean up obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
// Check if obstacle went off screen
if (obstacle.y > 2800) {
obstacle.destroy();
obstacles.splice(i, 1);
continue;
}
// Check collision with vehicle
if (obstacle.intersects(vehicle) && !vehicleInvincible) {
if (obstacle.slippery) {
// Oil spill effect - vehicle slides randomly and slows down for 1 second
vehicle.targetX += (Math.random() - 0.5) * 400;
vehicleSlowed = true;
vehicleSlowedEndTime = LK.ticks + 60; // 1 second at 60 FPS
LK.effects.flashScreen(0x8B4513, 300);
// Tint vehicle brown to show oil effect
tween(vehicle, {
tint: 0x8B4513
}, {
duration: 200
});
// Slight damage but no life lost
} else if (obstacle.deadly) {
// Spike - instant death
lives = 0;
LK.effects.flashScreen(0xff0000, 1000);
LK.getSound('crash').play();
} else if (obstacle.stunEffect) {
// Trap hole - stun vehicle for 2 seconds (cannot move)
vehicleStunned = true;
vehicleStunnedEndTime = LK.ticks + 120; // 2 seconds at 60 FPS
// Add rotation animation while stunned
tween(vehicle, {
rotation: vehicle.rotation + Math.PI * 4 // 2 full rotations
}, {
duration: 2000,
// 2 seconds
easing: tween.easeOut
});
// No life lost, just stunning effect
} else if (obstacle.blocking) {
// Barrier - damages vehicle and pushes it back
lives--;
LK.getSound('crash').play();
// Push vehicle back slightly
vehicle.targetX = vehicle.x + (Math.random() - 0.5) * 200;
} else {
// Regular obstacle
lives--;
LK.effects.flashScreen(0xff0000, 500);
LK.getSound('crash').play();
}
// Hide a heart icon for regular, deadly, and blocking obstacles
if (!obstacle.slippery && !obstacle.stunEffect && lives >= 0 && lives < heartIcons.length) {
heartIcons[heartIcons.length - 1 - lives].visible = false;
}
// Activate invincibility for 1 second after any collision
vehicleInvincible = true;
vehicleInvincibleEndTime = LK.ticks + 60; // 1 second at 60 FPS
// Add flashing effect during invincibility
tween(vehicle, {
alpha: 0.3
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
if (vehicleInvincible) {
tween(vehicle, {
alpha: 1.0
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
if (vehicleInvincible) {
// Continue flashing by calling this recursively
tween(vehicle, {
alpha: 0.3
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
if (vehicleInvincible) {
tween(vehicle, {
alpha: 1.0
}, {
duration: 100
});
}
}
});
}
}
});
}
}
});
// Remove the obstacle that was hit
obstacle.destroy();
obstacles.splice(i, 1);
// Check if game over
if (lives <= 0) {
gameRunning = false;
LK.setTimeout(function () {
LK.showGameOver();
}, 500);
return;
}
continue;
}
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Barrier = Container.expand(function () {
var self = Container.call(this);
var barrierGraphics = self.attachAsset('barrier', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.blocking = true;
self.update = function () {
self.y += self.speed + gameSpeed;
// Add sliding animation
barrierGraphics.x = Math.sin(LK.ticks * 0.05) * 20;
};
return self;
});
var CharacterButton = Container.expand(function (vehicleType, vehicleAsset) {
var self = Container.call(this);
self.vehicleType = vehicleType;
var buttonBg = self.attachAsset('startButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
var vehiclePreview = self.attachAsset(vehicleAsset, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
self.down = function (x, y, obj) {
buttonBg.alpha = 0.7;
selectedVehicle = self.vehicleType;
// Update all character buttons to show selection
updateCharacterButtons();
};
self.up = function (x, y, obj) {
buttonBg.alpha = 1.0;
};
self.setSelected = function (isSelected) {
if (isSelected) {
buttonBg.tint = 0x00ff00;
vehiclePreview.tint = 0x00ff00;
} else {
buttonBg.tint = 0xffffff;
vehiclePreview.tint = 0xffffff;
}
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
self.y += self.speed + gameSpeed;
// Ensure cone doesn't rotate
obstacleGraphics.rotation = 0;
};
return self;
});
var OilSpill = Container.expand(function () {
var self = Container.call(this);
var oilGraphics = self.attachAsset('oilspill', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.slippery = true;
self.update = function () {
self.y += self.speed + gameSpeed;
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerupGraphics = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.update = function () {
// Power-up stays stationary, only rotate for visual effect
powerupGraphics.rotation += 0.1;
};
return self;
});
var Spike = Container.expand(function () {
var self = Container.call(this);
var spikeGraphics = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.deadly = true;
self.update = function () {
self.y += self.speed + gameSpeed;
// Add pulsing effect
spikeGraphics.scaleY = 1 + Math.sin(LK.ticks * 0.1) * 0.1;
};
return self;
});
var StartButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('startButton', {
anchorX: 0.5,
anchorY: 0.5
});
// Text removed from button
self.down = function (x, y, obj) {
buttonGraphics.alpha = 0.7;
if (!gameStarted && characterSelectionVisible) {
startGame();
}
};
self.up = function (x, y, obj) {
buttonGraphics.alpha = 1.0;
};
return self;
});
var TrapHole = Container.expand(function () {
var self = Container.call(this);
var trapGraphics = self.attachAsset('trap', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.stunEffect = true;
self.update = function () {
self.y += self.speed + gameSpeed;
};
return self;
});
var Vehicle = Container.expand(function () {
var self = Container.call(this);
var vehicleGraphics = self.attachAsset(selectedVehicle, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0;
self.targetX = 0;
self.update = function () {
// Check if vehicle is stunned (cannot move)
if (vehicleStunned) {
// Vehicle cannot move at all when stunned
return;
}
// Smooth movement toward target position
var dx = self.targetX - self.x;
var moveSpeed = originalVehicleSpeed;
// Apply slowdown effect if active
if (vehicleSlowed) {
moveSpeed = originalVehicleSpeed * 0.3; // 70% slower
}
if (Math.abs(dx) > 2) {
self.x += dx * moveSpeed;
} else {
self.x = self.targetX;
}
// Keep vehicle within bounds horizontally
var halfWidth = vehicleGraphics.width / 2;
if (self.x - halfWidth < 200) {
self.x = 200 + halfWidth;
self.targetX = self.x;
}
if (self.x + halfWidth > 1848) {
self.x = 1848 - halfWidth;
self.targetX = self.x;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
// Add background
var background = game.addChild(LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5
}));
background.x = 1024;
background.y = 1208;
// Add white car background made with pixels
var whiteCar = game.addChild(LK.getAsset('whitecar', {
anchorX: 0.5,
anchorY: 0.5
}));
whiteCar.x = 1024;
whiteCar.y = 1366;
whiteCar.tint = 0xffffff;
whiteCar.scaleX = 2;
whiteCar.scaleY = 2;
var gameSpeed = 5;
var maxSpeed = 20;
var speedIncrement = 0.01;
var distanceTraveled = 0;
var obstacles = [];
var powerups = [];
var vehicle = null;
var dragNode = null;
var gameRunning = true;
var gameStarted = false;
var lives = 3;
var maxLives = 3;
var heartIcons = [];
var speedBoostActive = false;
var speedBoostEndTime = 0;
var originalSpeed = 5;
var vehicleStunned = false;
var vehicleStunnedEndTime = 0;
var vehicleSlowed = false;
var vehicleSlowedEndTime = 0;
var originalVehicleSpeed = 0.15;
var vehicleInvincible = false;
var vehicleInvincibleEndTime = 0;
var startButton = null;
var selectedVehicle = 'vehicle';
var characterButtons = [];
var characterSelectionVisible = false;
// Create score display
var scoreTxt = new Text2('0', {
size: 100,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
scoreTxt.visible = false;
LK.gui.top.addChild(scoreTxt);
// Create speed display
var speedTxt = new Text2('Speed: 5', {
size: 60,
fill: 0xFFFFFF
});
speedTxt.anchor.set(0, 0);
speedTxt.x = 50;
speedTxt.y = 50;
speedTxt.visible = false;
LK.gui.topLeft.addChild(speedTxt);
// Create heart icons for lives display
for (var h = 0; h < maxLives; h++) {
var heart = LK.getAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
heart.x = 1950 - h * 80; // Position hearts from right to left
heart.y = 80;
heart.visible = false;
heartIcons.push(heart);
LK.gui.bottomRight.addChild(heart);
}
// Create character selection button
startButton = game.addChild(new StartButton());
startButton.x = 1024;
startButton.y = 1600;
// Create speedrunner logo for character selection
var speedrunnerLogo = game.addChild(LK.getAsset('speedrunner', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3
}));
speedrunnerLogo.x = 1024;
speedrunnerLogo.y = 600;
speedrunnerLogo.visible = false;
// Create character selection buttons (initially hidden)
var vehicleTypes = ['vehicle', 'vehicle2', 'vehicle3', 'vehicle4', 'truck', 'motorbike', 'policecar'];
var vehicleAssets = ['vehicle', 'vehicle2', 'vehicle3', 'vehicle4', 'truck', 'motorbike', 'policecar'];
for (var c = 0; c < vehicleTypes.length; c++) {
var charButton = game.addChild(new CharacterButton(vehicleTypes[c], vehicleAssets[c]));
charButton.x = 200 + c * 280;
charButton.y = 1200;
charButton.visible = false;
characterButtons.push(charButton);
}
// Set default selection
characterButtons[0].setSelected(true);
// Create vehicle (initially hidden) - positioned higher
vehicle = game.addChild(new Vehicle());
vehicle.x = 1024;
vehicle.y = 2000;
vehicle.targetX = vehicle.x;
vehicle.visible = false;
// Show character selection
function showCharacterSelection() {
characterSelectionVisible = true;
// Show speedrunner logo
speedrunnerLogo.visible = true;
// Show character buttons
for (var c = 0; c < characterButtons.length; c++) {
characterButtons[c].visible = true;
}
}
// Show character selection immediately on game start
showCharacterSelection();
// Update character button selection states
function updateCharacterButtons() {
for (var c = 0; c < characterButtons.length; c++) {
characterButtons[c].setSelected(characterButtons[c].vehicleType === selectedVehicle);
}
}
// Start game function
function startGame() {
gameStarted = true;
gameRunning = true;
// Hide start menu
startButton.visible = false;
whiteCar.visible = false;
// Hide speedrunner logo
speedrunnerLogo.visible = false;
// Hide character selection
for (var c = 0; c < characterButtons.length; c++) {
characterButtons[c].visible = false;
}
characterSelectionVisible = false;
// Recreate vehicle with selected character
vehicle.destroy();
vehicle = game.addChild(new Vehicle());
vehicle.x = 1024;
vehicle.y = 2000;
vehicle.targetX = vehicle.x;
vehicle.visible = true;
// Show UI elements
scoreTxt.visible = true;
speedTxt.visible = true;
// Show heart icons
for (var h = 0; h < heartIcons.length; h++) {
heartIcons[h].visible = true;
}
// Play startup sound when game starts
LK.getSound('startup').play();
// Start playing music on loop
LK.playMusic('musica');
}
// Spawn obstacles
function spawnObstacle() {
if (!gameRunning) return;
var obstacle;
var obstacleType = Math.random();
if (obstacleType < 0.3) {
obstacle = new Obstacle();
} else if (obstacleType < 0.5) {
obstacle = new OilSpill();
} else if (obstacleType < 0.7) {
obstacle = new Spike();
} else if (obstacleType < 0.9) {
obstacle = new TrapHole();
} else {
obstacle = new Barrier();
}
// Random position within track bounds
obstacle.x = 300 + Math.random() * 1448;
obstacle.y = -100;
obstacles.push(obstacle);
game.addChild(obstacle);
}
// Update and clean up power-ups
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
// Check if power-up went off screen
if (powerup.y > 2800) {
powerup.destroy();
powerups.splice(i, 1);
continue;
}
// Check collision with vehicle
if (powerup.intersects(vehicle)) {
// Activate speed boost
speedBoostActive = true;
speedBoostEndTime = LK.ticks + 600; // 10 seconds at 60 FPS
originalSpeed = gameSpeed;
gameSpeed = Math.min(gameSpeed + 10, maxSpeed); // Increase speed by 10
LK.getSound('powerup').play();
// Flash screen gold
LK.effects.flashScreen(0xffd700, 500);
// Animate vehicle with golden tint and pulsing effect
tween(vehicle, {
tint: 0xffd700
}, {
duration: 200
});
// Remove the power-up
powerup.destroy();
powerups.splice(i, 1);
continue;
}
}
// Update and clean up track lines
function spawnPowerUp() {
if (!gameRunning) return;
var powerup = new PowerUp();
// Random position within track bounds
powerup.x = 300 + Math.random() * 1448;
powerup.y = -100;
powerups.push(powerup);
game.addChild(powerup);
}
// Handle touch input
function handleMove(x, y, obj) {
if (!gameRunning) return;
if (dragNode) {
// Convert touch position to vehicle target position
vehicle.targetX = x;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
if (!gameRunning) return;
dragNode = vehicle;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Main game loop
game.update = function () {
if (!gameStarted || !gameRunning) return;
// Increase speed over time
if (gameSpeed < maxSpeed) {
gameSpeed += speedIncrement;
}
// Update distance traveled
distanceTraveled += gameSpeed;
// Update score based on distance
var score = Math.floor(distanceTraveled / 10);
LK.setScore(score);
scoreTxt.setText(score);
speedTxt.setText('Speed: ' + Math.floor(gameSpeed));
// Check win condition at 7,500 points
if (score >= 7500) {
gameRunning = false;
LK.setTimeout(function () {
LK.showYouWin();
}, 500);
return;
}
// Check if speed boost expired
if (speedBoostActive && LK.ticks > speedBoostEndTime) {
speedBoostActive = false;
// Restore normal speed (but keep natural progression)
gameSpeed = Math.max(originalSpeed, 5 + distanceTraveled / 10000 * speedIncrement);
// Stop tween and restore normal vehicle color
tween.stop(vehicle);
vehicle.tint = 0xffffff;
}
// Check if vehicle stun expired
if (vehicleStunned && LK.ticks > vehicleStunnedEndTime) {
vehicleStunned = false;
// Stop tween and restore normal vehicle color
tween.stop(vehicle);
vehicle.tint = 0xffffff;
// Reset rotation to normal
vehicle.rotation = 0;
}
// Check if vehicle slowdown expired
if (vehicleSlowed && LK.ticks > vehicleSlowedEndTime) {
vehicleSlowed = false;
// Stop tween and restore normal vehicle color if not stunned
if (!vehicleStunned) {
tween.stop(vehicle);
vehicle.tint = 0xffffff;
}
}
// Check if vehicle invincibility expired
if (vehicleInvincible && LK.ticks > vehicleInvincibleEndTime) {
vehicleInvincible = false;
// Stop flashing effect
tween.stop(vehicle, {
alpha: true
});
vehicle.alpha = 1.0;
}
// Progressive obstacle spawning - gradually increase frequency and probability
var baseSpawnRate = Math.max(70 - Math.floor(gameSpeed * 2), 30); // Faster spawn rate
var progressMultiplier = Math.min(1 + distanceTraveled / 5000, 3); // Up to 3x more likely over time
var spawnProbability = Math.min(0.3 + distanceTraveled / 8000, 0.8); // Start at 30%, increase to 80%
if (LK.ticks % baseSpawnRate === 0) {
// Multiple obstacle spawning based on progress
var numObstacles = Math.floor(progressMultiplier);
for (var spawns = 0; spawns < numObstacles; spawns++) {
// Check if we already have maximum number of obstacles on screen
if (obstacles.length >= 5) {
break; // Stop spawning if we have 5 or more obstacles
}
if (Math.random() < spawnProbability) {
spawnObstacle();
}
}
}
// Spawn power-ups occasionally (15% chance when spawning obstacles)
if (LK.ticks % Math.max(60 - Math.floor(gameSpeed), 30) === 0 && Math.random() < 0.15) {
spawnPowerUp();
}
// Update and clean up obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
// Check if obstacle went off screen
if (obstacle.y > 2800) {
obstacle.destroy();
obstacles.splice(i, 1);
continue;
}
// Check collision with vehicle
if (obstacle.intersects(vehicle) && !vehicleInvincible) {
if (obstacle.slippery) {
// Oil spill effect - vehicle slides randomly and slows down for 1 second
vehicle.targetX += (Math.random() - 0.5) * 400;
vehicleSlowed = true;
vehicleSlowedEndTime = LK.ticks + 60; // 1 second at 60 FPS
LK.effects.flashScreen(0x8B4513, 300);
// Tint vehicle brown to show oil effect
tween(vehicle, {
tint: 0x8B4513
}, {
duration: 200
});
// Slight damage but no life lost
} else if (obstacle.deadly) {
// Spike - instant death
lives = 0;
LK.effects.flashScreen(0xff0000, 1000);
LK.getSound('crash').play();
} else if (obstacle.stunEffect) {
// Trap hole - stun vehicle for 2 seconds (cannot move)
vehicleStunned = true;
vehicleStunnedEndTime = LK.ticks + 120; // 2 seconds at 60 FPS
// Add rotation animation while stunned
tween(vehicle, {
rotation: vehicle.rotation + Math.PI * 4 // 2 full rotations
}, {
duration: 2000,
// 2 seconds
easing: tween.easeOut
});
// No life lost, just stunning effect
} else if (obstacle.blocking) {
// Barrier - damages vehicle and pushes it back
lives--;
LK.getSound('crash').play();
// Push vehicle back slightly
vehicle.targetX = vehicle.x + (Math.random() - 0.5) * 200;
} else {
// Regular obstacle
lives--;
LK.effects.flashScreen(0xff0000, 500);
LK.getSound('crash').play();
}
// Hide a heart icon for regular, deadly, and blocking obstacles
if (!obstacle.slippery && !obstacle.stunEffect && lives >= 0 && lives < heartIcons.length) {
heartIcons[heartIcons.length - 1 - lives].visible = false;
}
// Activate invincibility for 1 second after any collision
vehicleInvincible = true;
vehicleInvincibleEndTime = LK.ticks + 60; // 1 second at 60 FPS
// Add flashing effect during invincibility
tween(vehicle, {
alpha: 0.3
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
if (vehicleInvincible) {
tween(vehicle, {
alpha: 1.0
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
if (vehicleInvincible) {
// Continue flashing by calling this recursively
tween(vehicle, {
alpha: 0.3
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
if (vehicleInvincible) {
tween(vehicle, {
alpha: 1.0
}, {
duration: 100
});
}
}
});
}
}
});
}
}
});
// Remove the obstacle that was hit
obstacle.destroy();
obstacles.splice(i, 1);
// Check if game over
if (lives <= 0) {
gameRunning = false;
LK.setTimeout(function () {
LK.showGameOver();
}, 500);
return;
}
continue;
}
}
};
un propulsor turbo de carreras que expulse fuego con perspectiva hacia arriba In-Game asset. 2d. High contrast. No shadows
carretera oscura con perspectiva desde arriba. In-Game asset. 2d. High contrast. No shadows
carro azul deportivo con la perspectiva hacia arriba. In-Game asset. 2d. High contrast. No shadows hecho con pixeles acelerando con perspectiva trasera sin el rastro
un carro rojo deportivo con la perspectiva hacia arriba. In-Game asset. 2d. High contrast. No shadows hecho con pixeles
tres corazones hechos con pixeles. In-Game asset. 2d. High contrast. No shadows
un coche blanco deportivo hecho con pixeles con perspectiva desde arriba de la parte trasera. In-Game asset. 2d. High contrast. No shadows
tira de puas hecho con pixeles. In-Game asset. 2d. High contrast. No shadows
boton rojo hecho con pixeles con la palabra play en medio In-Game asset. 2d. High contrast. No shadows
un auto verde deportivo con perspectiva desde arriba hecho con pixeles. In-Game asset. 2d. High contrast. No shadows con perspectiva trasera
un auto amarillo deportivo con perspectiva desde arriba hecho con pixeles. In-Game asset. 2d. High contrast. No shadows con perspectiva trasera
un auto negro deportivo hecho con pixeles con la perspectiva desde arriba y trasera. In-Game asset. 2d. High contrast. No shadows
agujero de la calle hecho con pixeles. In-Game asset. 2d. High contrast. No shadows
un barril derramando petroleo negro hecho con pixeles In-Game asset. 2d. High contrast. No shadows
coche de policia hecho con pixeles con perspectiva desde arriba In-Game asset. 2d. High contrast. No shadows
motociclista de chaqueta negra hecha con pixeles con perspectiva desde arriba y trasera. In-Game asset. 2d. High contrast. No shadows
auto celeste deportivo con perspectiva desde arriba y trasera hecho con pixeles. In-Game asset. 2d. High contrast. No shadows
letras que dicen speed runner las letras speed tienen un color blanco con contorno negro y las letras runner un color celeste con un contorno azul todo hecho con pixeles y con una cursiva. In-Game asset. 2d. High contrast. No shadows