/**** * 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; } } };
===================================================================
--- original.js
+++ change.js
@@ -128,8 +128,41 @@
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,
@@ -223,8 +256,10 @@
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;
@@ -338,14 +373,9 @@
if (!isGameActive) return;
// Update distance
gameDistance += trackSpeed;
distanceTxt.setText('Distance: ' + Math.floor(gameDistance / 10) + 'm');
- // Check win condition
- if (gameDistance >= targetDistance) {
- isGameActive = false;
- LK.showYouWin();
- return;
- }
+ // 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) {
@@ -364,8 +394,15 @@
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) {
@@ -503,8 +540,33 @@
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];