/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0, carsUnlocked: 1 }); /**** * Classes ****/ var Car = Container.expand(function () { var self = Container.call(this); // Properties self.velocity = { x: 0, y: 0 }; self.speed = 0; self.maxSpeed = 15; self.acceleration = 0.2; self.deceleration = 0.1; self.direction = 0; // Angle in radians self.drifting = false; self.driftDirection = 0; self.driftPower = 0.5; self.driftFactor = 0; self.steeringPower = 0.05; self.turningRadius = 0.04; // Graphics var carGraphics = self.attachAsset('car', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5 }); // Trail container to hold drift trails self.trailContainer = new Container(); self.addChild(self.trailContainer); // Drift trail particles self.trailParticles = []; // Methods self.accelerate = function () { if (self.speed < self.maxSpeed) { self.speed += self.acceleration; if (self.speed > self.maxSpeed) { self.speed = self.maxSpeed; } } }; self.decelerate = function () { if (self.speed > 0) { self.speed -= self.deceleration; if (self.speed < 0) { self.speed = 0; } } }; self.startDrift = function (direction) { self.drifting = true; self.driftDirection = direction; LK.getSound('drift').play(); }; self.stopDrift = function () { self.drifting = false; self.driftFactor = 0; }; self.addTrailParticle = function () { if (self.speed > 1 && self.drifting) { var trail = LK.getAsset('driftTrail', { anchorX: 0.5, anchorY: 0.5, alpha: 0.7 }); trail.x = 0; trail.y = 0; self.trailContainer.addChild(trail); // Store the global position for this trail particle var globalPos = self.trailContainer.toGlobal({ x: 0, y: 0 }); trail.globalX = globalPos.x; trail.globalY = globalPos.y; self.trailParticles.push(trail); // Fade out the trail particle tween(trail, { alpha: 0 }, { duration: 1000, onFinish: function onFinish() { if (trail.parent) { trail.parent.removeChild(trail); } var index = self.trailParticles.indexOf(trail); if (index !== -1) { self.trailParticles.splice(index, 1); } } }); } }; self.update = function () { // Apply physics if (self.drifting) { self.driftFactor = Math.min(1, self.driftFactor + 0.05); self.direction += self.driftDirection * self.driftPower * self.driftFactor; // Add trail particles at a rate based on speed if (LK.ticks % Math.max(1, Math.floor(10 / self.speed)) === 0) { self.addTrailParticle(); } } else { // Normal steering self.direction += self.driftDirection * self.steeringPower; } // Update velocity based on speed and direction self.velocity.x = Math.sin(self.direction) * self.speed; self.velocity.y = -Math.cos(self.direction) * self.speed; // Apply velocity to position self.x += self.velocity.x; self.y += self.velocity.y; // Set rotation to match direction carGraphics.rotation = self.direction; // Natural speed decay self.decelerate(); // Update position of trail container self.trailContainer.x = -self.x; self.trailContainer.y = -self.y; }; return self; }); var ScorePopup = Container.expand(function (value, x, y) { var self = Container.call(this); // Create the popup var popup = self.attachAsset('scorePopup', { anchorX: 0.5, anchorY: 0.5 }); // Add score text var scoreText = new Text2("+" + value, { size: 40, fill: 0x000000 }); scoreText.anchor.set(0.5, 0.5); self.addChild(scoreText); // Set initial position self.x = x; self.y = y; // Animate the popup tween(self, { y: y - 100, alpha: 0 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { if (self.parent) { self.parent.removeChild(self); } } }); return self; }); var Track = Container.expand(function () { var self = Container.call(this); // Track background var trackBackground = self.attachAsset('track', { anchorX: 0.5, anchorY: 0.5 }); // Track elements self.borders = []; self.checkpoints = []; self.corners = []; // Create track layout self.createTrack = function (level) { // Clear existing track elements for (var i = 0; i < self.borders.length; i++) { self.removeChild(self.borders[i]); } self.borders = []; for (var i = 0; i < self.checkpoints.length; i++) { self.removeChild(self.checkpoints[i]); } self.checkpoints = []; for (var i = 0; i < self.corners.length; i++) { self.removeChild(self.corners[i]); } self.corners = []; // Track width and height var trackWidth = trackBackground.width; var trackHeight = trackBackground.height; // Create border walls // Top border var topBorder = self.createBorder(0, -trackHeight / 2, trackWidth, 50); // Bottom border var bottomBorder = self.createBorder(0, trackHeight / 2, trackWidth, 50); // Left border var leftBorder = self.createBorder(-trackWidth / 2, 0, 50, trackHeight); // Right border var rightBorder = self.createBorder(trackWidth / 2, 0, 50, trackHeight); // Add obstacles based on level if (level >= 1) { // Center obstacle self.createBorder(0, 0, 500, 100); // Create checkpoints self.createCheckpoint(-trackWidth / 4, -trackHeight / 4, 0, 200, 50); self.createCheckpoint(trackWidth / 4, trackHeight / 4, Math.PI / 2, 200, 50); // Create corners for drift points self.createCorner(-trackWidth / 4, trackHeight / 4, 300); self.createCorner(trackWidth / 4, -trackHeight / 4, 300); } if (level >= 2) { // Add more obstacles self.createBorder(-trackWidth / 4, -trackHeight / 3, 100, 400); self.createBorder(trackWidth / 4, trackHeight / 3, 100, 400); // Additional checkpoints self.createCheckpoint(0, trackHeight / 3, Math.PI, 200, 50); self.createCheckpoint(0, -trackHeight / 3, 0, 200, 50); // More corners self.createCorner(-trackWidth / 3, 0, 250); self.createCorner(trackWidth / 3, 0, 250); } }; self.createBorder = function (x, y, width, height) { var border = LK.getAsset('trackBorder', { anchorX: 0.5, anchorY: 0.5, scaleX: width / 50, scaleY: height / 50 }); border.x = x; border.y = y; border.width = width; border.height = height; self.addChild(border); self.borders.push(border); return border; }; self.createCheckpoint = function (x, y, rotation, width, height) { var checkpoint = LK.getAsset('checkpoint', { anchorX: 0.5, anchorY: 0.5, scaleX: width / 200, scaleY: height / 50, alpha: 0.7 }); checkpoint.x = x; checkpoint.y = y; checkpoint.rotation = rotation; checkpoint.width = width; checkpoint.height = height; checkpoint.passed = false; self.addChild(checkpoint); self.checkpoints.push(checkpoint); return checkpoint; }; self.createCorner = function (x, y, size) { var corner = LK.getAsset('corner', { anchorX: 0.5, anchorY: 0.5, scaleX: size / 300, scaleY: size / 300, alpha: 0.3 }); corner.x = x; corner.y = y; corner.size = size; self.addChild(corner); self.corners.push(corner); return corner; }; // Check collision between car and track elements self.checkCollisions = function (car) { // Check border collisions for (var i = 0; i < self.borders.length; i++) { if (car.intersects(self.borders[i])) { return { type: 'border', element: self.borders[i] }; } } // Check checkpoint collisions for (var i = 0; i < self.checkpoints.length; i++) { if (!self.checkpoints[i].passed && car.intersects(self.checkpoints[i])) { self.checkpoints[i].passed = true; return { type: 'checkpoint', element: self.checkpoints[i] }; } } // Check if in corner for drift points for (var i = 0; i < self.corners.length; i++) { if (car.intersects(self.corners[i])) { return { type: 'corner', element: self.corners[i] }; } } return null; }; // Check if all checkpoints have been passed self.allCheckpointsPassed = function () { for (var i = 0; i < self.checkpoints.length; i++) { if (!self.checkpoints[i].passed) { return false; } } return self.checkpoints.length > 0; }; // Reset checkpoints self.resetCheckpoints = function () { for (var i = 0; i < self.checkpoints.length; i++) { self.checkpoints[i].passed = false; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x008080 }); /**** * Game Code ****/ // Music // Sounds // Game feedback // Drift trail // Track elements // Car // Game state var gameState = 'ready'; // ready, playing, gameover var currentLevel = 1; var score = 0; var highScore = storage.highScore || 0; var driftScore = 0; var driftMultiplier = 1; var driftCombo = 0; var lastDriftTime = 0; var checkpointValue = 100; var car; var track; var swipeStartX = 0; var swipeStartY = 0; var touchStart = false; // UI Elements var scoreText = new Text2("SCORE: 0", { size: 50, fill: 0xFFFFFF }); scoreText.anchor.set(0, 0); LK.gui.topRight.addChild(scoreText); scoreText.x = -200; scoreText.y = 20; var highScoreText = new Text2("BEST: " + highScore, { size: 30, fill: 0xCCCCCC }); highScoreText.anchor.set(0, 0); LK.gui.topRight.addChild(highScoreText); highScoreText.x = -200; highScoreText.y = 80; var multiplierText = new Text2("x1", { size: 60, fill: 0xFFCC00 }); multiplierText.anchor.set(0.5, 0.5); multiplierText.alpha = 0; LK.gui.center.addChild(multiplierText); var instructionText = new Text2("TAP to accelerate\nSWIPE to drift", { size: 50, fill: 0xFFFFFF }); instructionText.anchor.set(0.5, 0.5); LK.gui.center.addChild(instructionText); instructionText.y = 300; // Initialize the game function initGame() { // Reset game state gameState = 'ready'; score = 0; driftScore = 0; driftMultiplier = 1; driftCombo = 0; // Update UI updateScoreText(); multiplierText.alpha = 0; instructionText.alpha = 1; // Create track if (!track) { track = new Track(); game.addChild(track); } track.createTrack(currentLevel); // Create car if (!car) { car = new Car(); game.addChild(car); } // Reset car position car.x = 0; car.y = 0; car.speed = 0; car.direction = 0; car.drifting = false; car.driftDirection = 0; car.driftFactor = 0; // Clear all trail particles for (var i = 0; i < car.trailParticles.length; i++) { if (car.trailParticles[i].parent) { car.trailParticles[i].parent.removeChild(car.trailParticles[i]); } } car.trailParticles = []; // Play music LK.playMusic('bgmusic'); } // Update the score display function updateScoreText() { scoreText.setText("SCORE: " + score); highScoreText.setText("BEST: " + highScore); } // Create a score popup function createScorePopup(value, x, y) { var popup = new ScorePopup(value, x, y); game.addChild(popup); } // Handle drift scoring function handleDriftPoints() { // Check if still drifting if (car.drifting && car.speed > 5) { // Check time since last drift point var currentTime = Date.now(); var timeDiff = currentTime - lastDriftTime; // Add drift points every 500ms while drifting if (timeDiff >= 500) { lastDriftTime = currentTime; // Calculate points based on speed and drift factor var points = Math.floor(car.speed * car.driftFactor * driftMultiplier); // Bonus for drifting in a corner var collision = track.checkCollisions(car); if (collision && collision.type === 'corner') { points *= 2; } // Add points to score if (points > 0) { score += points; createScorePopup(points, car.x, car.y); // Increase combo driftCombo++; // Update multiplier based on combo driftMultiplier = Math.min(5, 1 + Math.floor(driftCombo / 3)); // Show multiplier multiplierText.setText("x" + driftMultiplier); multiplierText.alpha = 1; tween.stop(multiplierText); tween(multiplierText, { scale: 1.3 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(multiplierText, { scale: 1 }, { duration: 200, easing: tween.easeIn }); } }); } } } else if (!car.drifting && driftCombo > 0) { // Reset combo when not drifting driftCombo = 0; driftMultiplier = 1; // Hide multiplier tween(multiplierText, { alpha: 0 }, { duration: 500, easing: tween.easeOut }); } } // Handle checkpoint scoring function handleCheckpoint() { var points = checkpointValue * driftMultiplier; score += points; createScorePopup(points, car.x, car.y); LK.getSound('checkpoint').play(); // Flash the checkpoint text var checkpointScoreText = new Text2("CHECKPOINT: +" + points, { size: 60, fill: 0xFFCC00 }); checkpointScoreText.anchor.set(0.5, 0.5); LK.gui.center.addChild(checkpointScoreText); checkpointScoreText.y = -200; tween(checkpointScoreText, { y: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { tween(checkpointScoreText, { alpha: 0 }, { duration: 1000, easing: tween.easeIn, onFinish: function onFinish() { if (checkpointScoreText.parent) { checkpointScoreText.parent.removeChild(checkpointScoreText); } } }); } }); // Check if all checkpoints are passed if (track.allCheckpointsPassed()) { // Level complete! var levelCompleteText = new Text2("LEVEL COMPLETE!", { size: 80, fill: 0xFFCC00 }); levelCompleteText.anchor.set(0.5, 0.5); LK.gui.center.addChild(levelCompleteText); // Animate the level complete text tween(levelCompleteText, { scale: 1.5 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { tween(levelCompleteText, { alpha: 0 }, { duration: 1000, easing: tween.easeIn, onFinish: function onFinish() { if (levelCompleteText.parent) { levelCompleteText.parent.removeChild(levelCompleteText); } // Advance to next level currentLevel++; track.createTrack(currentLevel); track.resetCheckpoints(); } }); } }); } } // Handle game over function gameOver() { gameState = 'gameover'; // Update high score if necessary if (score > highScore) { highScore = score; storage.highScore = highScore; updateScoreText(); } // Show game over LK.showGameOver(); } // Game initialization initGame(); // Event handlers game.down = function (x, y, obj) { if (gameState === 'ready') { gameState = 'playing'; instructionText.alpha = 0; } if (gameState === 'playing') { // Start acceleration car.accelerate(); // Store start position for swipe detection swipeStartX = x; swipeStartY = y; touchStart = true; // Play engine sound LK.getSound('engine').play(); } }; game.up = function (x, y, obj) { if (gameState === 'playing') { // Stop drifting when releasing car.stopDrift(); touchStart = false; } }; game.move = function (x, y, obj) { if (gameState === 'playing' && touchStart) { // Calculate swipe direction and magnitude var deltaX = x - swipeStartX; var deltaY = y - swipeStartY; var swipeMagnitude = Math.sqrt(deltaX * deltaX + deltaY * deltaY); // Only register as a swipe if the magnitude is significant if (swipeMagnitude > 30) { // Determine drift direction based on swipe direction var swipeDirection = Math.atan2(deltaY, deltaX); // Simplified direction determination (left or right) var driftDir = 0; // Compare swipe angle to car direction to determine if it's a left or right drift var angleDiff = swipeDirection - car.direction; // Normalize angle to -PI to PI while (angleDiff > Math.PI) { angleDiff -= Math.PI * 2; } while (angleDiff < -Math.PI) { angleDiff += Math.PI * 2; } if (angleDiff > 0) { driftDir = 1; // Right drift } else { driftDir = -1; // Left drift } // Start drifting in the determined direction car.startDrift(driftDir); // Reset drift time tracking lastDriftTime = Date.now(); } } }; // Main game loop game.update = function () { if (gameState === 'playing') { // Update car physics car.update(); // Check collisions var collision = track.checkCollisions(car); if (collision) { if (collision.type === 'border') { // Hit a wall - game over gameOver(); } else if (collision.type === 'checkpoint' && !collision.element.passed) { // Passed checkpoint handleCheckpoint(); } // Corner collisions are handled in the drift scoring } // Handle drift scoring handleDriftPoints(); // Update score display updateScoreText(); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
carsUnlocked: 1
});
/****
* Classes
****/
var Car = Container.expand(function () {
var self = Container.call(this);
// Properties
self.velocity = {
x: 0,
y: 0
};
self.speed = 0;
self.maxSpeed = 15;
self.acceleration = 0.2;
self.deceleration = 0.1;
self.direction = 0; // Angle in radians
self.drifting = false;
self.driftDirection = 0;
self.driftPower = 0.5;
self.driftFactor = 0;
self.steeringPower = 0.05;
self.turningRadius = 0.04;
// Graphics
var carGraphics = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
// Trail container to hold drift trails
self.trailContainer = new Container();
self.addChild(self.trailContainer);
// Drift trail particles
self.trailParticles = [];
// Methods
self.accelerate = function () {
if (self.speed < self.maxSpeed) {
self.speed += self.acceleration;
if (self.speed > self.maxSpeed) {
self.speed = self.maxSpeed;
}
}
};
self.decelerate = function () {
if (self.speed > 0) {
self.speed -= self.deceleration;
if (self.speed < 0) {
self.speed = 0;
}
}
};
self.startDrift = function (direction) {
self.drifting = true;
self.driftDirection = direction;
LK.getSound('drift').play();
};
self.stopDrift = function () {
self.drifting = false;
self.driftFactor = 0;
};
self.addTrailParticle = function () {
if (self.speed > 1 && self.drifting) {
var trail = LK.getAsset('driftTrail', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
trail.x = 0;
trail.y = 0;
self.trailContainer.addChild(trail);
// Store the global position for this trail particle
var globalPos = self.trailContainer.toGlobal({
x: 0,
y: 0
});
trail.globalX = globalPos.x;
trail.globalY = globalPos.y;
self.trailParticles.push(trail);
// Fade out the trail particle
tween(trail, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
if (trail.parent) {
trail.parent.removeChild(trail);
}
var index = self.trailParticles.indexOf(trail);
if (index !== -1) {
self.trailParticles.splice(index, 1);
}
}
});
}
};
self.update = function () {
// Apply physics
if (self.drifting) {
self.driftFactor = Math.min(1, self.driftFactor + 0.05);
self.direction += self.driftDirection * self.driftPower * self.driftFactor;
// Add trail particles at a rate based on speed
if (LK.ticks % Math.max(1, Math.floor(10 / self.speed)) === 0) {
self.addTrailParticle();
}
} else {
// Normal steering
self.direction += self.driftDirection * self.steeringPower;
}
// Update velocity based on speed and direction
self.velocity.x = Math.sin(self.direction) * self.speed;
self.velocity.y = -Math.cos(self.direction) * self.speed;
// Apply velocity to position
self.x += self.velocity.x;
self.y += self.velocity.y;
// Set rotation to match direction
carGraphics.rotation = self.direction;
// Natural speed decay
self.decelerate();
// Update position of trail container
self.trailContainer.x = -self.x;
self.trailContainer.y = -self.y;
};
return self;
});
var ScorePopup = Container.expand(function (value, x, y) {
var self = Container.call(this);
// Create the popup
var popup = self.attachAsset('scorePopup', {
anchorX: 0.5,
anchorY: 0.5
});
// Add score text
var scoreText = new Text2("+" + value, {
size: 40,
fill: 0x000000
});
scoreText.anchor.set(0.5, 0.5);
self.addChild(scoreText);
// Set initial position
self.x = x;
self.y = y;
// Animate the popup
tween(self, {
y: y - 100,
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
if (self.parent) {
self.parent.removeChild(self);
}
}
});
return self;
});
var Track = Container.expand(function () {
var self = Container.call(this);
// Track background
var trackBackground = self.attachAsset('track', {
anchorX: 0.5,
anchorY: 0.5
});
// Track elements
self.borders = [];
self.checkpoints = [];
self.corners = [];
// Create track layout
self.createTrack = function (level) {
// Clear existing track elements
for (var i = 0; i < self.borders.length; i++) {
self.removeChild(self.borders[i]);
}
self.borders = [];
for (var i = 0; i < self.checkpoints.length; i++) {
self.removeChild(self.checkpoints[i]);
}
self.checkpoints = [];
for (var i = 0; i < self.corners.length; i++) {
self.removeChild(self.corners[i]);
}
self.corners = [];
// Track width and height
var trackWidth = trackBackground.width;
var trackHeight = trackBackground.height;
// Create border walls
// Top border
var topBorder = self.createBorder(0, -trackHeight / 2, trackWidth, 50);
// Bottom border
var bottomBorder = self.createBorder(0, trackHeight / 2, trackWidth, 50);
// Left border
var leftBorder = self.createBorder(-trackWidth / 2, 0, 50, trackHeight);
// Right border
var rightBorder = self.createBorder(trackWidth / 2, 0, 50, trackHeight);
// Add obstacles based on level
if (level >= 1) {
// Center obstacle
self.createBorder(0, 0, 500, 100);
// Create checkpoints
self.createCheckpoint(-trackWidth / 4, -trackHeight / 4, 0, 200, 50);
self.createCheckpoint(trackWidth / 4, trackHeight / 4, Math.PI / 2, 200, 50);
// Create corners for drift points
self.createCorner(-trackWidth / 4, trackHeight / 4, 300);
self.createCorner(trackWidth / 4, -trackHeight / 4, 300);
}
if (level >= 2) {
// Add more obstacles
self.createBorder(-trackWidth / 4, -trackHeight / 3, 100, 400);
self.createBorder(trackWidth / 4, trackHeight / 3, 100, 400);
// Additional checkpoints
self.createCheckpoint(0, trackHeight / 3, Math.PI, 200, 50);
self.createCheckpoint(0, -trackHeight / 3, 0, 200, 50);
// More corners
self.createCorner(-trackWidth / 3, 0, 250);
self.createCorner(trackWidth / 3, 0, 250);
}
};
self.createBorder = function (x, y, width, height) {
var border = LK.getAsset('trackBorder', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: width / 50,
scaleY: height / 50
});
border.x = x;
border.y = y;
border.width = width;
border.height = height;
self.addChild(border);
self.borders.push(border);
return border;
};
self.createCheckpoint = function (x, y, rotation, width, height) {
var checkpoint = LK.getAsset('checkpoint', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: width / 200,
scaleY: height / 50,
alpha: 0.7
});
checkpoint.x = x;
checkpoint.y = y;
checkpoint.rotation = rotation;
checkpoint.width = width;
checkpoint.height = height;
checkpoint.passed = false;
self.addChild(checkpoint);
self.checkpoints.push(checkpoint);
return checkpoint;
};
self.createCorner = function (x, y, size) {
var corner = LK.getAsset('corner', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: size / 300,
scaleY: size / 300,
alpha: 0.3
});
corner.x = x;
corner.y = y;
corner.size = size;
self.addChild(corner);
self.corners.push(corner);
return corner;
};
// Check collision between car and track elements
self.checkCollisions = function (car) {
// Check border collisions
for (var i = 0; i < self.borders.length; i++) {
if (car.intersects(self.borders[i])) {
return {
type: 'border',
element: self.borders[i]
};
}
}
// Check checkpoint collisions
for (var i = 0; i < self.checkpoints.length; i++) {
if (!self.checkpoints[i].passed && car.intersects(self.checkpoints[i])) {
self.checkpoints[i].passed = true;
return {
type: 'checkpoint',
element: self.checkpoints[i]
};
}
}
// Check if in corner for drift points
for (var i = 0; i < self.corners.length; i++) {
if (car.intersects(self.corners[i])) {
return {
type: 'corner',
element: self.corners[i]
};
}
}
return null;
};
// Check if all checkpoints have been passed
self.allCheckpointsPassed = function () {
for (var i = 0; i < self.checkpoints.length; i++) {
if (!self.checkpoints[i].passed) {
return false;
}
}
return self.checkpoints.length > 0;
};
// Reset checkpoints
self.resetCheckpoints = function () {
for (var i = 0; i < self.checkpoints.length; i++) {
self.checkpoints[i].passed = false;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x008080
});
/****
* Game Code
****/
// Music
// Sounds
// Game feedback
// Drift trail
// Track elements
// Car
// Game state
var gameState = 'ready'; // ready, playing, gameover
var currentLevel = 1;
var score = 0;
var highScore = storage.highScore || 0;
var driftScore = 0;
var driftMultiplier = 1;
var driftCombo = 0;
var lastDriftTime = 0;
var checkpointValue = 100;
var car;
var track;
var swipeStartX = 0;
var swipeStartY = 0;
var touchStart = false;
// UI Elements
var scoreText = new Text2("SCORE: 0", {
size: 50,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreText);
scoreText.x = -200;
scoreText.y = 20;
var highScoreText = new Text2("BEST: " + highScore, {
size: 30,
fill: 0xCCCCCC
});
highScoreText.anchor.set(0, 0);
LK.gui.topRight.addChild(highScoreText);
highScoreText.x = -200;
highScoreText.y = 80;
var multiplierText = new Text2("x1", {
size: 60,
fill: 0xFFCC00
});
multiplierText.anchor.set(0.5, 0.5);
multiplierText.alpha = 0;
LK.gui.center.addChild(multiplierText);
var instructionText = new Text2("TAP to accelerate\nSWIPE to drift", {
size: 50,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(instructionText);
instructionText.y = 300;
// Initialize the game
function initGame() {
// Reset game state
gameState = 'ready';
score = 0;
driftScore = 0;
driftMultiplier = 1;
driftCombo = 0;
// Update UI
updateScoreText();
multiplierText.alpha = 0;
instructionText.alpha = 1;
// Create track
if (!track) {
track = new Track();
game.addChild(track);
}
track.createTrack(currentLevel);
// Create car
if (!car) {
car = new Car();
game.addChild(car);
}
// Reset car position
car.x = 0;
car.y = 0;
car.speed = 0;
car.direction = 0;
car.drifting = false;
car.driftDirection = 0;
car.driftFactor = 0;
// Clear all trail particles
for (var i = 0; i < car.trailParticles.length; i++) {
if (car.trailParticles[i].parent) {
car.trailParticles[i].parent.removeChild(car.trailParticles[i]);
}
}
car.trailParticles = [];
// Play music
LK.playMusic('bgmusic');
}
// Update the score display
function updateScoreText() {
scoreText.setText("SCORE: " + score);
highScoreText.setText("BEST: " + highScore);
}
// Create a score popup
function createScorePopup(value, x, y) {
var popup = new ScorePopup(value, x, y);
game.addChild(popup);
}
// Handle drift scoring
function handleDriftPoints() {
// Check if still drifting
if (car.drifting && car.speed > 5) {
// Check time since last drift point
var currentTime = Date.now();
var timeDiff = currentTime - lastDriftTime;
// Add drift points every 500ms while drifting
if (timeDiff >= 500) {
lastDriftTime = currentTime;
// Calculate points based on speed and drift factor
var points = Math.floor(car.speed * car.driftFactor * driftMultiplier);
// Bonus for drifting in a corner
var collision = track.checkCollisions(car);
if (collision && collision.type === 'corner') {
points *= 2;
}
// Add points to score
if (points > 0) {
score += points;
createScorePopup(points, car.x, car.y);
// Increase combo
driftCombo++;
// Update multiplier based on combo
driftMultiplier = Math.min(5, 1 + Math.floor(driftCombo / 3));
// Show multiplier
multiplierText.setText("x" + driftMultiplier);
multiplierText.alpha = 1;
tween.stop(multiplierText);
tween(multiplierText, {
scale: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(multiplierText, {
scale: 1
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
}
} else if (!car.drifting && driftCombo > 0) {
// Reset combo when not drifting
driftCombo = 0;
driftMultiplier = 1;
// Hide multiplier
tween(multiplierText, {
alpha: 0
}, {
duration: 500,
easing: tween.easeOut
});
}
}
// Handle checkpoint scoring
function handleCheckpoint() {
var points = checkpointValue * driftMultiplier;
score += points;
createScorePopup(points, car.x, car.y);
LK.getSound('checkpoint').play();
// Flash the checkpoint text
var checkpointScoreText = new Text2("CHECKPOINT: +" + points, {
size: 60,
fill: 0xFFCC00
});
checkpointScoreText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(checkpointScoreText);
checkpointScoreText.y = -200;
tween(checkpointScoreText, {
y: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(checkpointScoreText, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeIn,
onFinish: function onFinish() {
if (checkpointScoreText.parent) {
checkpointScoreText.parent.removeChild(checkpointScoreText);
}
}
});
}
});
// Check if all checkpoints are passed
if (track.allCheckpointsPassed()) {
// Level complete!
var levelCompleteText = new Text2("LEVEL COMPLETE!", {
size: 80,
fill: 0xFFCC00
});
levelCompleteText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(levelCompleteText);
// Animate the level complete text
tween(levelCompleteText, {
scale: 1.5
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(levelCompleteText, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeIn,
onFinish: function onFinish() {
if (levelCompleteText.parent) {
levelCompleteText.parent.removeChild(levelCompleteText);
}
// Advance to next level
currentLevel++;
track.createTrack(currentLevel);
track.resetCheckpoints();
}
});
}
});
}
}
// Handle game over
function gameOver() {
gameState = 'gameover';
// Update high score if necessary
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
updateScoreText();
}
// Show game over
LK.showGameOver();
}
// Game initialization
initGame();
// Event handlers
game.down = function (x, y, obj) {
if (gameState === 'ready') {
gameState = 'playing';
instructionText.alpha = 0;
}
if (gameState === 'playing') {
// Start acceleration
car.accelerate();
// Store start position for swipe detection
swipeStartX = x;
swipeStartY = y;
touchStart = true;
// Play engine sound
LK.getSound('engine').play();
}
};
game.up = function (x, y, obj) {
if (gameState === 'playing') {
// Stop drifting when releasing
car.stopDrift();
touchStart = false;
}
};
game.move = function (x, y, obj) {
if (gameState === 'playing' && touchStart) {
// Calculate swipe direction and magnitude
var deltaX = x - swipeStartX;
var deltaY = y - swipeStartY;
var swipeMagnitude = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
// Only register as a swipe if the magnitude is significant
if (swipeMagnitude > 30) {
// Determine drift direction based on swipe direction
var swipeDirection = Math.atan2(deltaY, deltaX);
// Simplified direction determination (left or right)
var driftDir = 0;
// Compare swipe angle to car direction to determine if it's a left or right drift
var angleDiff = swipeDirection - car.direction;
// Normalize angle to -PI to PI
while (angleDiff > Math.PI) {
angleDiff -= Math.PI * 2;
}
while (angleDiff < -Math.PI) {
angleDiff += Math.PI * 2;
}
if (angleDiff > 0) {
driftDir = 1; // Right drift
} else {
driftDir = -1; // Left drift
}
// Start drifting in the determined direction
car.startDrift(driftDir);
// Reset drift time tracking
lastDriftTime = Date.now();
}
}
};
// Main game loop
game.update = function () {
if (gameState === 'playing') {
// Update car physics
car.update();
// Check collisions
var collision = track.checkCollisions(car);
if (collision) {
if (collision.type === 'border') {
// Hit a wall - game over
gameOver();
} else if (collision.type === 'checkpoint' && !collision.element.passed) {
// Passed checkpoint
handleCheckpoint();
}
// Corner collisions are handled in the drift scoring
}
// Handle drift scoring
handleDriftPoints();
// Update score display
updateScoreText();
}
};