/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var AIKart = Container.expand(function () { var self = Container.call(this); var kartGraphics = self.attachAsset('kart', { anchorX: 0.5, anchorY: 0.5 }); // AI specific properties self.speed = 8; self.currentLane = Math.floor(Math.random() * 4); self.targetLane = self.currentLane; self.decisionTimer = 0; self.lastDecisionTime = 0; self.aggressiveness = 0.5 + Math.random() * 0.5; // 0.5 to 1.0 self.score = 0; self.isShielded = false; self.shieldTime = 0; // Color the AI kart differently kartGraphics.tint = 0x0088ff + Math.floor(Math.random() * 0x888888); self.update = function () { // Update shield effect if (self.isShielded) { self.shieldTime--; if (self.shieldTime <= 0) { self.isShielded = false; kartGraphics.tint = 0x0088ff + Math.floor(Math.random() * 0x888888); } } // AI decision making self.decisionTimer++; if (self.decisionTimer >= 30 + Math.random() * 30) { // Make decisions every 0.5-1 second self.makeDecision(); self.decisionTimer = 0; } // Smooth movement to target lane var targetX = lanePositions[self.targetLane]; var deltaX = targetX - self.x; if (Math.abs(deltaX) > 5) { self.x += deltaX * 0.12; } else { self.x = targetX; self.currentLane = self.targetLane; } }; self.makeDecision = function () { var bestLane = self.currentLane; var bestScore = -1000; // Evaluate each lane for (var lane = 0; lane < 4; lane++) { var laneScore = self.evaluateLane(lane); if (laneScore > bestScore) { bestScore = laneScore; bestLane = lane; } } // Only change lanes if significantly better or randomly if (bestScore > self.evaluateLane(self.currentLane) + 20 || Math.random() < 0.1) { self.targetLane = bestLane; } }; self.evaluateLane = function (lane) { var score = 0; var laneX = lanePositions[lane]; var checkDistance = 800; // Look ahead distance // Check for butter in this lane for (var i = 0; i < butterItems.length; i++) { var butter = butterItems[i]; if (!butter.collected && Math.abs(butter.x - laneX) < 50) { var distance = butter.y - self.y; if (distance > 0 && distance < checkDistance) { score += Math.max(100 - distance / 4, 20); // Closer butter = higher score } } } // Check for obstacles in this lane for (var i = 0; i < obstacles.length; i++) { var obstacle = obstacles[i]; if (Math.abs(obstacle.x - laneX) < 50) { var distance = obstacle.y - self.y; if (distance > 0 && distance < checkDistance) { score -= Math.max(150 - distance / 3, 50); // Closer obstacles = bigger penalty } } } // Check for speed boosts in this lane for (var i = 0; i < speedBoosts.length; i++) { var boost = speedBoosts[i]; if (!boost.collected && Math.abs(boost.x - laneX) < 50) { var distance = boost.y - self.y; if (distance > 0 && distance < checkDistance) { score += Math.max(80 - distance / 5, 30); } } } // Prefer staying in current lane (reduce unnecessary movements) if (lane == self.currentLane) { score += 10; } // Add some randomness based on aggressiveness score += (Math.random() - 0.5) * self.aggressiveness * 40; return score; }; self.activateShield = function () { self.isShielded = true; self.shieldTime = 180; kartGraphics.tint = 0x00ffff; }; return self; }); var Butter = Container.expand(function () { var self = Container.call(this); var butterGraphics = self.attachAsset('butter', { anchorX: 0.5, anchorY: 0.5 }); self.collected = false; self.update = function () { self.y += trackSpeed; // Simple rotation animation butterGraphics.rotation += 0.1; }; return self; }); var FinishLine = Container.expand(function () { var self = Container.call(this); var finishGraphics = self.attachAsset('finishLine', { anchorX: 0.5, anchorY: 0.5 }); // Add checkered pattern with flags for (var i = 0; i < 5; i++) { var flag = self.attachAsset('finishFlag', { anchorX: 0.5, anchorY: 0.5, x: i * 400 - 800, y: -60 }); // Alternate colors for checkered effect if (i % 2 === 0) { flag.tint = 0x000000; } } self.crossed = false; self.update = function () { self.y += trackSpeed; // Animate flags waving for (var i = 0; i < self.children.length; i++) { if (i > 0) { // Skip the main finish line var flag = self.children[i]; flag.rotation = Math.sin(LK.ticks * 0.1 + i) * 0.2; } } }; return self; }); var Kart = Container.expand(function () { var self = Container.call(this); var kartGraphics = self.attachAsset('kart', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.maxSpeed = 12; self.steerSpeed = 6; self.isShielded = false; self.shieldTime = 0; self.update = function () { // Update shield effect if (self.isShielded) { self.shieldTime--; if (self.shieldTime <= 0) { self.isShielded = false; kartGraphics.tint = 0xffffff; } } }; self.activateShield = function () { self.isShielded = true; self.shieldTime = 180; // 3 seconds at 60fps kartGraphics.tint = 0x00ffff; }; return self; }); var Obstacle = Container.expand(function () { var self = Container.call(this); var obstacleGraphics = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { self.y += trackSpeed; }; return self; }); var SpeedBoost = Container.expand(function () { var self = Container.call(this); var boostGraphics = self.attachAsset('speedBoost', { anchorX: 0.5, anchorY: 0.5 }); self.collected = false; self.update = function () { self.y += trackSpeed; // Pulsing animation var scale = 1 + Math.sin(LK.ticks * 0.2) * 0.2; boostGraphics.scaleX = scale; boostGraphics.scaleY = scale; }; return self; }); var TrackSegment = Container.expand(function () { var self = Container.call(this); var trackGraphics = self.attachAsset('track', { anchorX: 0.5, anchorY: 0.5 }); // Add lane dividers for (var i = 1; i < 4; i++) { var line = self.attachAsset('trackLine', { anchorX: 0.5, anchorY: 0.5, x: i * 512 - 768, // Position lines to create 4 lanes y: 0 }); } self.update = function () { self.y += trackSpeed; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x228b22 }); /**** * Game Code ****/ // Game variables var labubu; var aiPlayers = []; var trackSpeed = 6; var butterItems = []; var obstacles = []; var speedBoosts = []; var trackSegments = []; var finishLine = null; var finishLineSpawned = false; var gameDistance = 0; var targetDistance = 5000; // Distance to finish line var isGameActive = true; var spawnTimer = 0; var trackTimer = 0; // Lane positions (4 lanes) var lanePositions = [384, 768, 1280, 1664]; var currentLane = 1; // Start in second lane // UI Elements var scoreTxt = new Text2('Butter: 0', { size: 80, fill: 0xFFFFFF }); scoreTxt.anchor.set(0, 0); LK.gui.topLeft.addChild(scoreTxt); scoreTxt.x = 120; // Avoid platform menu var distanceTxt = new Text2('Distance: 0m', { size: 60, fill: 0xFFFFFF }); distanceTxt.anchor.set(1, 0); LK.gui.topRight.addChild(distanceTxt); var speedTxt = new Text2('SPEED BOOST!', { size: 100, fill: 0x00FF00 }); speedTxt.anchor.set(0.5, 0.5); speedTxt.alpha = 0; LK.gui.center.addChild(speedTxt); // Initialize player kart labubu = game.addChild(new Kart()); labubu.x = lanePositions[currentLane]; labubu.y = 2200; // Initialize AI players for (var i = 0; i < 3; i++) { var aiKart = game.addChild(new AIKart()); aiKart.x = lanePositions[i == 0 ? 0 : i == 1 ? 2 : 3]; // Spread them across lanes aiKart.y = 2200 + (i + 1) * 150; // Stagger starting positions aiPlayers.push(aiKart); } // Initialize track segments for (var i = 0; i < 5; i++) { var segment = game.addChild(new TrackSegment()); segment.x = 1024; segment.y = i * 200 - 400; trackSegments.push(segment); } // Touch controls var touchStartX = 0; var isTouching = false; game.down = function (x, y, obj) { isTouching = true; touchStartX = x; }; game.up = function (x, y, obj) { isTouching = false; }; game.move = function (x, y, obj) { if (isTouching && isGameActive) { var deltaX = x - touchStartX; // Steer left or right based on touch movement if (Math.abs(deltaX) > 100) { if (deltaX > 0 && currentLane < 3) { currentLane++; touchStartX = x; } else if (deltaX < 0 && currentLane > 0) { currentLane--; touchStartX = x; } } } }; // Simple tap controls for lane switching game.down = function (x, y, obj) { if (!isGameActive) return; if (x < 1024 && currentLane > 0) { // Left side tap - move left currentLane--; } else if (x >= 1024 && currentLane < 3) { // Right side tap - move right currentLane++; } }; function spawnButter() { var butter = game.addChild(new Butter()); var lane = Math.floor(Math.random() * 4); butter.x = lanePositions[lane]; butter.y = -50; butterItems.push(butter); } function spawnObstacle() { var obstacle = game.addChild(new Obstacle()); var lane = Math.floor(Math.random() * 4); obstacle.x = lanePositions[lane]; obstacle.y = -100; obstacles.push(obstacle); } function spawnSpeedBoost() { var boost = game.addChild(new SpeedBoost()); var lane = Math.floor(Math.random() * 4); boost.x = lanePositions[lane]; boost.y = -80; speedBoosts.push(boost); } function spawnTrackSegment() { var segment = game.addChild(new TrackSegment()); segment.x = 1024; segment.y = -100; trackSegments.push(segment); } game.update = function () { if (!isGameActive) return; // Update distance gameDistance += trackSpeed; distanceTxt.setText('Distance: ' + Math.floor(gameDistance / 10) + 'm'); // Win condition now handled by finish line crossing // Smooth kart movement to target lane var targetX = lanePositions[currentLane]; var deltaX = targetX - labubu.x; if (Math.abs(deltaX) > 5) { labubu.x += deltaX * 0.15; } else { labubu.x = targetX; } // Increase difficulty over time if (gameDistance > 1000) trackSpeed = 7; if (gameDistance > 2500) trackSpeed = 8; if (gameDistance > 4000) trackSpeed = 9; // Spawn items spawnTimer++; if (spawnTimer % 90 == 0) spawnButter(); // Every 1.5 seconds if (spawnTimer % 120 == 0) spawnObstacle(); // Every 2 seconds if (spawnTimer % 300 == 0) spawnSpeedBoost(); // Every 5 seconds // Spawn track segments trackTimer++; if (trackTimer % 60 == 0) spawnTrackSegment(); // Every second // Spawn finish line when approaching target distance if (!finishLineSpawned && gameDistance >= targetDistance - 500) { finishLine = game.addChild(new FinishLine()); finishLine.x = 1024; finishLine.y = -100; finishLineSpawned = true; } // Update and check butter collection for (var i = butterItems.length - 1; i >= 0; i--) { var butter = butterItems[i]; if (butter.y > 2800) { butter.destroy(); butterItems.splice(i, 1); continue; } if (!butter.collected && butter.intersects(labubu)) { butter.collected = true; LK.setScore(LK.getScore() + 10); scoreTxt.setText('Butter: ' + LK.getScore()); LK.getSound('collect').play(); // Butter collection effect tween(butter, { scaleX: 2, scaleY: 2, alpha: 0 }, { duration: 300, onFinish: function onFinish() { butter.destroy(); } }); butterItems.splice(i, 1); continue; } // Check AI players for butter collection for (var j = 0; j < aiPlayers.length; j++) { var aiKart = aiPlayers[j]; if (!butter.collected && butter.intersects(aiKart)) { butter.collected = true; aiKart.score += 10; // AI butter collection effect (no sound to avoid spam) tween(butter, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 200, onFinish: function onFinish() { butter.destroy(); } }); butterItems.splice(i, 1); break; } } } // Update and check obstacle collisions for (var i = obstacles.length - 1; i >= 0; i--) { var obstacle = obstacles[i]; if (obstacle.y > 2800) { obstacle.destroy(); obstacles.splice(i, 1); continue; } var collisionDetected = false; if (obstacle.intersects(labubu)) { if (!labubu.isShielded) { LK.getSound('crash').play(); LK.effects.flashScreen(0xff0000, 500); trackSpeed = Math.max(3, trackSpeed - 2); // Slow down // Crash effect tween(labubu, {}, { duration: 500, onFinish: function onFinish() { trackSpeed = Math.min(trackSpeed + 2, 9); // Recover speed } }); } collisionDetected = true; } // Check AI players for obstacle collisions for (var j = 0; j < aiPlayers.length; j++) { var aiKart = aiPlayers[j]; if (obstacle.intersects(aiKart)) { if (!aiKart.isShielded) { // AI slows down slightly when hitting obstacles aiKart.score = Math.max(0, aiKart.score - 5); } collisionDetected = true; break; } } if (collisionDetected) { obstacle.destroy(); obstacles.splice(i, 1); } } // Update and check speed boost collection for (var i = speedBoosts.length - 1; i >= 0; i--) { var boost = speedBoosts[i]; if (boost.y > 2800) { boost.destroy(); speedBoosts.splice(i, 1); continue; } if (!boost.collected && boost.intersects(labubu)) { boost.collected = true; LK.getSound('boost').play(); labubu.activateShield(); trackSpeed += 3; // Show speed boost text speedTxt.alpha = 1; tween(speedTxt, { alpha: 0 }, { duration: 1000 }); // Return to normal speed after boost LK.setTimeout(function () { trackSpeed = Math.max(6, trackSpeed - 3); }, 2000); boost.destroy(); speedBoosts.splice(i, 1); continue; } // Check AI players for speed boost collection for (var j = 0; j < aiPlayers.length; j++) { var aiKart = aiPlayers[j]; if (!boost.collected && boost.intersects(aiKart)) { boost.collected = true; aiKart.activateShield(); aiKart.score += 15; // Bonus points for speed boost boost.destroy(); speedBoosts.splice(i, 1); break; } } } // Clean up track segments for (var i = trackSegments.length - 1; i >= 0; i--) { var segment = trackSegments[i]; if (segment.y > 2800) { segment.destroy(); trackSegments.splice(i, 1); } } // Check finish line crossing if (finishLine && !finishLine.crossed) { // Check if player crosses finish line if (finishLine.intersects(labubu)) { finishLine.crossed = true; isGameActive = false; LK.showYouWin(); return; } // Check if any AI crosses finish line first for (var i = 0; i < aiPlayers.length; i++) { var aiKart = aiPlayers[i]; if (finishLine.intersects(aiKart)) { finishLine.crossed = true; isGameActive = false; LK.showGameOver(); return; } } } // Clean up finish line if it goes off screen if (finishLine && finishLine.y > 2800) { finishLine.destroy(); finishLine = null; } // Check if any AI player has won by reaching the target distance first // or having significantly more points for (var i = 0; i < aiPlayers.length; i++) { var aiKart = aiPlayers[i]; if (aiKart.score > LK.getScore() + 100 && gameDistance > targetDistance * 0.8) { // AI wins if they have 100+ more points and we're near the end isGameActive = false; LK.showGameOver(); return; } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var AIKart = Container.expand(function () {
var self = Container.call(this);
var kartGraphics = self.attachAsset('kart', {
anchorX: 0.5,
anchorY: 0.5
});
// AI specific properties
self.speed = 8;
self.currentLane = Math.floor(Math.random() * 4);
self.targetLane = self.currentLane;
self.decisionTimer = 0;
self.lastDecisionTime = 0;
self.aggressiveness = 0.5 + Math.random() * 0.5; // 0.5 to 1.0
self.score = 0;
self.isShielded = false;
self.shieldTime = 0;
// Color the AI kart differently
kartGraphics.tint = 0x0088ff + Math.floor(Math.random() * 0x888888);
self.update = function () {
// Update shield effect
if (self.isShielded) {
self.shieldTime--;
if (self.shieldTime <= 0) {
self.isShielded = false;
kartGraphics.tint = 0x0088ff + Math.floor(Math.random() * 0x888888);
}
}
// AI decision making
self.decisionTimer++;
if (self.decisionTimer >= 30 + Math.random() * 30) {
// Make decisions every 0.5-1 second
self.makeDecision();
self.decisionTimer = 0;
}
// Smooth movement to target lane
var targetX = lanePositions[self.targetLane];
var deltaX = targetX - self.x;
if (Math.abs(deltaX) > 5) {
self.x += deltaX * 0.12;
} else {
self.x = targetX;
self.currentLane = self.targetLane;
}
};
self.makeDecision = function () {
var bestLane = self.currentLane;
var bestScore = -1000;
// Evaluate each lane
for (var lane = 0; lane < 4; lane++) {
var laneScore = self.evaluateLane(lane);
if (laneScore > bestScore) {
bestScore = laneScore;
bestLane = lane;
}
}
// Only change lanes if significantly better or randomly
if (bestScore > self.evaluateLane(self.currentLane) + 20 || Math.random() < 0.1) {
self.targetLane = bestLane;
}
};
self.evaluateLane = function (lane) {
var score = 0;
var laneX = lanePositions[lane];
var checkDistance = 800; // Look ahead distance
// Check for butter in this lane
for (var i = 0; i < butterItems.length; i++) {
var butter = butterItems[i];
if (!butter.collected && Math.abs(butter.x - laneX) < 50) {
var distance = butter.y - self.y;
if (distance > 0 && distance < checkDistance) {
score += Math.max(100 - distance / 4, 20); // Closer butter = higher score
}
}
}
// Check for obstacles in this lane
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (Math.abs(obstacle.x - laneX) < 50) {
var distance = obstacle.y - self.y;
if (distance > 0 && distance < checkDistance) {
score -= Math.max(150 - distance / 3, 50); // Closer obstacles = bigger penalty
}
}
}
// Check for speed boosts in this lane
for (var i = 0; i < speedBoosts.length; i++) {
var boost = speedBoosts[i];
if (!boost.collected && Math.abs(boost.x - laneX) < 50) {
var distance = boost.y - self.y;
if (distance > 0 && distance < checkDistance) {
score += Math.max(80 - distance / 5, 30);
}
}
}
// Prefer staying in current lane (reduce unnecessary movements)
if (lane == self.currentLane) {
score += 10;
}
// Add some randomness based on aggressiveness
score += (Math.random() - 0.5) * self.aggressiveness * 40;
return score;
};
self.activateShield = function () {
self.isShielded = true;
self.shieldTime = 180;
kartGraphics.tint = 0x00ffff;
};
return self;
});
var Butter = Container.expand(function () {
var self = Container.call(this);
var butterGraphics = self.attachAsset('butter', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
self.update = function () {
self.y += trackSpeed;
// Simple rotation animation
butterGraphics.rotation += 0.1;
};
return self;
});
var FinishLine = Container.expand(function () {
var self = Container.call(this);
var finishGraphics = self.attachAsset('finishLine', {
anchorX: 0.5,
anchorY: 0.5
});
// Add checkered pattern with flags
for (var i = 0; i < 5; i++) {
var flag = self.attachAsset('finishFlag', {
anchorX: 0.5,
anchorY: 0.5,
x: i * 400 - 800,
y: -60
});
// Alternate colors for checkered effect
if (i % 2 === 0) {
flag.tint = 0x000000;
}
}
self.crossed = false;
self.update = function () {
self.y += trackSpeed;
// Animate flags waving
for (var i = 0; i < self.children.length; i++) {
if (i > 0) {
// Skip the main finish line
var flag = self.children[i];
flag.rotation = Math.sin(LK.ticks * 0.1 + i) * 0.2;
}
}
};
return self;
});
var Kart = Container.expand(function () {
var self = Container.call(this);
var kartGraphics = self.attachAsset('kart', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.maxSpeed = 12;
self.steerSpeed = 6;
self.isShielded = false;
self.shieldTime = 0;
self.update = function () {
// Update shield effect
if (self.isShielded) {
self.shieldTime--;
if (self.shieldTime <= 0) {
self.isShielded = false;
kartGraphics.tint = 0xffffff;
}
}
};
self.activateShield = function () {
self.isShielded = true;
self.shieldTime = 180; // 3 seconds at 60fps
kartGraphics.tint = 0x00ffff;
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
self.y += trackSpeed;
};
return self;
});
var SpeedBoost = Container.expand(function () {
var self = Container.call(this);
var boostGraphics = self.attachAsset('speedBoost', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
self.update = function () {
self.y += trackSpeed;
// Pulsing animation
var scale = 1 + Math.sin(LK.ticks * 0.2) * 0.2;
boostGraphics.scaleX = scale;
boostGraphics.scaleY = scale;
};
return self;
});
var TrackSegment = Container.expand(function () {
var self = Container.call(this);
var trackGraphics = self.attachAsset('track', {
anchorX: 0.5,
anchorY: 0.5
});
// Add lane dividers
for (var i = 1; i < 4; i++) {
var line = self.attachAsset('trackLine', {
anchorX: 0.5,
anchorY: 0.5,
x: i * 512 - 768,
// Position lines to create 4 lanes
y: 0
});
}
self.update = function () {
self.y += trackSpeed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228b22
});
/****
* Game Code
****/
// Game variables
var labubu;
var aiPlayers = [];
var trackSpeed = 6;
var butterItems = [];
var obstacles = [];
var speedBoosts = [];
var trackSegments = [];
var finishLine = null;
var finishLineSpawned = false;
var gameDistance = 0;
var targetDistance = 5000; // Distance to finish line
var isGameActive = true;
var spawnTimer = 0;
var trackTimer = 0;
// Lane positions (4 lanes)
var lanePositions = [384, 768, 1280, 1664];
var currentLane = 1; // Start in second lane
// UI Elements
var scoreTxt = new Text2('Butter: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(scoreTxt);
scoreTxt.x = 120; // Avoid platform menu
var distanceTxt = new Text2('Distance: 0m', {
size: 60,
fill: 0xFFFFFF
});
distanceTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(distanceTxt);
var speedTxt = new Text2('SPEED BOOST!', {
size: 100,
fill: 0x00FF00
});
speedTxt.anchor.set(0.5, 0.5);
speedTxt.alpha = 0;
LK.gui.center.addChild(speedTxt);
// Initialize player kart
labubu = game.addChild(new Kart());
labubu.x = lanePositions[currentLane];
labubu.y = 2200;
// Initialize AI players
for (var i = 0; i < 3; i++) {
var aiKart = game.addChild(new AIKart());
aiKart.x = lanePositions[i == 0 ? 0 : i == 1 ? 2 : 3]; // Spread them across lanes
aiKart.y = 2200 + (i + 1) * 150; // Stagger starting positions
aiPlayers.push(aiKart);
}
// Initialize track segments
for (var i = 0; i < 5; i++) {
var segment = game.addChild(new TrackSegment());
segment.x = 1024;
segment.y = i * 200 - 400;
trackSegments.push(segment);
}
// Touch controls
var touchStartX = 0;
var isTouching = false;
game.down = function (x, y, obj) {
isTouching = true;
touchStartX = x;
};
game.up = function (x, y, obj) {
isTouching = false;
};
game.move = function (x, y, obj) {
if (isTouching && isGameActive) {
var deltaX = x - touchStartX;
// Steer left or right based on touch movement
if (Math.abs(deltaX) > 100) {
if (deltaX > 0 && currentLane < 3) {
currentLane++;
touchStartX = x;
} else if (deltaX < 0 && currentLane > 0) {
currentLane--;
touchStartX = x;
}
}
}
};
// Simple tap controls for lane switching
game.down = function (x, y, obj) {
if (!isGameActive) return;
if (x < 1024 && currentLane > 0) {
// Left side tap - move left
currentLane--;
} else if (x >= 1024 && currentLane < 3) {
// Right side tap - move right
currentLane++;
}
};
function spawnButter() {
var butter = game.addChild(new Butter());
var lane = Math.floor(Math.random() * 4);
butter.x = lanePositions[lane];
butter.y = -50;
butterItems.push(butter);
}
function spawnObstacle() {
var obstacle = game.addChild(new Obstacle());
var lane = Math.floor(Math.random() * 4);
obstacle.x = lanePositions[lane];
obstacle.y = -100;
obstacles.push(obstacle);
}
function spawnSpeedBoost() {
var boost = game.addChild(new SpeedBoost());
var lane = Math.floor(Math.random() * 4);
boost.x = lanePositions[lane];
boost.y = -80;
speedBoosts.push(boost);
}
function spawnTrackSegment() {
var segment = game.addChild(new TrackSegment());
segment.x = 1024;
segment.y = -100;
trackSegments.push(segment);
}
game.update = function () {
if (!isGameActive) return;
// Update distance
gameDistance += trackSpeed;
distanceTxt.setText('Distance: ' + Math.floor(gameDistance / 10) + 'm');
// Win condition now handled by finish line crossing
// Smooth kart movement to target lane
var targetX = lanePositions[currentLane];
var deltaX = targetX - labubu.x;
if (Math.abs(deltaX) > 5) {
labubu.x += deltaX * 0.15;
} else {
labubu.x = targetX;
}
// Increase difficulty over time
if (gameDistance > 1000) trackSpeed = 7;
if (gameDistance > 2500) trackSpeed = 8;
if (gameDistance > 4000) trackSpeed = 9;
// Spawn items
spawnTimer++;
if (spawnTimer % 90 == 0) spawnButter(); // Every 1.5 seconds
if (spawnTimer % 120 == 0) spawnObstacle(); // Every 2 seconds
if (spawnTimer % 300 == 0) spawnSpeedBoost(); // Every 5 seconds
// Spawn track segments
trackTimer++;
if (trackTimer % 60 == 0) spawnTrackSegment(); // Every second
// Spawn finish line when approaching target distance
if (!finishLineSpawned && gameDistance >= targetDistance - 500) {
finishLine = game.addChild(new FinishLine());
finishLine.x = 1024;
finishLine.y = -100;
finishLineSpawned = true;
}
// Update and check butter collection
for (var i = butterItems.length - 1; i >= 0; i--) {
var butter = butterItems[i];
if (butter.y > 2800) {
butter.destroy();
butterItems.splice(i, 1);
continue;
}
if (!butter.collected && butter.intersects(labubu)) {
butter.collected = true;
LK.setScore(LK.getScore() + 10);
scoreTxt.setText('Butter: ' + LK.getScore());
LK.getSound('collect').play();
// Butter collection effect
tween(butter, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
butter.destroy();
}
});
butterItems.splice(i, 1);
continue;
}
// Check AI players for butter collection
for (var j = 0; j < aiPlayers.length; j++) {
var aiKart = aiPlayers[j];
if (!butter.collected && butter.intersects(aiKart)) {
butter.collected = true;
aiKart.score += 10;
// AI butter collection effect (no sound to avoid spam)
tween(butter, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
butter.destroy();
}
});
butterItems.splice(i, 1);
break;
}
}
}
// Update and check obstacle collisions
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
if (obstacle.y > 2800) {
obstacle.destroy();
obstacles.splice(i, 1);
continue;
}
var collisionDetected = false;
if (obstacle.intersects(labubu)) {
if (!labubu.isShielded) {
LK.getSound('crash').play();
LK.effects.flashScreen(0xff0000, 500);
trackSpeed = Math.max(3, trackSpeed - 2); // Slow down
// Crash effect
tween(labubu, {}, {
duration: 500,
onFinish: function onFinish() {
trackSpeed = Math.min(trackSpeed + 2, 9); // Recover speed
}
});
}
collisionDetected = true;
}
// Check AI players for obstacle collisions
for (var j = 0; j < aiPlayers.length; j++) {
var aiKart = aiPlayers[j];
if (obstacle.intersects(aiKart)) {
if (!aiKart.isShielded) {
// AI slows down slightly when hitting obstacles
aiKart.score = Math.max(0, aiKart.score - 5);
}
collisionDetected = true;
break;
}
}
if (collisionDetected) {
obstacle.destroy();
obstacles.splice(i, 1);
}
}
// Update and check speed boost collection
for (var i = speedBoosts.length - 1; i >= 0; i--) {
var boost = speedBoosts[i];
if (boost.y > 2800) {
boost.destroy();
speedBoosts.splice(i, 1);
continue;
}
if (!boost.collected && boost.intersects(labubu)) {
boost.collected = true;
LK.getSound('boost').play();
labubu.activateShield();
trackSpeed += 3;
// Show speed boost text
speedTxt.alpha = 1;
tween(speedTxt, {
alpha: 0
}, {
duration: 1000
});
// Return to normal speed after boost
LK.setTimeout(function () {
trackSpeed = Math.max(6, trackSpeed - 3);
}, 2000);
boost.destroy();
speedBoosts.splice(i, 1);
continue;
}
// Check AI players for speed boost collection
for (var j = 0; j < aiPlayers.length; j++) {
var aiKart = aiPlayers[j];
if (!boost.collected && boost.intersects(aiKart)) {
boost.collected = true;
aiKart.activateShield();
aiKart.score += 15; // Bonus points for speed boost
boost.destroy();
speedBoosts.splice(i, 1);
break;
}
}
}
// Clean up track segments
for (var i = trackSegments.length - 1; i >= 0; i--) {
var segment = trackSegments[i];
if (segment.y > 2800) {
segment.destroy();
trackSegments.splice(i, 1);
}
}
// Check finish line crossing
if (finishLine && !finishLine.crossed) {
// Check if player crosses finish line
if (finishLine.intersects(labubu)) {
finishLine.crossed = true;
isGameActive = false;
LK.showYouWin();
return;
}
// Check if any AI crosses finish line first
for (var i = 0; i < aiPlayers.length; i++) {
var aiKart = aiPlayers[i];
if (finishLine.intersects(aiKart)) {
finishLine.crossed = true;
isGameActive = false;
LK.showGameOver();
return;
}
}
}
// Clean up finish line if it goes off screen
if (finishLine && finishLine.y > 2800) {
finishLine.destroy();
finishLine = null;
}
// Check if any AI player has won by reaching the target distance first
// or having significantly more points
for (var i = 0; i < aiPlayers.length; i++) {
var aiKart = aiPlayers[i];
if (aiKart.score > LK.getScore() + 100 && gameDistance > targetDistance * 0.8) {
// AI wins if they have 100+ more points and we're near the end
isGameActive = false;
LK.showGameOver();
return;
}
}
};