/****
* 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 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 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');
// Check win condition
if (gameDistance >= targetDistance) {
isGameActive = false;
LK.showYouWin();
return;
}
// 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
// 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 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
@@ -5,8 +5,117 @@
/****
* 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,
@@ -108,8 +217,9 @@
* Game Code
****/
// Game variables
var labubu;
+var aiPlayers = [];
var trackSpeed = 6;
var butterItems = [];
var obstacles = [];
var speedBoosts = [];
@@ -146,8 +256,15 @@
// 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;
@@ -272,9 +389,31 @@
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];
@@ -282,8 +421,9 @@
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);
@@ -295,8 +435,23 @@
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);
}
}
@@ -325,9 +480,22 @@
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];
@@ -335,5 +503,16 @@
segment.destroy();
trackSegments.splice(i, 1);
}
}
+ // 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;
+ }
+ }
};
\ No newline at end of file