/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Button = Container.expand(function (text, callback) {
var self = Container.call(this);
self.callback = callback;
var bg = self.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
var label = new Text2(text, {
size: 40,
fill: 0xFFFFFF
});
label.anchor.set(0.5, 0.5);
self.addChild(label);
self.down = function () {
LK.getSound('select').play();
if (self.callback) {
self.callback();
}
};
return self;
});
var Character = Container.expand(function (characterType) {
var self = Container.call(this);
self.characterType = characterType || 'freddy';
self.speed = 0;
self.maxSpeed = 8;
self.acceleration = 0.3;
self.deceleration = 0.2;
self.turnSpeed = 0.1;
self.direction = 0;
self.isUnlocked = true;
// Set unlock status
if (self.characterType === 'endo') {
self.isUnlocked = storage.endoUnlocked || false;
} else if (self.characterType === 'golden_freddy') {
self.isUnlocked = storage.goldenFreddyUnlocked || false;
}
var sprite = self.attachAsset(self.characterType, {
anchorX: 0.5,
anchorY: 0.5
});
self.moveForward = function () {
if (self.speed < self.maxSpeed) {
self.speed += self.acceleration;
}
};
self.moveBackward = function () {
if (self.speed > -self.maxSpeed * 0.5) {
self.speed -= self.acceleration;
}
};
self.turnLeft = function () {
self.direction -= self.turnSpeed;
sprite.rotation = self.direction;
};
self.turnRight = function () {
self.direction += self.turnSpeed;
sprite.rotation = self.direction;
};
self.applyDeceleration = function () {
if (self.speed > 0) {
self.speed -= self.deceleration;
if (self.speed < 0) self.speed = 0;
} else if (self.speed < 0) {
self.speed += self.deceleration;
if (self.speed > 0) self.speed = 0;
}
};
self.update = function () {
// Move based on current speed and direction
self.x += Math.sin(self.direction) * self.speed;
self.y -= Math.cos(self.direction) * self.speed;
// Keep within track bounds - adjust based on selected track
var minX = 100;
var maxX = 1948;
if (selectedTrack === 1) {
// Forest track - dynamic bounds based on Y position
var ySection = Math.floor(self.y / 200);
if (ySection >= 0 && ySection <= 12) {
var forestBounds = [{
minX: 100,
maxX: 1948
}, {
minX: 100,
maxX: 1948
}, {
minX: 100,
maxX: 1948
}, {
minX: 100,
maxX: 1948
}, {
minX: 300,
maxX: 1748
}, {
minX: 400,
maxX: 1648
}, {
minX: 350,
maxX: 1698
}, {
minX: 250,
maxX: 1798
}, {
minX: 150,
maxX: 1898
}, {
minX: 100,
maxX: 1948
}, {
minX: 150,
maxX: 1898
}, {
minX: 200,
maxX: 1848
}, {
minX: 250,
maxX: 1798
}];
if (forestBounds[ySection]) {
minX = forestBounds[ySection].minX;
maxX = forestBounds[ySection].maxX;
}
}
} else if (selectedTrack === 2) {
// Desert track - canyon bounds
var ySection = Math.floor(self.y / 200);
if (ySection >= 0 && ySection <= 12) {
var desertBounds = [{
minX: 300,
maxX: 1748
}, {
minX: 250,
maxX: 1798
}, {
minX: 200,
maxX: 1848
}, {
minX: 150,
maxX: 1898
}, {
minX: 100,
maxX: 1948
}, {
minX: 150,
maxX: 1898
}, {
minX: 200,
maxX: 1848
}, {
minX: 300,
maxX: 1748
}, {
minX: 400,
maxX: 1648
}, {
minX: 500,
maxX: 1548
}, {
minX: 400,
maxX: 1648
}, {
minX: 300,
maxX: 1748
}, {
minX: 200,
maxX: 1848
}];
if (desertBounds[ySection]) {
minX = desertBounds[ySection].minX;
maxX = desertBounds[ySection].maxX;
}
}
} else if (selectedTrack === 3) {
// Night track - city bounds
var ySection = Math.floor(self.y / 200);
if (ySection >= 0 && ySection <= 12) {
var nightBounds = [{
minX: 150,
maxX: 1898
}, {
minX: 200,
maxX: 1848
}, {
minX: 350,
maxX: 1698
}, {
minX: 500,
maxX: 1548
}, {
minX: 400,
maxX: 1648
}, {
minX: 250,
maxX: 1798
}, {
minX: 150,
maxX: 1898
}, {
minX: 100,
maxX: 1948
}, {
minX: 200,
maxX: 1848
}, {
minX: 350,
maxX: 1698
}, {
minX: 450,
maxX: 1598
}, {
minX: 300,
maxX: 1748
}, {
minX: 150,
maxX: 1898
}];
if (nightBounds[ySection]) {
minX = nightBounds[ySection].minX;
maxX = nightBounds[ySection].maxX;
}
}
}
if (self.x < minX) self.x = minX;
if (self.x > maxX) self.x = maxX;
if (self.y < 100) self.y = 100;
if (self.y > 2632) self.y = 2632;
};
return self;
});
var CharacterCard = Container.expand(function (characterType) {
var self = Container.call(this);
self.characterType = characterType;
var bg = self.attachAsset('character_card', {
anchorX: 0.5,
anchorY: 0.5
});
var character = new Character(characterType);
character.x = 0;
character.y = -50;
self.addChild(character);
var nameText = new Text2(characterType.toUpperCase(), {
size: 24,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0.5);
nameText.y = 80;
self.addChild(nameText);
// Show locked status
if (!character.isUnlocked) {
bg.tint = 0x666666;
var lockText = new Text2("LOCKED", {
size: 20,
fill: 0xFF0000
});
lockText.anchor.set(0.5, 0.5);
lockText.y = 100;
self.addChild(lockText);
}
self.down = function () {
if (character.isUnlocked) {
LK.getSound('select').play();
selectedCharacter = self.characterType;
// Just select character, don't start race yet
}
};
return self;
});
var Obstacle = Container.expand(function (obstacleType, x, y) {
var self = Container.call(this);
self.obstacleType = obstacleType || 'endoskeleton';
self.x = x || 0;
self.y = y || 0;
var sprite = self.attachAsset(self.obstacleType, {
anchorX: 0.5,
anchorY: 0.5
});
// Add some rotation for visual variety
sprite.rotation = Math.random() * Math.PI * 2;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x001122
});
/****
* Game Code
****/
// Character sprites
// Track elements
// Obstacles
// UI elements
// Sounds
var gameState = 'menu'; // 'menu', 'character_select', 'racing', 'results', 'multiplayer_lobby', 'multiplayer_racing'
var selectedCharacter = 'freddy';
var selectedTrack = 1;
var player = null;
var otherPlayers = [];
var obstacles = [];
var lapTime = 0;
var startTime = 0;
var bestTime = storage.bestTime || 999999;
var raceDistance = 0;
var totalRaceDistance = 3000;
var isMultiplayer = false;
var playerNickname = storage.playerNickname || "Player";
var multiplayerRoom = null;
var playersFinished = [];
var playerLives = 3;
// Define multiplayer object to prevent undefined errors
var multiplayer = {
sendData: function sendData(data) {
// Mock function for when multiplayer is not available
console.log("Multiplayer not available - would send:", data);
},
getPlayerId: function getPlayerId() {
return 'player_' + Date.now();
},
createRoom: function createRoom(config, callback) {
console.log("Multiplayer not available - would create room");
if (callback) callback(null);
},
joinRoom: function joinRoom(config, callback) {
console.log("Multiplayer not available - would join room");
if (callback) callback(null);
},
on: function on(event, callback) {
console.log("Multiplayer not available - would listen for:", event);
},
leaveRoom: function leaveRoom() {
console.log("Multiplayer not available - would leave room");
}
};
// Initialize storage defaults
if (storage.gamesPlayed === undefined) storage.gamesPlayed = 0;
if (storage.wins === undefined) storage.wins = 0;
if (storage.endoUnlocked === undefined) storage.endoUnlocked = false;
if (storage.goldenFreddyUnlocked === undefined) storage.goldenFreddyUnlocked = false;
// UI Elements
var titleText = new Text2("FREDDY'S RACING\nCHAMPIONSHIP", {
size: 80,
fill: 0xFFD700
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
var timeText = new Text2("", {
size: 60,
fill: 0xFFFFFF
});
timeText.anchor.set(0.5, 0);
LK.gui.top.addChild(timeText);
var speedText = new Text2("", {
size: 40,
fill: 0x00FF00
});
speedText.anchor.set(0, 0);
LK.gui.bottomLeft.addChild(speedText);
var progressText = new Text2("", {
size: 40,
fill: 0xFFFF00
});
progressText.anchor.set(1, 0);
LK.gui.bottomRight.addChild(progressText);
var livesText = new Text2("", {
size: 40,
fill: 0xFF0000
});
livesText.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(livesText);
function showMenu() {
gameState = 'menu';
game.removeChildren();
// Add background image
var menuBg = LK.getAsset('menu_background', {
anchorX: 0.5,
anchorY: 0.5
});
menuBg.x = 1024;
menuBg.y = 1366;
game.addChild(menuBg);
// Add pulsing animation to background
tween(menuBg, {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(menuBg, {
scaleX: 1,
scaleY: 1
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Restart the animation loop
if (gameState === 'menu') {
tween(menuBg, {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: arguments.callee.caller
});
}
}
});
}
});
// Add subtle rotation animation
tween(menuBg, {
rotation: 0.02
}, {
duration: 4000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(menuBg, {
rotation: -0.02
}, {
duration: 4000,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (gameState === 'menu') {
tween(menuBg, {
rotation: 0.02
}, {
duration: 4000,
easing: tween.easeInOut,
onFinish: arguments.callee.caller
});
}
}
});
}
});
game.addChild(titleText);
// Add floating animation to title
tween(titleText, {
y: 380
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(titleText, {
y: 420
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (gameState === 'menu') {
tween(titleText, {
y: 380
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: arguments.callee.caller
});
}
}
});
}
});
// Add color tinting animation to title
tween(titleText, {
tint: 0xFFFF00
}, {
duration: 2500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(titleText, {
tint: 0xFFD700
}, {
duration: 2500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (gameState === 'menu') {
tween(titleText, {
tint: 0xFFFF00
}, {
duration: 2500,
easing: tween.easeInOut,
onFinish: arguments.callee.caller
});
}
}
});
}
});
var singlePlayerButton = new Button("SINGLE PLAYER", function () {
isMultiplayer = false;
showCharacterSelect();
});
singlePlayerButton.x = 1024;
singlePlayerButton.y = 700;
game.addChild(singlePlayerButton);
var multiplayerButton = new Button("MULTIPLAYER", function () {
isMultiplayer = true;
showMultiplayerLobby();
});
multiplayerButton.x = 1024;
multiplayerButton.y = 820;
game.addChild(multiplayerButton);
var statsButton = new Button("STATS", function () {
showStats();
});
statsButton.x = 1024;
statsButton.y = 940;
game.addChild(statsButton);
// Show best time
if (bestTime < 999999) {
var bestTimeText = new Text2("Best Time: " + (bestTime / 1000).toFixed(2) + "s", {
size: 40,
fill: 0x00FF00
});
bestTimeText.anchor.set(0.5, 0.5);
bestTimeText.x = 1024;
bestTimeText.y = 1200;
game.addChild(bestTimeText);
}
}
function showCharacterSelect() {
gameState = 'character_select';
game.removeChildren();
var selectText = new Text2("SELECT CHARACTER", {
size: 60,
fill: 0xFFD700
});
selectText.anchor.set(0.5, 0.5);
selectText.x = 1024;
selectText.y = 300;
game.addChild(selectText);
var characters = ['freddy', 'bonnie', 'chica', 'foxy', 'endo', 'golden_freddy'];
var startX = 300;
var spacing = 300;
for (var i = 0; i < characters.length; i++) {
var card = new CharacterCard(characters[i]);
card.x = startX + i % 3 * spacing;
card.y = 600 + Math.floor(i / 3) * 400;
game.addChild(card);
}
var nextButton = new Button("NEXT: SELECT TRACK", function () {
showTrackSelect();
});
nextButton.x = 1024;
nextButton.y = 1400;
game.addChild(nextButton);
var backButton = new Button("BACK", function () {
showMenu();
});
backButton.x = 200;
backButton.y = 200;
game.addChild(backButton);
}
function showTrackSelect() {
gameState = 'track_select';
game.removeChildren();
var selectText = new Text2("SELECT TRACK", {
size: 60,
fill: 0xFFD700
});
selectText.anchor.set(0.5, 0.5);
selectText.x = 1024;
selectText.y = 300;
game.addChild(selectText);
// Track 1 - Forest Track
var track1Bg = LK.getAsset('track_background_1', {
anchorX: 0.5,
anchorY: 0.5
});
track1Bg.x = 512;
track1Bg.y = 700;
track1Bg.scaleX = 0.4;
track1Bg.scaleY = 0.4;
game.addChild(track1Bg);
var track1Button = new Button("FOREST TRACK", function () {
selectedTrack = 1;
if (isMultiplayer) {
if (multiplayer && multiplayer.sendData) {
multiplayer.sendData({
type: 'characterSelected',
character: selectedCharacter,
nickname: playerNickname,
track: selectedTrack
});
}
startMultiplayerRace();
} else {
startRace();
}
});
track1Button.x = 512;
track1Button.y = 950;
game.addChild(track1Button);
var track1Text = new Text2("FOREST", {
size: 30,
fill: 0xFFFFFF
});
track1Text.anchor.set(0.5, 0.5);
track1Text.x = 512;
track1Text.y = 500;
game.addChild(track1Text);
// Track 2 - Desert Track
var track2Bg = LK.getAsset('track_background_2', {
anchorX: 0.5,
anchorY: 0.5
});
track2Bg.x = 1024;
track2Bg.y = 700;
track2Bg.scaleX = 0.4;
track2Bg.scaleY = 0.4;
game.addChild(track2Bg);
var track2Button = new Button("DESERT TRACK", function () {
selectedTrack = 2;
if (isMultiplayer) {
if (multiplayer && multiplayer.sendData) {
multiplayer.sendData({
type: 'characterSelected',
character: selectedCharacter,
nickname: playerNickname,
track: selectedTrack
});
}
startMultiplayerRace();
} else {
startRace();
}
});
track2Button.x = 1024;
track2Button.y = 950;
game.addChild(track2Button);
var track2Text = new Text2("DESERT", {
size: 30,
fill: 0xFFFFFF
});
track2Text.anchor.set(0.5, 0.5);
track2Text.x = 1024;
track2Text.y = 500;
game.addChild(track2Text);
// Track 3 - Night Track
var track3Bg = LK.getAsset('track_background_3', {
anchorX: 0.5,
anchorY: 0.5
});
track3Bg.x = 1536;
track3Bg.y = 700;
track3Bg.scaleX = 0.4;
track3Bg.scaleY = 0.4;
game.addChild(track3Bg);
var track3Button = new Button("NIGHT TRACK", function () {
selectedTrack = 3;
if (isMultiplayer) {
if (multiplayer && multiplayer.sendData) {
multiplayer.sendData({
type: 'characterSelected',
character: selectedCharacter,
nickname: playerNickname,
track: selectedTrack
});
}
startMultiplayerRace();
} else {
startRace();
}
});
track3Button.x = 1536;
track3Button.y = 950;
game.addChild(track3Button);
var track3Text = new Text2("NIGHT", {
size: 30,
fill: 0xFFFFFF
});
track3Text.anchor.set(0.5, 0.5);
track3Text.x = 1536;
track3Text.y = 500;
game.addChild(track3Text);
var backButton = new Button("BACK", function () {
showCharacterSelect();
});
backButton.x = 200;
backButton.y = 200;
game.addChild(backButton);
}
function showStats() {
game.removeChildren();
var statsText = new Text2("STATISTICS", {
size: 60,
fill: 0xFFD700
});
statsText.anchor.set(0.5, 0.5);
statsText.x = 1024;
statsText.y = 300;
game.addChild(statsText);
var gamesText = new Text2("Games Played: " + storage.gamesPlayed, {
size: 40,
fill: 0xFFFFFF
});
gamesText.anchor.set(0.5, 0.5);
gamesText.x = 1024;
gamesText.y = 500;
game.addChild(gamesText);
var winsText = new Text2("Wins: " + storage.wins, {
size: 40,
fill: 0xFFFFFF
});
winsText.anchor.set(0.5, 0.5);
winsText.x = 1024;
winsText.y = 600;
game.addChild(winsText);
var backButton = new Button("BACK", function () {
showMenu();
});
backButton.x = 1024;
backButton.y = 800;
game.addChild(backButton);
}
function showMultiplayerLobby() {
gameState = 'multiplayer_lobby';
game.removeChildren();
var lobbyText = new Text2("MULTIPLAYER LOBBY", {
size: 60,
fill: 0xFFD700
});
lobbyText.anchor.set(0.5, 0.5);
lobbyText.x = 1024;
lobbyText.y = 300;
game.addChild(lobbyText);
var nicknameText = new Text2("Nickname: " + playerNickname, {
size: 40,
fill: 0xFFFFFF
});
nicknameText.anchor.set(0.5, 0.5);
nicknameText.x = 1024;
nicknameText.y = 500;
game.addChild(nicknameText);
var joinRoomButton = new Button("JOIN ROOM", function () {
joinMultiplayerRoom();
});
joinRoomButton.x = 1024;
joinRoomButton.y = 700;
game.addChild(joinRoomButton);
var createRoomButton = new Button("CREATE ROOM", function () {
createMultiplayerRoom();
});
createRoomButton.x = 1024;
createRoomButton.y = 820;
game.addChild(createRoomButton);
var backButton = new Button("BACK", function () {
showMenu();
});
backButton.x = 1024;
backButton.y = 940;
game.addChild(backButton);
}
function createMultiplayerRoom() {
if (multiplayer && multiplayer.createRoom) {
multiplayer.createRoom({
maxPlayers: 4,
gameType: "racing"
}, function (room) {
multiplayerRoom = room;
setupMultiplayerEvents();
showCharacterSelect();
});
} else {
console.log("Multiplayer not available");
showCharacterSelect();
}
}
function joinMultiplayerRoom() {
if (multiplayer && multiplayer.joinRoom) {
multiplayer.joinRoom({
gameType: "racing"
}, function (room) {
multiplayerRoom = room;
setupMultiplayerEvents();
showCharacterSelect();
});
} else {
console.log("Multiplayer not available");
showCharacterSelect();
}
}
function setupMultiplayerEvents() {
if (multiplayer && multiplayer.on) {
multiplayer.on('playerJoined', function (playerData) {
console.log('Player joined:', playerData.nickname);
});
multiplayer.on('playerLeft', function (playerData) {
console.log('Player left:', playerData.nickname);
// Remove player from game
for (var i = otherPlayers.length - 1; i >= 0; i--) {
if (otherPlayers[i].playerId === playerData.id) {
if (otherPlayers[i].parent) {
otherPlayers[i].destroy();
}
otherPlayers.splice(i, 1);
break;
}
}
});
multiplayer.on('gameData', function (data) {
handleMultiplayerData(data);
});
multiplayer.on('raceStart', function (data) {
if (gameState === 'character_select') {
startMultiplayerRace();
}
});
}
}
function handleMultiplayerData(data) {
if (data.type === 'playerPosition' && gameState === 'multiplayer_racing') {
updateOtherPlayerPosition(data);
} else if (data.type === 'playerFinished') {
handlePlayerFinished(data);
}
}
function updateOtherPlayerPosition(data) {
var existingPlayer = null;
for (var i = 0; i < otherPlayers.length; i++) {
if (otherPlayers[i].playerId === data.playerId) {
existingPlayer = otherPlayers[i];
break;
}
}
if (!existingPlayer) {
existingPlayer = new Character(data.character);
existingPlayer.playerId = data.playerId;
existingPlayer.nickname = data.nickname;
otherPlayers.push(existingPlayer);
game.addChild(existingPlayer);
}
if (existingPlayer && existingPlayer.parent) {
existingPlayer.x = data.x;
existingPlayer.y = data.y;
existingPlayer.direction = data.direction;
if (existingPlayer.children[0]) {
existingPlayer.children[0].rotation = data.direction;
}
}
}
function handlePlayerFinished(data) {
playersFinished.push({
playerId: data.playerId,
nickname: data.nickname,
time: data.time,
character: data.character
});
}
function startRace() {
gameState = 'racing';
game.removeChildren();
// Add track background
var trackBg = LK.getAsset('track_background_' + selectedTrack, {
anchorX: 0.5,
anchorY: 0.5
});
trackBg.x = 1024;
trackBg.y = 1366;
game.addChild(trackBg);
// Create player
player = new Character(selectedCharacter);
player.x = 1024;
player.y = 2400;
game.addChild(player);
// Create track elements
createTrack();
createObstacles();
// Start race timer
startTime = Date.now();
lapTime = 0;
raceDistance = 0;
playerLives = 3;
// Play race music
LK.playMusic('race_music');
storage.gamesPlayed++;
// Create control buttons
createControlButtons();
}
function startMultiplayerRace() {
gameState = 'multiplayer_racing';
game.removeChildren();
// Add track background
var trackBg = LK.getAsset('track_background_' + selectedTrack, {
anchorX: 0.5,
anchorY: 0.5
});
trackBg.x = 1024;
trackBg.y = 1366;
game.addChild(trackBg);
// Create player
player = new Character(selectedCharacter);
player.x = 1024;
player.y = 2400;
player.playerId = multiplayer && multiplayer.getPlayerId ? multiplayer.getPlayerId() : 'player_' + Date.now();
player.nickname = playerNickname;
game.addChild(player);
// Create track elements
createTrack();
createObstacles();
// Start race timer
startTime = Date.now();
lapTime = 0;
raceDistance = 0;
playersFinished = [];
playerLives = 3;
// Play race music
LK.playMusic('race_music');
storage.gamesPlayed++;
// Create control buttons
createControlButtons();
// Send race start signal
if (multiplayer && multiplayer.sendData) {
multiplayer.sendData({
type: 'raceStarted'
});
}
}
function createTrack() {
// Create track-specific walls based on selected track
if (selectedTrack === 1) {
// Forest Track - winding path with trees
createForestTrack();
} else if (selectedTrack === 2) {
// Desert Track - canyon walls
createDesertTrack();
} else if (selectedTrack === 3) {
// Night Track - city buildings
createNightTrack();
}
// Create finish line
var finishLine = LK.getAsset('finish_line', {
anchorX: 0.5,
anchorY: 0.5
});
finishLine.x = 1024;
finishLine.y = 100;
game.addChild(finishLine);
}
function createForestTrack() {
// Forest track with edge walls only - center is completely open
var trackSections = [{
leftX: 0,
rightX: 2048,
y: 0
}, {
leftX: 0,
rightX: 2048,
y: 200
}, {
leftX: 0,
rightX: 2048,
y: 400
}, {
leftX: 0,
rightX: 2048,
y: 600
}, {
leftX: 0,
rightX: 2048,
y: 800
}, {
leftX: 0,
rightX: 2048,
y: 1000
}, {
leftX: 0,
rightX: 2048,
y: 1200
}, {
leftX: 0,
rightX: 2048,
y: 1400
}, {
leftX: 0,
rightX: 2048,
y: 1600
}, {
leftX: 0,
rightX: 2048,
y: 1800
}, {
leftX: 0,
rightX: 2048,
y: 2000
}, {
leftX: 0,
rightX: 2048,
y: 2200
}, {
leftX: 0,
rightX: 2048,
y: 2400
}];
for (var i = 0; i < trackSections.length; i++) {
var section = trackSections[i];
var leftWall = LK.getAsset('track_wall', {
anchorX: 0.5,
anchorY: 0.5
});
leftWall.x = section.leftX;
leftWall.y = section.y;
leftWall.tint = 0x228B22; // Forest green
game.addChild(leftWall);
var rightWall = LK.getAsset('track_wall', {
anchorX: 0.5,
anchorY: 0.5
});
rightWall.x = section.rightX;
rightWall.y = section.y;
rightWall.tint = 0x228B22; // Forest green
game.addChild(rightWall);
}
}
function createDesertTrack() {
// Desert track with edge walls only - center is completely open
var trackSections = [{
leftX: 0,
rightX: 2048,
y: 0
}, {
leftX: 0,
rightX: 2048,
y: 200
}, {
leftX: 0,
rightX: 2048,
y: 400
}, {
leftX: 0,
rightX: 2048,
y: 600
}, {
leftX: 0,
rightX: 2048,
y: 800
}, {
leftX: 0,
rightX: 2048,
y: 1000
}, {
leftX: 0,
rightX: 2048,
y: 1200
}, {
leftX: 0,
rightX: 2048,
y: 1400
}, {
leftX: 0,
rightX: 2048,
y: 1600
}, {
leftX: 0,
rightX: 2048,
y: 1800
}, {
leftX: 0,
rightX: 2048,
y: 2000
}, {
leftX: 0,
rightX: 2048,
y: 2200
}, {
leftX: 0,
rightX: 2048,
y: 2400
}];
for (var i = 0; i < trackSections.length; i++) {
var section = trackSections[i];
var leftWall = LK.getAsset('track_wall', {
anchorX: 0.5,
anchorY: 0.5
});
leftWall.x = section.leftX;
leftWall.y = section.y;
leftWall.tint = 0xDEB887; // Sandy brown
leftWall.scaleY = 1.5; // Taller canyon walls
game.addChild(leftWall);
var rightWall = LK.getAsset('track_wall', {
anchorX: 0.5,
anchorY: 0.5
});
rightWall.x = section.rightX;
rightWall.y = section.y;
rightWall.tint = 0xDEB887; // Sandy brown
rightWall.scaleY = 1.5; // Taller canyon walls
game.addChild(rightWall);
}
}
function createNightTrack() {
// Night track with edge walls only - center is completely open
var trackSections = [{
leftX: 0,
rightX: 2048,
y: 0
}, {
leftX: 0,
rightX: 2048,
y: 200
}, {
leftX: 0,
rightX: 2048,
y: 400
}, {
leftX: 0,
rightX: 2048,
y: 600
}, {
leftX: 0,
rightX: 2048,
y: 800
}, {
leftX: 0,
rightX: 2048,
y: 1000
}, {
leftX: 0,
rightX: 2048,
y: 1200
}, {
leftX: 0,
rightX: 2048,
y: 1400
}, {
leftX: 0,
rightX: 2048,
y: 1600
}, {
leftX: 0,
rightX: 2048,
y: 1800
}, {
leftX: 0,
rightX: 2048,
y: 2000
}, {
leftX: 0,
rightX: 2048,
y: 2200
}, {
leftX: 0,
rightX: 2048,
y: 2400
}];
for (var i = 0; i < trackSections.length; i++) {
var section = trackSections[i];
var leftWall = LK.getAsset('track_wall', {
anchorX: 0.5,
anchorY: 0.5
});
leftWall.x = section.leftX;
leftWall.y = section.y;
leftWall.tint = 0x2F4F4F; // Dark slate gray
leftWall.scaleY = 2; // Tall building walls
game.addChild(leftWall);
var rightWall = LK.getAsset('track_wall', {
anchorX: 0.5,
anchorY: 0.5
});
rightWall.x = section.rightX;
rightWall.y = section.y;
rightWall.tint = 0x2F4F4F; // Dark slate gray
rightWall.scaleY = 2; // Tall building walls
game.addChild(rightWall);
}
}
function createObstacles() {
obstacles = [];
var obstacleTypes = ['endoskeleton', 'broken_head', 'wrench', 'knife'];
// Increase number of obstacles from 15 to 25
for (var i = 0; i < 25; i++) {
var type = obstacleTypes[Math.floor(Math.random() * obstacleTypes.length)];
var x = 200 + Math.random() * 1648;
var y = 200 + Math.random() * 2200;
var obstacle = new Obstacle(type, x, y);
obstacles.push(obstacle);
game.addChild(obstacle);
}
// Add additional clusters of obstacles in strategic locations
// First cluster - early section
for (var i = 0; i < 5; i++) {
var type = obstacleTypes[Math.floor(Math.random() * obstacleTypes.length)];
var x = 400 + Math.random() * 1248; // More centered placement
var y = 1800 + Math.random() * 300; // Near starting area
var obstacle = new Obstacle(type, x, y);
obstacles.push(obstacle);
game.addChild(obstacle);
}
// Second cluster - middle section
for (var i = 0; i < 5; i++) {
var type = obstacleTypes[Math.floor(Math.random() * obstacleTypes.length)];
var x = 300 + Math.random() * 1448;
var y = 1200 + Math.random() * 400; // Middle section
var obstacle = new Obstacle(type, x, y);
obstacles.push(obstacle);
game.addChild(obstacle);
}
// Third cluster - near finish line
for (var i = 0; i < 5; i++) {
var type = obstacleTypes[Math.floor(Math.random() * obstacleTypes.length)];
var x = 500 + Math.random() * 1048;
var y = 300 + Math.random() * 200; // Near finish line
var obstacle = new Obstacle(type, x, y);
obstacles.push(obstacle);
game.addChild(obstacle);
}
}
function checkCollisions() {
if (!player || !player.parent) return;
try {
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (obstacle && obstacle.parent && player.intersects && player.intersects(obstacle)) {
// Crash effect
if (LK.getSound) {
LK.getSound('crash').play();
}
if (LK.effects && LK.effects.flashObject) {
LK.effects.flashObject(player, 0xFF0000, 500);
}
// Reduce lives
playerLives--;
// Slow down player
player.speed *= 0.5;
// Move obstacle away
obstacle.x = -1000;
obstacle.y = -1000;
// Check if game over
if (playerLives <= 0) {
LK.setTimeout(function () {
LK.showGameOver();
}, 500);
}
}
}
} catch (e) {
console.log("Error in collision detection:", e);
}
}
function updateRace() {
if (!player || !player.parent) return;
// Update race distance based on forward progress
var progress = (2400 - player.y) / 2300;
if (progress > raceDistance / totalRaceDistance) {
raceDistance = progress * totalRaceDistance;
}
// Check if race is finished
if (player.y <= 100) {
finishRace();
}
// Update UI
lapTime = Date.now() - startTime;
try {
if (timeText && timeText.parent && timeText.setText) {
timeText.setText("Time: " + (lapTime / 1000).toFixed(2) + "s");
}
if (speedText && speedText.parent && speedText.setText) {
speedText.setText("Speed: " + Math.floor(player.speed * 10));
}
if (progressText && progressText.parent && progressText.setText) {
progressText.setText("Progress: " + Math.floor(raceDistance / totalRaceDistance * 100) + "%");
}
if (livesText && livesText.parent && livesText.setText) {
livesText.setText("Lives: " + playerLives);
}
} catch (e) {
console.log("Error updating UI:", e);
}
}
function finishRace() {
gameState = 'results';
// Stop music
LK.stopMusic();
// Check for new best time
var isNewBest = lapTime < bestTime;
if (isNewBest) {
bestTime = lapTime;
storage.bestTime = bestTime;
storage.wins++;
}
// Check for unlock conditions
if (lapTime < 30000 && !storage.endoUnlocked) {
// Under 30 seconds
storage.endoUnlocked = true;
LK.effects.flashScreen(0x00FF00, 1000);
}
if (storage.wins >= 5 && !storage.goldenFreddyUnlocked) {
storage.goldenFreddyUnlocked = true;
LK.effects.flashScreen(0xDAA520, 1000);
}
// Show results
LK.setTimeout(function () {
showResults(isNewBest);
}, 1000);
}
function updateMultiplayerRace() {
if (!player || !player.parent) return;
// Update race distance based on forward progress
var progress = (2400 - player.y) / 2300;
if (progress > raceDistance / totalRaceDistance) {
raceDistance = progress * totalRaceDistance;
}
// Check if race is finished
if (player.y <= 100) {
finishMultiplayerRace();
}
// Update UI
lapTime = Date.now() - startTime;
try {
if (timeText && timeText.parent && timeText.setText) {
timeText.setText("Time: " + (lapTime / 1000).toFixed(2) + "s");
}
if (speedText && speedText.parent && speedText.setText) {
speedText.setText("Speed: " + Math.floor(player.speed * 10));
}
if (progressText && progressText.parent && progressText.setText) {
progressText.setText("Progress: " + Math.floor(raceDistance / totalRaceDistance * 100) + "%");
}
if (livesText && livesText.parent && livesText.setText) {
livesText.setText("Lives: " + playerLives);
}
} catch (e) {
console.log("Error updating multiplayer UI:", e);
}
}
function finishMultiplayerRace() {
// Stop music
LK.stopMusic();
// Send finish data to other players
if (multiplayer && multiplayer.sendData) {
multiplayer.sendData({
type: 'playerFinished',
playerId: player.playerId,
nickname: player.nickname,
time: lapTime,
character: selectedCharacter
});
}
// Add self to finished list
playersFinished.push({
playerId: player.playerId,
nickname: player.nickname,
time: lapTime,
character: selectedCharacter
});
// Check for new best time
var isNewBest = lapTime < bestTime;
if (isNewBest) {
bestTime = lapTime;
storage.bestTime = bestTime;
storage.wins++;
}
// Check for unlock conditions
if (lapTime < 30000 && !storage.endoUnlocked) {
storage.endoUnlocked = true;
LK.effects.flashScreen(0x00FF00, 1000);
}
if (storage.wins >= 5 && !storage.goldenFreddyUnlocked) {
storage.goldenFreddyUnlocked = true;
LK.effects.flashScreen(0xDAA520, 1000);
}
// Show results after delay
LK.setTimeout(function () {
showMultiplayerResults(isNewBest);
}, 1000);
}
function showMultiplayerResults(isNewBest) {
game.removeChildren();
var resultsText = new Text2("RACE COMPLETE!", {
size: 60,
fill: 0xFFD700
});
resultsText.anchor.set(0.5, 0.5);
resultsText.x = 1024;
resultsText.y = 200;
game.addChild(resultsText);
// Sort players by finish time
playersFinished.sort(function (a, b) {
return a.time - b.time;
});
// Display rankings
for (var i = 0; i < Math.min(playersFinished.length, 4); i++) {
var playerData = playersFinished[i];
var position = i + 1;
var rankText = new Text2(position + ". " + playerData.nickname + " - " + (playerData.time / 1000).toFixed(2) + "s", {
size: 40,
fill: position === 1 ? 0xFFD700 : 0xFFFFFF
});
rankText.anchor.set(0.5, 0.5);
rankText.x = 1024;
rankText.y = 400 + i * 80;
game.addChild(rankText);
}
if (isNewBest) {
var newBestText = new Text2("NEW PERSONAL BEST!", {
size: 40,
fill: 0x00FF00
});
newBestText.anchor.set(0.5, 0.5);
newBestText.x = 1024;
newBestText.y = 800;
game.addChild(newBestText);
}
var playAgainButton = new Button("RACE AGAIN", function () {
showCharacterSelect();
});
playAgainButton.x = 1024;
playAgainButton.y = 1000;
game.addChild(playAgainButton);
var menuButton = new Button("MAIN MENU", function () {
if (multiplayerRoom && multiplayer && multiplayer.leaveRoom) {
multiplayer.leaveRoom();
multiplayerRoom = null;
}
showMenu();
});
menuButton.x = 1024;
menuButton.y = 1120;
game.addChild(menuButton);
}
function createControlButtons() {
try {
// Create accelerate button
accelerateButton = new Button("ACCELERATE", function () {});
accelerateButton.x = 1536;
accelerateButton.y = 2500;
accelerateButton.scaleX = 0.8;
accelerateButton.scaleY = 0.8;
accelerateButton.alpha = 0.8;
game.addChild(accelerateButton);
// Override button events for continuous press
accelerateButton.down = function () {
isAccelerating = true;
if (LK.getSound) LK.getSound('select').play();
};
accelerateButton.up = function () {
isAccelerating = false;
};
// Create brake button
brakeButton = new Button("BRAKE", function () {});
brakeButton.x = 512;
brakeButton.y = 2500;
brakeButton.scaleX = 0.8;
brakeButton.scaleY = 0.8;
brakeButton.alpha = 0.8;
game.addChild(brakeButton);
brakeButton.down = function () {
isBraking = true;
if (LK.getSound) LK.getSound('select').play();
};
brakeButton.up = function () {
isBraking = false;
};
// Create left turn button
leftButton = new Button("LEFT", function () {});
leftButton.x = 300;
leftButton.y = 2200;
leftButton.scaleX = 0.7;
leftButton.scaleY = 0.7;
leftButton.alpha = 0.8;
game.addChild(leftButton);
leftButton.down = function () {
isTurningLeft = true;
if (LK.getSound) LK.getSound('select').play();
};
leftButton.up = function () {
isTurningLeft = false;
};
// Create right turn button
rightButton = new Button("RIGHT", function () {});
rightButton.x = 1748;
rightButton.y = 2200;
rightButton.scaleX = 0.7;
rightButton.scaleY = 0.7;
rightButton.alpha = 0.8;
game.addChild(rightButton);
rightButton.down = function () {
isTurningRight = true;
if (LK.getSound) LK.getSound('select').play();
};
rightButton.up = function () {
isTurningRight = false;
};
} catch (e) {
console.log("Error creating control buttons:", e);
}
}
function showResults(isNewBest) {
game.removeChildren();
var resultsText = new Text2("RACE COMPLETE!", {
size: 60,
fill: 0xFFD700
});
resultsText.anchor.set(0.5, 0.5);
resultsText.x = 1024;
resultsText.y = 400;
game.addChild(resultsText);
var timeResultText = new Text2("Your Time: " + (lapTime / 1000).toFixed(2) + "s", {
size: 50,
fill: isNewBest ? "#00FF00" : "#FFFFFF"
});
timeResultText.anchor.set(0.5, 0.5);
timeResultText.x = 1024;
timeResultText.y = 600;
game.addChild(timeResultText);
if (isNewBest) {
var newBestText = new Text2("NEW BEST TIME!", {
size: 40,
fill: 0x00FF00
});
newBestText.anchor.set(0.5, 0.5);
newBestText.x = 1024;
newBestText.y = 700;
game.addChild(newBestText);
}
var playAgainButton = new Button("RACE AGAIN", function () {
showCharacterSelect();
});
playAgainButton.x = 1024;
playAgainButton.y = 900;
game.addChild(playAgainButton);
var menuButton = new Button("MAIN MENU", function () {
showMenu();
});
menuButton.x = 1024;
menuButton.y = 1020;
game.addChild(menuButton);
}
// Touch controls
var isAccelerating = false;
var isBraking = false;
var isTurningLeft = false;
var isTurningRight = false;
// Control buttons
var accelerateButton = null;
var brakeButton = null;
var leftButton = null;
var rightButton = null;
game.down = function (x, y, obj) {
if (gameState !== 'racing' && gameState !== 'multiplayer_racing' || !player) return;
// Divide screen into control zones
if (y > 2000) {
// Bottom area for acceleration/braking
if (x < 1024) {
isBraking = true;
} else {
isAccelerating = true;
}
} else {
// Upper area for steering
if (x < 1024) {
isTurningLeft = true;
} else {
isTurningRight = true;
}
}
};
game.up = function (x, y, obj) {
// Reset all controls on any touch release
isAccelerating = false;
isBraking = false;
isTurningLeft = false;
isTurningRight = false;
};
game.update = function () {
if ((gameState === 'racing' || gameState === 'multiplayer_racing') && player && player.parent) {
// Validate player object exists and is properly attached
try {
// Handle controls
if (isAccelerating) {
player.moveForward();
if (LK.ticks % 20 === 0) {
LK.getSound('engine').play();
}
} else if (isBraking) {
player.moveBackward();
} else {
player.applyDeceleration();
}
if (isTurningLeft) {
player.turnLeft();
}
if (isTurningRight) {
player.turnRight();
}
checkCollisions();
if (gameState === 'multiplayer_racing') {
updateMultiplayerRace();
// Send player position every few frames
if (LK.ticks % 3 === 0 && multiplayer && multiplayer.sendData) {
multiplayer.sendData({
type: 'playerPosition',
playerId: player.playerId,
nickname: player.nickname,
character: selectedCharacter,
x: player.x,
y: player.y,
direction: player.direction
});
}
} else {
updateRace();
}
} catch (e) {
console.log("Error in game update:", e);
}
}
};
// Initialize game
showMenu(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Button = Container.expand(function (text, callback) {
var self = Container.call(this);
self.callback = callback;
var bg = self.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
var label = new Text2(text, {
size: 40,
fill: 0xFFFFFF
});
label.anchor.set(0.5, 0.5);
self.addChild(label);
self.down = function () {
LK.getSound('select').play();
if (self.callback) {
self.callback();
}
};
return self;
});
var Character = Container.expand(function (characterType) {
var self = Container.call(this);
self.characterType = characterType || 'freddy';
self.speed = 0;
self.maxSpeed = 8;
self.acceleration = 0.3;
self.deceleration = 0.2;
self.turnSpeed = 0.1;
self.direction = 0;
self.isUnlocked = true;
// Set unlock status
if (self.characterType === 'endo') {
self.isUnlocked = storage.endoUnlocked || false;
} else if (self.characterType === 'golden_freddy') {
self.isUnlocked = storage.goldenFreddyUnlocked || false;
}
var sprite = self.attachAsset(self.characterType, {
anchorX: 0.5,
anchorY: 0.5
});
self.moveForward = function () {
if (self.speed < self.maxSpeed) {
self.speed += self.acceleration;
}
};
self.moveBackward = function () {
if (self.speed > -self.maxSpeed * 0.5) {
self.speed -= self.acceleration;
}
};
self.turnLeft = function () {
self.direction -= self.turnSpeed;
sprite.rotation = self.direction;
};
self.turnRight = function () {
self.direction += self.turnSpeed;
sprite.rotation = self.direction;
};
self.applyDeceleration = function () {
if (self.speed > 0) {
self.speed -= self.deceleration;
if (self.speed < 0) self.speed = 0;
} else if (self.speed < 0) {
self.speed += self.deceleration;
if (self.speed > 0) self.speed = 0;
}
};
self.update = function () {
// Move based on current speed and direction
self.x += Math.sin(self.direction) * self.speed;
self.y -= Math.cos(self.direction) * self.speed;
// Keep within track bounds - adjust based on selected track
var minX = 100;
var maxX = 1948;
if (selectedTrack === 1) {
// Forest track - dynamic bounds based on Y position
var ySection = Math.floor(self.y / 200);
if (ySection >= 0 && ySection <= 12) {
var forestBounds = [{
minX: 100,
maxX: 1948
}, {
minX: 100,
maxX: 1948
}, {
minX: 100,
maxX: 1948
}, {
minX: 100,
maxX: 1948
}, {
minX: 300,
maxX: 1748
}, {
minX: 400,
maxX: 1648
}, {
minX: 350,
maxX: 1698
}, {
minX: 250,
maxX: 1798
}, {
minX: 150,
maxX: 1898
}, {
minX: 100,
maxX: 1948
}, {
minX: 150,
maxX: 1898
}, {
minX: 200,
maxX: 1848
}, {
minX: 250,
maxX: 1798
}];
if (forestBounds[ySection]) {
minX = forestBounds[ySection].minX;
maxX = forestBounds[ySection].maxX;
}
}
} else if (selectedTrack === 2) {
// Desert track - canyon bounds
var ySection = Math.floor(self.y / 200);
if (ySection >= 0 && ySection <= 12) {
var desertBounds = [{
minX: 300,
maxX: 1748
}, {
minX: 250,
maxX: 1798
}, {
minX: 200,
maxX: 1848
}, {
minX: 150,
maxX: 1898
}, {
minX: 100,
maxX: 1948
}, {
minX: 150,
maxX: 1898
}, {
minX: 200,
maxX: 1848
}, {
minX: 300,
maxX: 1748
}, {
minX: 400,
maxX: 1648
}, {
minX: 500,
maxX: 1548
}, {
minX: 400,
maxX: 1648
}, {
minX: 300,
maxX: 1748
}, {
minX: 200,
maxX: 1848
}];
if (desertBounds[ySection]) {
minX = desertBounds[ySection].minX;
maxX = desertBounds[ySection].maxX;
}
}
} else if (selectedTrack === 3) {
// Night track - city bounds
var ySection = Math.floor(self.y / 200);
if (ySection >= 0 && ySection <= 12) {
var nightBounds = [{
minX: 150,
maxX: 1898
}, {
minX: 200,
maxX: 1848
}, {
minX: 350,
maxX: 1698
}, {
minX: 500,
maxX: 1548
}, {
minX: 400,
maxX: 1648
}, {
minX: 250,
maxX: 1798
}, {
minX: 150,
maxX: 1898
}, {
minX: 100,
maxX: 1948
}, {
minX: 200,
maxX: 1848
}, {
minX: 350,
maxX: 1698
}, {
minX: 450,
maxX: 1598
}, {
minX: 300,
maxX: 1748
}, {
minX: 150,
maxX: 1898
}];
if (nightBounds[ySection]) {
minX = nightBounds[ySection].minX;
maxX = nightBounds[ySection].maxX;
}
}
}
if (self.x < minX) self.x = minX;
if (self.x > maxX) self.x = maxX;
if (self.y < 100) self.y = 100;
if (self.y > 2632) self.y = 2632;
};
return self;
});
var CharacterCard = Container.expand(function (characterType) {
var self = Container.call(this);
self.characterType = characterType;
var bg = self.attachAsset('character_card', {
anchorX: 0.5,
anchorY: 0.5
});
var character = new Character(characterType);
character.x = 0;
character.y = -50;
self.addChild(character);
var nameText = new Text2(characterType.toUpperCase(), {
size: 24,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0.5);
nameText.y = 80;
self.addChild(nameText);
// Show locked status
if (!character.isUnlocked) {
bg.tint = 0x666666;
var lockText = new Text2("LOCKED", {
size: 20,
fill: 0xFF0000
});
lockText.anchor.set(0.5, 0.5);
lockText.y = 100;
self.addChild(lockText);
}
self.down = function () {
if (character.isUnlocked) {
LK.getSound('select').play();
selectedCharacter = self.characterType;
// Just select character, don't start race yet
}
};
return self;
});
var Obstacle = Container.expand(function (obstacleType, x, y) {
var self = Container.call(this);
self.obstacleType = obstacleType || 'endoskeleton';
self.x = x || 0;
self.y = y || 0;
var sprite = self.attachAsset(self.obstacleType, {
anchorX: 0.5,
anchorY: 0.5
});
// Add some rotation for visual variety
sprite.rotation = Math.random() * Math.PI * 2;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x001122
});
/****
* Game Code
****/
// Character sprites
// Track elements
// Obstacles
// UI elements
// Sounds
var gameState = 'menu'; // 'menu', 'character_select', 'racing', 'results', 'multiplayer_lobby', 'multiplayer_racing'
var selectedCharacter = 'freddy';
var selectedTrack = 1;
var player = null;
var otherPlayers = [];
var obstacles = [];
var lapTime = 0;
var startTime = 0;
var bestTime = storage.bestTime || 999999;
var raceDistance = 0;
var totalRaceDistance = 3000;
var isMultiplayer = false;
var playerNickname = storage.playerNickname || "Player";
var multiplayerRoom = null;
var playersFinished = [];
var playerLives = 3;
// Define multiplayer object to prevent undefined errors
var multiplayer = {
sendData: function sendData(data) {
// Mock function for when multiplayer is not available
console.log("Multiplayer not available - would send:", data);
},
getPlayerId: function getPlayerId() {
return 'player_' + Date.now();
},
createRoom: function createRoom(config, callback) {
console.log("Multiplayer not available - would create room");
if (callback) callback(null);
},
joinRoom: function joinRoom(config, callback) {
console.log("Multiplayer not available - would join room");
if (callback) callback(null);
},
on: function on(event, callback) {
console.log("Multiplayer not available - would listen for:", event);
},
leaveRoom: function leaveRoom() {
console.log("Multiplayer not available - would leave room");
}
};
// Initialize storage defaults
if (storage.gamesPlayed === undefined) storage.gamesPlayed = 0;
if (storage.wins === undefined) storage.wins = 0;
if (storage.endoUnlocked === undefined) storage.endoUnlocked = false;
if (storage.goldenFreddyUnlocked === undefined) storage.goldenFreddyUnlocked = false;
// UI Elements
var titleText = new Text2("FREDDY'S RACING\nCHAMPIONSHIP", {
size: 80,
fill: 0xFFD700
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
var timeText = new Text2("", {
size: 60,
fill: 0xFFFFFF
});
timeText.anchor.set(0.5, 0);
LK.gui.top.addChild(timeText);
var speedText = new Text2("", {
size: 40,
fill: 0x00FF00
});
speedText.anchor.set(0, 0);
LK.gui.bottomLeft.addChild(speedText);
var progressText = new Text2("", {
size: 40,
fill: 0xFFFF00
});
progressText.anchor.set(1, 0);
LK.gui.bottomRight.addChild(progressText);
var livesText = new Text2("", {
size: 40,
fill: 0xFF0000
});
livesText.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(livesText);
function showMenu() {
gameState = 'menu';
game.removeChildren();
// Add background image
var menuBg = LK.getAsset('menu_background', {
anchorX: 0.5,
anchorY: 0.5
});
menuBg.x = 1024;
menuBg.y = 1366;
game.addChild(menuBg);
// Add pulsing animation to background
tween(menuBg, {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(menuBg, {
scaleX: 1,
scaleY: 1
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Restart the animation loop
if (gameState === 'menu') {
tween(menuBg, {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: arguments.callee.caller
});
}
}
});
}
});
// Add subtle rotation animation
tween(menuBg, {
rotation: 0.02
}, {
duration: 4000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(menuBg, {
rotation: -0.02
}, {
duration: 4000,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (gameState === 'menu') {
tween(menuBg, {
rotation: 0.02
}, {
duration: 4000,
easing: tween.easeInOut,
onFinish: arguments.callee.caller
});
}
}
});
}
});
game.addChild(titleText);
// Add floating animation to title
tween(titleText, {
y: 380
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(titleText, {
y: 420
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (gameState === 'menu') {
tween(titleText, {
y: 380
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: arguments.callee.caller
});
}
}
});
}
});
// Add color tinting animation to title
tween(titleText, {
tint: 0xFFFF00
}, {
duration: 2500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(titleText, {
tint: 0xFFD700
}, {
duration: 2500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (gameState === 'menu') {
tween(titleText, {
tint: 0xFFFF00
}, {
duration: 2500,
easing: tween.easeInOut,
onFinish: arguments.callee.caller
});
}
}
});
}
});
var singlePlayerButton = new Button("SINGLE PLAYER", function () {
isMultiplayer = false;
showCharacterSelect();
});
singlePlayerButton.x = 1024;
singlePlayerButton.y = 700;
game.addChild(singlePlayerButton);
var multiplayerButton = new Button("MULTIPLAYER", function () {
isMultiplayer = true;
showMultiplayerLobby();
});
multiplayerButton.x = 1024;
multiplayerButton.y = 820;
game.addChild(multiplayerButton);
var statsButton = new Button("STATS", function () {
showStats();
});
statsButton.x = 1024;
statsButton.y = 940;
game.addChild(statsButton);
// Show best time
if (bestTime < 999999) {
var bestTimeText = new Text2("Best Time: " + (bestTime / 1000).toFixed(2) + "s", {
size: 40,
fill: 0x00FF00
});
bestTimeText.anchor.set(0.5, 0.5);
bestTimeText.x = 1024;
bestTimeText.y = 1200;
game.addChild(bestTimeText);
}
}
function showCharacterSelect() {
gameState = 'character_select';
game.removeChildren();
var selectText = new Text2("SELECT CHARACTER", {
size: 60,
fill: 0xFFD700
});
selectText.anchor.set(0.5, 0.5);
selectText.x = 1024;
selectText.y = 300;
game.addChild(selectText);
var characters = ['freddy', 'bonnie', 'chica', 'foxy', 'endo', 'golden_freddy'];
var startX = 300;
var spacing = 300;
for (var i = 0; i < characters.length; i++) {
var card = new CharacterCard(characters[i]);
card.x = startX + i % 3 * spacing;
card.y = 600 + Math.floor(i / 3) * 400;
game.addChild(card);
}
var nextButton = new Button("NEXT: SELECT TRACK", function () {
showTrackSelect();
});
nextButton.x = 1024;
nextButton.y = 1400;
game.addChild(nextButton);
var backButton = new Button("BACK", function () {
showMenu();
});
backButton.x = 200;
backButton.y = 200;
game.addChild(backButton);
}
function showTrackSelect() {
gameState = 'track_select';
game.removeChildren();
var selectText = new Text2("SELECT TRACK", {
size: 60,
fill: 0xFFD700
});
selectText.anchor.set(0.5, 0.5);
selectText.x = 1024;
selectText.y = 300;
game.addChild(selectText);
// Track 1 - Forest Track
var track1Bg = LK.getAsset('track_background_1', {
anchorX: 0.5,
anchorY: 0.5
});
track1Bg.x = 512;
track1Bg.y = 700;
track1Bg.scaleX = 0.4;
track1Bg.scaleY = 0.4;
game.addChild(track1Bg);
var track1Button = new Button("FOREST TRACK", function () {
selectedTrack = 1;
if (isMultiplayer) {
if (multiplayer && multiplayer.sendData) {
multiplayer.sendData({
type: 'characterSelected',
character: selectedCharacter,
nickname: playerNickname,
track: selectedTrack
});
}
startMultiplayerRace();
} else {
startRace();
}
});
track1Button.x = 512;
track1Button.y = 950;
game.addChild(track1Button);
var track1Text = new Text2("FOREST", {
size: 30,
fill: 0xFFFFFF
});
track1Text.anchor.set(0.5, 0.5);
track1Text.x = 512;
track1Text.y = 500;
game.addChild(track1Text);
// Track 2 - Desert Track
var track2Bg = LK.getAsset('track_background_2', {
anchorX: 0.5,
anchorY: 0.5
});
track2Bg.x = 1024;
track2Bg.y = 700;
track2Bg.scaleX = 0.4;
track2Bg.scaleY = 0.4;
game.addChild(track2Bg);
var track2Button = new Button("DESERT TRACK", function () {
selectedTrack = 2;
if (isMultiplayer) {
if (multiplayer && multiplayer.sendData) {
multiplayer.sendData({
type: 'characterSelected',
character: selectedCharacter,
nickname: playerNickname,
track: selectedTrack
});
}
startMultiplayerRace();
} else {
startRace();
}
});
track2Button.x = 1024;
track2Button.y = 950;
game.addChild(track2Button);
var track2Text = new Text2("DESERT", {
size: 30,
fill: 0xFFFFFF
});
track2Text.anchor.set(0.5, 0.5);
track2Text.x = 1024;
track2Text.y = 500;
game.addChild(track2Text);
// Track 3 - Night Track
var track3Bg = LK.getAsset('track_background_3', {
anchorX: 0.5,
anchorY: 0.5
});
track3Bg.x = 1536;
track3Bg.y = 700;
track3Bg.scaleX = 0.4;
track3Bg.scaleY = 0.4;
game.addChild(track3Bg);
var track3Button = new Button("NIGHT TRACK", function () {
selectedTrack = 3;
if (isMultiplayer) {
if (multiplayer && multiplayer.sendData) {
multiplayer.sendData({
type: 'characterSelected',
character: selectedCharacter,
nickname: playerNickname,
track: selectedTrack
});
}
startMultiplayerRace();
} else {
startRace();
}
});
track3Button.x = 1536;
track3Button.y = 950;
game.addChild(track3Button);
var track3Text = new Text2("NIGHT", {
size: 30,
fill: 0xFFFFFF
});
track3Text.anchor.set(0.5, 0.5);
track3Text.x = 1536;
track3Text.y = 500;
game.addChild(track3Text);
var backButton = new Button("BACK", function () {
showCharacterSelect();
});
backButton.x = 200;
backButton.y = 200;
game.addChild(backButton);
}
function showStats() {
game.removeChildren();
var statsText = new Text2("STATISTICS", {
size: 60,
fill: 0xFFD700
});
statsText.anchor.set(0.5, 0.5);
statsText.x = 1024;
statsText.y = 300;
game.addChild(statsText);
var gamesText = new Text2("Games Played: " + storage.gamesPlayed, {
size: 40,
fill: 0xFFFFFF
});
gamesText.anchor.set(0.5, 0.5);
gamesText.x = 1024;
gamesText.y = 500;
game.addChild(gamesText);
var winsText = new Text2("Wins: " + storage.wins, {
size: 40,
fill: 0xFFFFFF
});
winsText.anchor.set(0.5, 0.5);
winsText.x = 1024;
winsText.y = 600;
game.addChild(winsText);
var backButton = new Button("BACK", function () {
showMenu();
});
backButton.x = 1024;
backButton.y = 800;
game.addChild(backButton);
}
function showMultiplayerLobby() {
gameState = 'multiplayer_lobby';
game.removeChildren();
var lobbyText = new Text2("MULTIPLAYER LOBBY", {
size: 60,
fill: 0xFFD700
});
lobbyText.anchor.set(0.5, 0.5);
lobbyText.x = 1024;
lobbyText.y = 300;
game.addChild(lobbyText);
var nicknameText = new Text2("Nickname: " + playerNickname, {
size: 40,
fill: 0xFFFFFF
});
nicknameText.anchor.set(0.5, 0.5);
nicknameText.x = 1024;
nicknameText.y = 500;
game.addChild(nicknameText);
var joinRoomButton = new Button("JOIN ROOM", function () {
joinMultiplayerRoom();
});
joinRoomButton.x = 1024;
joinRoomButton.y = 700;
game.addChild(joinRoomButton);
var createRoomButton = new Button("CREATE ROOM", function () {
createMultiplayerRoom();
});
createRoomButton.x = 1024;
createRoomButton.y = 820;
game.addChild(createRoomButton);
var backButton = new Button("BACK", function () {
showMenu();
});
backButton.x = 1024;
backButton.y = 940;
game.addChild(backButton);
}
function createMultiplayerRoom() {
if (multiplayer && multiplayer.createRoom) {
multiplayer.createRoom({
maxPlayers: 4,
gameType: "racing"
}, function (room) {
multiplayerRoom = room;
setupMultiplayerEvents();
showCharacterSelect();
});
} else {
console.log("Multiplayer not available");
showCharacterSelect();
}
}
function joinMultiplayerRoom() {
if (multiplayer && multiplayer.joinRoom) {
multiplayer.joinRoom({
gameType: "racing"
}, function (room) {
multiplayerRoom = room;
setupMultiplayerEvents();
showCharacterSelect();
});
} else {
console.log("Multiplayer not available");
showCharacterSelect();
}
}
function setupMultiplayerEvents() {
if (multiplayer && multiplayer.on) {
multiplayer.on('playerJoined', function (playerData) {
console.log('Player joined:', playerData.nickname);
});
multiplayer.on('playerLeft', function (playerData) {
console.log('Player left:', playerData.nickname);
// Remove player from game
for (var i = otherPlayers.length - 1; i >= 0; i--) {
if (otherPlayers[i].playerId === playerData.id) {
if (otherPlayers[i].parent) {
otherPlayers[i].destroy();
}
otherPlayers.splice(i, 1);
break;
}
}
});
multiplayer.on('gameData', function (data) {
handleMultiplayerData(data);
});
multiplayer.on('raceStart', function (data) {
if (gameState === 'character_select') {
startMultiplayerRace();
}
});
}
}
function handleMultiplayerData(data) {
if (data.type === 'playerPosition' && gameState === 'multiplayer_racing') {
updateOtherPlayerPosition(data);
} else if (data.type === 'playerFinished') {
handlePlayerFinished(data);
}
}
function updateOtherPlayerPosition(data) {
var existingPlayer = null;
for (var i = 0; i < otherPlayers.length; i++) {
if (otherPlayers[i].playerId === data.playerId) {
existingPlayer = otherPlayers[i];
break;
}
}
if (!existingPlayer) {
existingPlayer = new Character(data.character);
existingPlayer.playerId = data.playerId;
existingPlayer.nickname = data.nickname;
otherPlayers.push(existingPlayer);
game.addChild(existingPlayer);
}
if (existingPlayer && existingPlayer.parent) {
existingPlayer.x = data.x;
existingPlayer.y = data.y;
existingPlayer.direction = data.direction;
if (existingPlayer.children[0]) {
existingPlayer.children[0].rotation = data.direction;
}
}
}
function handlePlayerFinished(data) {
playersFinished.push({
playerId: data.playerId,
nickname: data.nickname,
time: data.time,
character: data.character
});
}
function startRace() {
gameState = 'racing';
game.removeChildren();
// Add track background
var trackBg = LK.getAsset('track_background_' + selectedTrack, {
anchorX: 0.5,
anchorY: 0.5
});
trackBg.x = 1024;
trackBg.y = 1366;
game.addChild(trackBg);
// Create player
player = new Character(selectedCharacter);
player.x = 1024;
player.y = 2400;
game.addChild(player);
// Create track elements
createTrack();
createObstacles();
// Start race timer
startTime = Date.now();
lapTime = 0;
raceDistance = 0;
playerLives = 3;
// Play race music
LK.playMusic('race_music');
storage.gamesPlayed++;
// Create control buttons
createControlButtons();
}
function startMultiplayerRace() {
gameState = 'multiplayer_racing';
game.removeChildren();
// Add track background
var trackBg = LK.getAsset('track_background_' + selectedTrack, {
anchorX: 0.5,
anchorY: 0.5
});
trackBg.x = 1024;
trackBg.y = 1366;
game.addChild(trackBg);
// Create player
player = new Character(selectedCharacter);
player.x = 1024;
player.y = 2400;
player.playerId = multiplayer && multiplayer.getPlayerId ? multiplayer.getPlayerId() : 'player_' + Date.now();
player.nickname = playerNickname;
game.addChild(player);
// Create track elements
createTrack();
createObstacles();
// Start race timer
startTime = Date.now();
lapTime = 0;
raceDistance = 0;
playersFinished = [];
playerLives = 3;
// Play race music
LK.playMusic('race_music');
storage.gamesPlayed++;
// Create control buttons
createControlButtons();
// Send race start signal
if (multiplayer && multiplayer.sendData) {
multiplayer.sendData({
type: 'raceStarted'
});
}
}
function createTrack() {
// Create track-specific walls based on selected track
if (selectedTrack === 1) {
// Forest Track - winding path with trees
createForestTrack();
} else if (selectedTrack === 2) {
// Desert Track - canyon walls
createDesertTrack();
} else if (selectedTrack === 3) {
// Night Track - city buildings
createNightTrack();
}
// Create finish line
var finishLine = LK.getAsset('finish_line', {
anchorX: 0.5,
anchorY: 0.5
});
finishLine.x = 1024;
finishLine.y = 100;
game.addChild(finishLine);
}
function createForestTrack() {
// Forest track with edge walls only - center is completely open
var trackSections = [{
leftX: 0,
rightX: 2048,
y: 0
}, {
leftX: 0,
rightX: 2048,
y: 200
}, {
leftX: 0,
rightX: 2048,
y: 400
}, {
leftX: 0,
rightX: 2048,
y: 600
}, {
leftX: 0,
rightX: 2048,
y: 800
}, {
leftX: 0,
rightX: 2048,
y: 1000
}, {
leftX: 0,
rightX: 2048,
y: 1200
}, {
leftX: 0,
rightX: 2048,
y: 1400
}, {
leftX: 0,
rightX: 2048,
y: 1600
}, {
leftX: 0,
rightX: 2048,
y: 1800
}, {
leftX: 0,
rightX: 2048,
y: 2000
}, {
leftX: 0,
rightX: 2048,
y: 2200
}, {
leftX: 0,
rightX: 2048,
y: 2400
}];
for (var i = 0; i < trackSections.length; i++) {
var section = trackSections[i];
var leftWall = LK.getAsset('track_wall', {
anchorX: 0.5,
anchorY: 0.5
});
leftWall.x = section.leftX;
leftWall.y = section.y;
leftWall.tint = 0x228B22; // Forest green
game.addChild(leftWall);
var rightWall = LK.getAsset('track_wall', {
anchorX: 0.5,
anchorY: 0.5
});
rightWall.x = section.rightX;
rightWall.y = section.y;
rightWall.tint = 0x228B22; // Forest green
game.addChild(rightWall);
}
}
function createDesertTrack() {
// Desert track with edge walls only - center is completely open
var trackSections = [{
leftX: 0,
rightX: 2048,
y: 0
}, {
leftX: 0,
rightX: 2048,
y: 200
}, {
leftX: 0,
rightX: 2048,
y: 400
}, {
leftX: 0,
rightX: 2048,
y: 600
}, {
leftX: 0,
rightX: 2048,
y: 800
}, {
leftX: 0,
rightX: 2048,
y: 1000
}, {
leftX: 0,
rightX: 2048,
y: 1200
}, {
leftX: 0,
rightX: 2048,
y: 1400
}, {
leftX: 0,
rightX: 2048,
y: 1600
}, {
leftX: 0,
rightX: 2048,
y: 1800
}, {
leftX: 0,
rightX: 2048,
y: 2000
}, {
leftX: 0,
rightX: 2048,
y: 2200
}, {
leftX: 0,
rightX: 2048,
y: 2400
}];
for (var i = 0; i < trackSections.length; i++) {
var section = trackSections[i];
var leftWall = LK.getAsset('track_wall', {
anchorX: 0.5,
anchorY: 0.5
});
leftWall.x = section.leftX;
leftWall.y = section.y;
leftWall.tint = 0xDEB887; // Sandy brown
leftWall.scaleY = 1.5; // Taller canyon walls
game.addChild(leftWall);
var rightWall = LK.getAsset('track_wall', {
anchorX: 0.5,
anchorY: 0.5
});
rightWall.x = section.rightX;
rightWall.y = section.y;
rightWall.tint = 0xDEB887; // Sandy brown
rightWall.scaleY = 1.5; // Taller canyon walls
game.addChild(rightWall);
}
}
function createNightTrack() {
// Night track with edge walls only - center is completely open
var trackSections = [{
leftX: 0,
rightX: 2048,
y: 0
}, {
leftX: 0,
rightX: 2048,
y: 200
}, {
leftX: 0,
rightX: 2048,
y: 400
}, {
leftX: 0,
rightX: 2048,
y: 600
}, {
leftX: 0,
rightX: 2048,
y: 800
}, {
leftX: 0,
rightX: 2048,
y: 1000
}, {
leftX: 0,
rightX: 2048,
y: 1200
}, {
leftX: 0,
rightX: 2048,
y: 1400
}, {
leftX: 0,
rightX: 2048,
y: 1600
}, {
leftX: 0,
rightX: 2048,
y: 1800
}, {
leftX: 0,
rightX: 2048,
y: 2000
}, {
leftX: 0,
rightX: 2048,
y: 2200
}, {
leftX: 0,
rightX: 2048,
y: 2400
}];
for (var i = 0; i < trackSections.length; i++) {
var section = trackSections[i];
var leftWall = LK.getAsset('track_wall', {
anchorX: 0.5,
anchorY: 0.5
});
leftWall.x = section.leftX;
leftWall.y = section.y;
leftWall.tint = 0x2F4F4F; // Dark slate gray
leftWall.scaleY = 2; // Tall building walls
game.addChild(leftWall);
var rightWall = LK.getAsset('track_wall', {
anchorX: 0.5,
anchorY: 0.5
});
rightWall.x = section.rightX;
rightWall.y = section.y;
rightWall.tint = 0x2F4F4F; // Dark slate gray
rightWall.scaleY = 2; // Tall building walls
game.addChild(rightWall);
}
}
function createObstacles() {
obstacles = [];
var obstacleTypes = ['endoskeleton', 'broken_head', 'wrench', 'knife'];
// Increase number of obstacles from 15 to 25
for (var i = 0; i < 25; i++) {
var type = obstacleTypes[Math.floor(Math.random() * obstacleTypes.length)];
var x = 200 + Math.random() * 1648;
var y = 200 + Math.random() * 2200;
var obstacle = new Obstacle(type, x, y);
obstacles.push(obstacle);
game.addChild(obstacle);
}
// Add additional clusters of obstacles in strategic locations
// First cluster - early section
for (var i = 0; i < 5; i++) {
var type = obstacleTypes[Math.floor(Math.random() * obstacleTypes.length)];
var x = 400 + Math.random() * 1248; // More centered placement
var y = 1800 + Math.random() * 300; // Near starting area
var obstacle = new Obstacle(type, x, y);
obstacles.push(obstacle);
game.addChild(obstacle);
}
// Second cluster - middle section
for (var i = 0; i < 5; i++) {
var type = obstacleTypes[Math.floor(Math.random() * obstacleTypes.length)];
var x = 300 + Math.random() * 1448;
var y = 1200 + Math.random() * 400; // Middle section
var obstacle = new Obstacle(type, x, y);
obstacles.push(obstacle);
game.addChild(obstacle);
}
// Third cluster - near finish line
for (var i = 0; i < 5; i++) {
var type = obstacleTypes[Math.floor(Math.random() * obstacleTypes.length)];
var x = 500 + Math.random() * 1048;
var y = 300 + Math.random() * 200; // Near finish line
var obstacle = new Obstacle(type, x, y);
obstacles.push(obstacle);
game.addChild(obstacle);
}
}
function checkCollisions() {
if (!player || !player.parent) return;
try {
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (obstacle && obstacle.parent && player.intersects && player.intersects(obstacle)) {
// Crash effect
if (LK.getSound) {
LK.getSound('crash').play();
}
if (LK.effects && LK.effects.flashObject) {
LK.effects.flashObject(player, 0xFF0000, 500);
}
// Reduce lives
playerLives--;
// Slow down player
player.speed *= 0.5;
// Move obstacle away
obstacle.x = -1000;
obstacle.y = -1000;
// Check if game over
if (playerLives <= 0) {
LK.setTimeout(function () {
LK.showGameOver();
}, 500);
}
}
}
} catch (e) {
console.log("Error in collision detection:", e);
}
}
function updateRace() {
if (!player || !player.parent) return;
// Update race distance based on forward progress
var progress = (2400 - player.y) / 2300;
if (progress > raceDistance / totalRaceDistance) {
raceDistance = progress * totalRaceDistance;
}
// Check if race is finished
if (player.y <= 100) {
finishRace();
}
// Update UI
lapTime = Date.now() - startTime;
try {
if (timeText && timeText.parent && timeText.setText) {
timeText.setText("Time: " + (lapTime / 1000).toFixed(2) + "s");
}
if (speedText && speedText.parent && speedText.setText) {
speedText.setText("Speed: " + Math.floor(player.speed * 10));
}
if (progressText && progressText.parent && progressText.setText) {
progressText.setText("Progress: " + Math.floor(raceDistance / totalRaceDistance * 100) + "%");
}
if (livesText && livesText.parent && livesText.setText) {
livesText.setText("Lives: " + playerLives);
}
} catch (e) {
console.log("Error updating UI:", e);
}
}
function finishRace() {
gameState = 'results';
// Stop music
LK.stopMusic();
// Check for new best time
var isNewBest = lapTime < bestTime;
if (isNewBest) {
bestTime = lapTime;
storage.bestTime = bestTime;
storage.wins++;
}
// Check for unlock conditions
if (lapTime < 30000 && !storage.endoUnlocked) {
// Under 30 seconds
storage.endoUnlocked = true;
LK.effects.flashScreen(0x00FF00, 1000);
}
if (storage.wins >= 5 && !storage.goldenFreddyUnlocked) {
storage.goldenFreddyUnlocked = true;
LK.effects.flashScreen(0xDAA520, 1000);
}
// Show results
LK.setTimeout(function () {
showResults(isNewBest);
}, 1000);
}
function updateMultiplayerRace() {
if (!player || !player.parent) return;
// Update race distance based on forward progress
var progress = (2400 - player.y) / 2300;
if (progress > raceDistance / totalRaceDistance) {
raceDistance = progress * totalRaceDistance;
}
// Check if race is finished
if (player.y <= 100) {
finishMultiplayerRace();
}
// Update UI
lapTime = Date.now() - startTime;
try {
if (timeText && timeText.parent && timeText.setText) {
timeText.setText("Time: " + (lapTime / 1000).toFixed(2) + "s");
}
if (speedText && speedText.parent && speedText.setText) {
speedText.setText("Speed: " + Math.floor(player.speed * 10));
}
if (progressText && progressText.parent && progressText.setText) {
progressText.setText("Progress: " + Math.floor(raceDistance / totalRaceDistance * 100) + "%");
}
if (livesText && livesText.parent && livesText.setText) {
livesText.setText("Lives: " + playerLives);
}
} catch (e) {
console.log("Error updating multiplayer UI:", e);
}
}
function finishMultiplayerRace() {
// Stop music
LK.stopMusic();
// Send finish data to other players
if (multiplayer && multiplayer.sendData) {
multiplayer.sendData({
type: 'playerFinished',
playerId: player.playerId,
nickname: player.nickname,
time: lapTime,
character: selectedCharacter
});
}
// Add self to finished list
playersFinished.push({
playerId: player.playerId,
nickname: player.nickname,
time: lapTime,
character: selectedCharacter
});
// Check for new best time
var isNewBest = lapTime < bestTime;
if (isNewBest) {
bestTime = lapTime;
storage.bestTime = bestTime;
storage.wins++;
}
// Check for unlock conditions
if (lapTime < 30000 && !storage.endoUnlocked) {
storage.endoUnlocked = true;
LK.effects.flashScreen(0x00FF00, 1000);
}
if (storage.wins >= 5 && !storage.goldenFreddyUnlocked) {
storage.goldenFreddyUnlocked = true;
LK.effects.flashScreen(0xDAA520, 1000);
}
// Show results after delay
LK.setTimeout(function () {
showMultiplayerResults(isNewBest);
}, 1000);
}
function showMultiplayerResults(isNewBest) {
game.removeChildren();
var resultsText = new Text2("RACE COMPLETE!", {
size: 60,
fill: 0xFFD700
});
resultsText.anchor.set(0.5, 0.5);
resultsText.x = 1024;
resultsText.y = 200;
game.addChild(resultsText);
// Sort players by finish time
playersFinished.sort(function (a, b) {
return a.time - b.time;
});
// Display rankings
for (var i = 0; i < Math.min(playersFinished.length, 4); i++) {
var playerData = playersFinished[i];
var position = i + 1;
var rankText = new Text2(position + ". " + playerData.nickname + " - " + (playerData.time / 1000).toFixed(2) + "s", {
size: 40,
fill: position === 1 ? 0xFFD700 : 0xFFFFFF
});
rankText.anchor.set(0.5, 0.5);
rankText.x = 1024;
rankText.y = 400 + i * 80;
game.addChild(rankText);
}
if (isNewBest) {
var newBestText = new Text2("NEW PERSONAL BEST!", {
size: 40,
fill: 0x00FF00
});
newBestText.anchor.set(0.5, 0.5);
newBestText.x = 1024;
newBestText.y = 800;
game.addChild(newBestText);
}
var playAgainButton = new Button("RACE AGAIN", function () {
showCharacterSelect();
});
playAgainButton.x = 1024;
playAgainButton.y = 1000;
game.addChild(playAgainButton);
var menuButton = new Button("MAIN MENU", function () {
if (multiplayerRoom && multiplayer && multiplayer.leaveRoom) {
multiplayer.leaveRoom();
multiplayerRoom = null;
}
showMenu();
});
menuButton.x = 1024;
menuButton.y = 1120;
game.addChild(menuButton);
}
function createControlButtons() {
try {
// Create accelerate button
accelerateButton = new Button("ACCELERATE", function () {});
accelerateButton.x = 1536;
accelerateButton.y = 2500;
accelerateButton.scaleX = 0.8;
accelerateButton.scaleY = 0.8;
accelerateButton.alpha = 0.8;
game.addChild(accelerateButton);
// Override button events for continuous press
accelerateButton.down = function () {
isAccelerating = true;
if (LK.getSound) LK.getSound('select').play();
};
accelerateButton.up = function () {
isAccelerating = false;
};
// Create brake button
brakeButton = new Button("BRAKE", function () {});
brakeButton.x = 512;
brakeButton.y = 2500;
brakeButton.scaleX = 0.8;
brakeButton.scaleY = 0.8;
brakeButton.alpha = 0.8;
game.addChild(brakeButton);
brakeButton.down = function () {
isBraking = true;
if (LK.getSound) LK.getSound('select').play();
};
brakeButton.up = function () {
isBraking = false;
};
// Create left turn button
leftButton = new Button("LEFT", function () {});
leftButton.x = 300;
leftButton.y = 2200;
leftButton.scaleX = 0.7;
leftButton.scaleY = 0.7;
leftButton.alpha = 0.8;
game.addChild(leftButton);
leftButton.down = function () {
isTurningLeft = true;
if (LK.getSound) LK.getSound('select').play();
};
leftButton.up = function () {
isTurningLeft = false;
};
// Create right turn button
rightButton = new Button("RIGHT", function () {});
rightButton.x = 1748;
rightButton.y = 2200;
rightButton.scaleX = 0.7;
rightButton.scaleY = 0.7;
rightButton.alpha = 0.8;
game.addChild(rightButton);
rightButton.down = function () {
isTurningRight = true;
if (LK.getSound) LK.getSound('select').play();
};
rightButton.up = function () {
isTurningRight = false;
};
} catch (e) {
console.log("Error creating control buttons:", e);
}
}
function showResults(isNewBest) {
game.removeChildren();
var resultsText = new Text2("RACE COMPLETE!", {
size: 60,
fill: 0xFFD700
});
resultsText.anchor.set(0.5, 0.5);
resultsText.x = 1024;
resultsText.y = 400;
game.addChild(resultsText);
var timeResultText = new Text2("Your Time: " + (lapTime / 1000).toFixed(2) + "s", {
size: 50,
fill: isNewBest ? "#00FF00" : "#FFFFFF"
});
timeResultText.anchor.set(0.5, 0.5);
timeResultText.x = 1024;
timeResultText.y = 600;
game.addChild(timeResultText);
if (isNewBest) {
var newBestText = new Text2("NEW BEST TIME!", {
size: 40,
fill: 0x00FF00
});
newBestText.anchor.set(0.5, 0.5);
newBestText.x = 1024;
newBestText.y = 700;
game.addChild(newBestText);
}
var playAgainButton = new Button("RACE AGAIN", function () {
showCharacterSelect();
});
playAgainButton.x = 1024;
playAgainButton.y = 900;
game.addChild(playAgainButton);
var menuButton = new Button("MAIN MENU", function () {
showMenu();
});
menuButton.x = 1024;
menuButton.y = 1020;
game.addChild(menuButton);
}
// Touch controls
var isAccelerating = false;
var isBraking = false;
var isTurningLeft = false;
var isTurningRight = false;
// Control buttons
var accelerateButton = null;
var brakeButton = null;
var leftButton = null;
var rightButton = null;
game.down = function (x, y, obj) {
if (gameState !== 'racing' && gameState !== 'multiplayer_racing' || !player) return;
// Divide screen into control zones
if (y > 2000) {
// Bottom area for acceleration/braking
if (x < 1024) {
isBraking = true;
} else {
isAccelerating = true;
}
} else {
// Upper area for steering
if (x < 1024) {
isTurningLeft = true;
} else {
isTurningRight = true;
}
}
};
game.up = function (x, y, obj) {
// Reset all controls on any touch release
isAccelerating = false;
isBraking = false;
isTurningLeft = false;
isTurningRight = false;
};
game.update = function () {
if ((gameState === 'racing' || gameState === 'multiplayer_racing') && player && player.parent) {
// Validate player object exists and is properly attached
try {
// Handle controls
if (isAccelerating) {
player.moveForward();
if (LK.ticks % 20 === 0) {
LK.getSound('engine').play();
}
} else if (isBraking) {
player.moveBackward();
} else {
player.applyDeceleration();
}
if (isTurningLeft) {
player.turnLeft();
}
if (isTurningRight) {
player.turnRight();
}
checkCollisions();
if (gameState === 'multiplayer_racing') {
updateMultiplayerRace();
// Send player position every few frames
if (LK.ticks % 3 === 0 && multiplayer && multiplayer.sendData) {
multiplayer.sendData({
type: 'playerPosition',
playerId: player.playerId,
nickname: player.nickname,
character: selectedCharacter,
x: player.x,
y: player.y,
direction: player.direction
});
}
} else {
updateRace();
}
} catch (e) {
console.log("Error in game update:", e);
}
}
};
// Initialize game
showMenu();
Fullscreen modern App Store landscape banner, 16:9, high definition, for a game titled "Freddy's Racing Championship" and with the description "Kart racing game featuring FNAF animatronic characters racing through 80s-themed tracks with unique obstacles and unlockable characters.". No text on banner!
Un conejo morado animatronico en un kart. In-Game asset. No shadows
Un oso dorado animatronico en un kart. In-Game asset. High contrast. No shadows
Un endoesqueleto animatronico en un kart. In-Game asset. 2d. High contrast. No shadows
Un pollo amarillo animatronico en un kart. In-Game asset. 2d. High contrast. No shadows
Un oso animatronico en un kart. In-Game asset. 2d. High contrast. No shadows
Un zorro rojo animatronico en un kart. In-Game asset. 2d. High contrast. No shadows
Mascara de Freddy Fazbear. In-Game asset. 2d. High contrast. No shadows
Llave inglesa. In-Game asset. 2d. High contrast. No shadows
Endoesqueleto animatronico. In-Game asset. 2d. High contrast. No shadows
Línea de meta. In-Game asset. 2d. High contrast. No shadows
Cuchillo con sangre. In-Game asset. 2d. High contrast. No shadows
Pared de ladrillos. In-Game asset. 2d. High contrast. No shadows
Botón rojo. In-Game asset. 2d. High contrast. No shadows
Imagen de bandera de carrera. In-Game asset. 2d. High contrast. No shadows
Pista de carreras desde arriba. In-Game asset. 2d. High contrast. No shadows