Code edit (1 edits merged)
Please save this source code
User prompt
Drag Race Legends
Initial prompt
i want you to make a 2d drag race game. The game will have a sign up page at first and then user will enter the game. For the second page there will be "race" button that leads the user to the drag race menu, "garage" button that leads the user to the personal garage where the user keeps his car or cars at (there is 10 car limit in the garage and only 2 spot will be open at first. Users needs to buy car spots one by one with the price of 5 crystals), "car gallery" button that leads the user to the car gallery to buy cars, and there will be a "shop" button that lead the user to the shop where users can buy engines with different hp and weight, turbos with different +hp, psi and weight, intakes with more hp, tires with more grip, spray paints for the car with the currency of cash. In "race" menu user will race with the car that user chooses that user owns in their garage. the race will be 500 meter long. The cars speed depends on how much hp it carries.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { garage: [], crystals: 10, cash: 5000, garageSlots: 2 }); /**** * Classes ****/ // Car class var Car = Container.expand(function () { var self = Container.call(this); // Car stats self.stats = { horsepower: 100, weight: 1200, grip: 1, turbo: 0, intake: 0, tires: 1, paint: 0x3333cc }; // Car parts (for upgrades) self.parts = { engine: 1, turbo: 0, intake: 0, tires: 1 }; // Cosmetic self.paint = 0x3333cc; // Attach car body var body = self.attachAsset('carBody', { anchorX: 0.5, anchorY: 0.5, tint: self.paint }); // Attach wheels var wheel1 = self.attachAsset('carWheel', { anchorX: 0.5, anchorY: 0.5, x: -120, y: 50 }); var wheel2 = self.attachAsset('carWheel', { anchorX: 0.5, anchorY: 0.5, x: 120, y: 50 }); // For race animation self.raceProgress = 0; // 0 to 1 self.isRacing = false; self.raceSpeed = 0; // Set car data self.setData = function (data) { if (!data) return; self.stats = data.stats; self.parts = data.parts; self.paint = data.paint || 0x3333cc; body.tint = self.paint; }; // Get car data for storage self.getData = function () { return { stats: self.stats, parts: self.parts, paint: self.paint }; }; // Start race self.startRace = function (speed) { self.isRacing = true; self.raceProgress = 0; self.raceSpeed = speed; }; // Update per tick self.update = function () { if (self.isRacing) { self.raceProgress += self.raceSpeed; if (self.raceProgress > 1) self.raceProgress = 1; } }; return self; }); // Shop part class var ShopPart = Container.expand(function () { var self = Container.call(this); var icon = self.attachAsset('partIcon', { anchorX: 0.5, anchorY: 0.5 }); self.textObj = new Text2('', { size: 32, fill: "#222" }); self.textObj.anchor.set(0.5, 0.5); self.textObj.y = 60; self.addChild(self.textObj); self.setText = function (txt) { self.textObj.setText(txt); }; self.down = function (x, y, obj) { if (self.onBuy) self.onBuy(); }; return self; }); // Button class var UIButton = Container.expand(function () { var self = Container.call(this); var bg = self.attachAsset('button', { anchorX: 0.5, anchorY: 0.5 }); self.textObj = new Text2('', { size: 60, fill: "#fff" }); self.textObj.anchor.set(0.5, 0.5); self.addChild(self.textObj); self.setText = function (txt) { self.textObj.setText(txt); }; self.setColor = function (color) { bg.tint = color; }; self.down = function (x, y, obj) { if (self.onClick) self.onClick(); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181818 }); /**** * Game Code ****/ // Shop part icon // Button // Garage slot background // Finish line // Start line // Race track // Car wheel // Car body (default) // --- Global State --- var GAME_STATE = { MAIN_MENU: 0, GARAGE: 1, RACE: 2, SHOP: 3 }; var state = GAME_STATE.MAIN_MENU; // Player data var garage = storage.garage && storage.garage.length ? storage.garage : []; var crystals = typeof storage.crystals === 'number' ? storage.crystals : 10; var cash = typeof storage.cash === 'number' ? storage.cash : 5000; var garageSlots = typeof storage.garageSlots === 'number' ? storage.garageSlots : 2; // UI elements var mainMenuUI = []; var garageUI = []; var raceUI = []; var shopUI = []; var infoText = null; var currencyText = null; // Race state var raceCar = null; var raceOpponent = null; var raceTrack = null; var raceStartLine = null; var raceFinishLine = null; var raceDistance = 500; // meters var raceStarted = false; var raceEnded = false; var raceTimer = 0; var raceResultText = null; // --- Utility Functions --- function savePlayerData() { storage.garage = garage; storage.crystals = crystals; storage.cash = cash; storage.garageSlots = garageSlots; } function clearUI(uiArr) { for (var i = 0; i < uiArr.length; i++) { if (uiArr[i].parent) uiArr[i].parent.removeChild(uiArr[i]); } uiArr.length = 0; } function showInfo(msg, duration) { if (!infoText) { infoText = new Text2('', { size: 60, fill: "#fff" }); infoText.anchor.set(0.5, 0); LK.gui.top.addChild(infoText); } infoText.setText(msg); infoText.visible = true; if (duration) { LK.setTimeout(function () { if (infoText) infoText.visible = false; }, duration); } } function updateCurrencyText() { if (!currencyText) { currencyText = new Text2('', { size: 48, fill: "#fff" }); currencyText.anchor.set(1, 0); currencyText.x = LK.gui.width - 40; currencyText.y = 10; LK.gui.top.addChild(currencyText); } currencyText.setText("💎" + crystals + " $" + cash); } // --- Main Menu --- function showMainMenu() { state = GAME_STATE.MAIN_MENU; clearUI(garageUI); clearUI(raceUI); clearUI(shopUI); // Title var title = new Text2('Drag Race Legends', { size: 120, fill: 0xFFD700 }); title.anchor.set(0.5, 0); title.x = 2048 / 2; title.y = 180; LK.gui.top.addChild(title); mainMenuUI.push(title); // Play button var playBtn = new UIButton(); playBtn.x = 2048 / 2; playBtn.y = 600; playBtn.setText("Race"); playBtn.onClick = function () { showGarage(); }; LK.gui.center.addChild(playBtn); mainMenuUI.push(playBtn); // Garage button var garageBtn = new UIButton(); garageBtn.x = 2048 / 2; garageBtn.y = 800; garageBtn.setText("Garage"); garageBtn.onClick = function () { showGarage(); }; LK.gui.center.addChild(garageBtn); mainMenuUI.push(garageBtn); // Shop button var shopBtn = new UIButton(); shopBtn.x = 2048 / 2; shopBtn.y = 1000; shopBtn.setText("Shop"); shopBtn.onClick = function () { showShop(); }; LK.gui.center.addChild(shopBtn); mainMenuUI.push(shopBtn); updateCurrencyText(); } function clearMainMenu() { clearUI(mainMenuUI); } // --- Garage --- function showGarage() { state = GAME_STATE.GARAGE; clearMainMenu(); clearUI(raceUI); clearUI(shopUI); // Title var title = new Text2('Garage', { size: 100, fill: 0xFFD700 }); title.anchor.set(0.5, 0); title.x = 2048 / 2; title.y = 120; LK.gui.top.addChild(title); garageUI.push(title); // Show car slots var slotY = 350; var slotGap = 200; for (var i = 0; i < garageSlots; i++) { var slotBg = LK.getAsset('garageSlot', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: slotY + i * slotGap }); LK.gui.center.addChild(slotBg); garageUI.push(slotBg); if (garage[i]) { // Show car var car = new Car(); car.setData(garage[i]); car.x = 2048 / 2; car.y = slotY + i * slotGap; car.scaleX = 0.7; car.scaleY = 0.7; LK.gui.center.addChild(car); garageUI.push(car); // Show stats var statTxt = new Text2("HP: " + car.stats.horsepower + " Turbo: " + car.parts.turbo + " Tires: " + car.parts.tires, { size: 36, fill: "#fff" }); statTxt.anchor.set(0.5, 0); statTxt.x = 2048 / 2; statTxt.y = slotY + i * slotGap + 80; LK.gui.center.addChild(statTxt); garageUI.push(statTxt); // Select for race button (function (carIdx) { var selectBtn = new UIButton(); selectBtn.x = 2048 / 2 + 300; selectBtn.y = slotY + i * slotGap; selectBtn.setText("Race"); selectBtn.setColor(0x00cc44); selectBtn.onClick = function () { startRaceWithCar(carIdx); }; LK.gui.center.addChild(selectBtn); garageUI.push(selectBtn); })(i); // Upgrade button (function (carIdx) { var upgradeBtn = new UIButton(); upgradeBtn.x = 2048 / 2 - 300; upgradeBtn.y = slotY + i * slotGap; upgradeBtn.setText("Upgrade"); upgradeBtn.setColor(0x0080cc); upgradeBtn.onClick = function () { showShop(carIdx); }; LK.gui.center.addChild(upgradeBtn); garageUI.push(upgradeBtn); })(i); } else { // Empty slot var addBtn = new UIButton(); addBtn.x = 2048 / 2; addBtn.y = slotY + i * slotGap; addBtn.setText("Buy Car ($2000)"); addBtn.setColor(0xcccc00); addBtn.onClick = function () { if (cash >= 2000) { cash -= 2000; var newCar = { stats: { horsepower: 100, weight: 1200, grip: 1, turbo: 0, intake: 0, tires: 1, paint: 0x3333cc }, parts: { engine: 1, turbo: 0, intake: 0, tires: 1 }, paint: 0x3333cc }; garage.push(newCar); savePlayerData(); showGarage(); } else { showInfo("Not enough cash!", 1200); } }; LK.gui.center.addChild(addBtn); garageUI.push(addBtn); } } // Add slot button (if < 10) if (garageSlots < 10) { var addSlotBtn = new UIButton(); addSlotBtn.x = 2048 / 2; addSlotBtn.y = slotY + garageSlots * slotGap + 40; addSlotBtn.setText("Add Slot (5💎)"); addSlotBtn.setColor(0x00cccc); addSlotBtn.onClick = function () { if (crystals >= 5) { crystals -= 5; garageSlots += 1; savePlayerData(); showGarage(); } else { showInfo("Not enough crystals!", 1200); } }; LK.gui.center.addChild(addSlotBtn); garageUI.push(addSlotBtn); } // Back button var backBtn = new UIButton(); backBtn.x = 2048 / 2; backBtn.y = 2600; backBtn.setText("Back"); backBtn.onClick = function () { showMainMenu(); }; LK.gui.bottom.addChild(backBtn); garageUI.push(backBtn); updateCurrencyText(); } // --- Shop --- function showShop(carIdx) { state = GAME_STATE.SHOP; clearMainMenu(); clearUI(garageUI); clearUI(raceUI); clearUI(shopUI); // Title var title = new Text2('Shop', { size: 100, fill: 0xFFD700 }); title.anchor.set(0.5, 0); title.x = 2048 / 2; title.y = 120; LK.gui.top.addChild(title); shopUI.push(title); // If carIdx is set, show upgrades for that car var shopY = 400; var shopGap = 220; var shopParts = [{ name: "Engine +20HP", stat: "horsepower", cost: 1000, part: "engine", value: 20 }, { name: "Turbo +30HP", stat: "turbo", cost: 1500, part: "turbo", value: 1 }, { name: "Intake +10HP", stat: "intake", cost: 800, part: "intake", value: 1 }, { name: "Tires +Grip", stat: "tires", cost: 600, part: "tires", value: 1 }, { name: "Spray Paint", stat: "paint", cost: 2, part: "paint", value: 0, isCrystal: true }]; for (var i = 0; i < shopParts.length; i++) { (function (part, idx) { var partBtn = new ShopPart(); partBtn.x = 2048 / 2; partBtn.y = shopY + idx * shopGap; partBtn.setText(part.name + " - " + (part.isCrystal ? part.cost + "💎" : "$" + part.cost)); partBtn.onBuy = function () { if (carIdx === undefined) { showInfo("Select a car in Garage to upgrade!", 1200); return; } var car = garage[carIdx]; if (!car) return; if (part.isCrystal) { if (crystals >= part.cost) { crystals -= part.cost; // Random paint var newColor = 0x100000 + Math.floor(Math.random() * 0xEFFFFF); car.paint = newColor; car.stats.paint = newColor; savePlayerData(); showInfo("Paint applied!", 1000); showShop(carIdx); } else { showInfo("Not enough crystals!", 1200); } } else { if (cash >= part.cost) { cash -= part.cost; if (part.stat === "horsepower") { car.stats.horsepower += part.value; car.parts.engine += 1; } else if (part.stat === "turbo") { car.stats.horsepower += 30; car.parts.turbo += 1; } else if (part.stat === "intake") { car.stats.horsepower += 10; car.parts.intake += 1; } else if (part.stat === "tires") { car.stats.grip += 0.2; car.parts.tires += 1; } savePlayerData(); showInfo("Upgrade applied!", 1000); showShop(carIdx); } else { showInfo("Not enough cash!", 1200); } } }; LK.gui.center.addChild(partBtn); shopUI.push(partBtn); })(shopParts[i], i); } // Back button var backBtn = new UIButton(); backBtn.x = 2048 / 2; backBtn.y = 2600; backBtn.setText("Back"); backBtn.onClick = function () { showGarage(); }; LK.gui.bottom.addChild(backBtn); shopUI.push(backBtn); updateCurrencyText(); } // --- Race --- function startRaceWithCar(carIdx) { state = GAME_STATE.RACE; clearMainMenu(); clearUI(garageUI); clearUI(shopUI); clearUI(raceUI); // Set up race track var trackY = 1800; raceTrack = LK.getAsset('track', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: trackY }); game.addChild(raceTrack); raceUI.push(raceTrack); // Start line raceStartLine = LK.getAsset('startLine', { anchorX: 0.5, anchorY: 0.5, x: 400, y: trackY }); game.addChild(raceStartLine); raceUI.push(raceStartLine); // Finish line raceFinishLine = LK.getAsset('finishLine', { anchorX: 0.5, anchorY: 0.5, x: 1800, y: trackY }); game.addChild(raceFinishLine); raceUI.push(raceFinishLine); // Player car raceCar = new Car(); raceCar.setData(garage[carIdx]); raceCar.x = 400; raceCar.y = trackY - 80; raceCar.scaleX = 0.8; raceCar.scaleY = 0.8; game.addChild(raceCar); raceUI.push(raceCar); // Opponent car (random stats) raceOpponent = new Car(); var oppStats = { stats: { horsepower: 90 + Math.floor(Math.random() * 60), weight: 1200, grip: 1 + Math.random() * 0.5, turbo: Math.floor(Math.random() * 2), intake: Math.floor(Math.random() * 2), tires: 1 + Math.floor(Math.random() * 2), paint: 0x880000 + Math.floor(Math.random() * 0x77ffff) }, parts: { engine: 1 + Math.floor(Math.random() * 2), turbo: Math.floor(Math.random() * 2), intake: Math.floor(Math.random() * 2), tires: 1 + Math.floor(Math.random() * 2) }, paint: 0x880000 + Math.floor(Math.random() * 0x77ffff) }; raceOpponent.setData(oppStats); raceOpponent.x = 400; raceOpponent.y = trackY + 80; raceOpponent.scaleX = 0.8; raceOpponent.scaleY = 0.8; game.addChild(raceOpponent); raceUI.push(raceOpponent); // Race state raceStarted = false; raceEnded = false; raceTimer = 0; // Countdown text var countdownText = new Text2('3', { size: 200, fill: "#fff" }); countdownText.anchor.set(0.5, 0.5); countdownText.x = 2048 / 2; countdownText.y = 600; LK.gui.center.addChild(countdownText); raceUI.push(countdownText); // Start countdown var countdown = 3; var countdownInterval = LK.setInterval(function () { countdown -= 1; if (countdown > 0) { countdownText.setText(countdown + ''); } else if (countdown === 0) { countdownText.setText("GO!"); // Start race raceStarted = true; // Calculate speed per tick (distance: 1400px, 500m) var pxPerMeter = 1400 / raceDistance; var carSpeed = raceCar.stats.horsepower / 100 * pxPerMeter / 60; // px per tick var oppSpeed = raceOpponent.stats.horsepower / 100 * pxPerMeter / 60; raceCar.startRace(carSpeed / 1400); // progress per tick raceOpponent.startRace(oppSpeed / 1400); } else { LK.clearInterval(countdownInterval); countdownText.visible = false; } }, 800); // Race timer text var timerText = new Text2('0.00s', { size: 80, fill: "#fff" }); timerText.anchor.set(0.5, 0); timerText.x = 2048 / 2; timerText.y = 100; LK.gui.top.addChild(timerText); raceUI.push(timerText); // Back button (disabled during race) var backBtn = new UIButton(); backBtn.x = 2048 / 2; backBtn.y = 2600; backBtn.setText("Back"); backBtn.onClick = function () { endRace(); showGarage(); }; LK.gui.bottom.addChild(backBtn); raceUI.push(backBtn); // Result text raceResultText = new Text2('', { size: 120, fill: 0xFFD700 }); raceResultText.anchor.set(0.5, 0.5); raceResultText.x = 2048 / 2; raceResultText.y = 900; LK.gui.center.addChild(raceResultText); raceResultText.visible = false; raceUI.push(raceResultText); updateCurrencyText(); // Race update game.update = function () { if (state !== GAME_STATE.RACE) return; if (raceStarted && !raceEnded) { raceTimer += 1 / 60; timerText.setText(raceTimer.toFixed(2) + "s"); // Move cars if (raceCar.isRacing) { raceCar.x = 400 + raceCar.raceProgress * 1400; if (raceCar.raceProgress >= 1) { raceCar.isRacing = false; } } if (raceOpponent.isRacing) { raceOpponent.x = 400 + raceOpponent.raceProgress * 1400; if (raceOpponent.raceProgress >= 1) { raceOpponent.isRacing = false; } } // Check finish if (!raceEnded && (raceCar.raceProgress >= 1 || raceOpponent.raceProgress >= 1)) { raceEnded = true; var playerWin = raceCar.raceProgress >= 1 && raceCar.raceProgress >= raceOpponent.raceProgress; var reward = playerWin ? 1000 : 200; var rewardCrystals = playerWin ? 1 : 0; if (playerWin) { raceResultText.setText("You Win!\n+$" + reward + (rewardCrystals ? " +1💎" : "")); } else { raceResultText.setText("You Lose!\n+$" + reward); } raceResultText.visible = true; cash += reward; crystals += rewardCrystals; savePlayerData(); updateCurrencyText(); LK.setTimeout(function () { endRace(); showGarage(); }, 2200); } } }; } function endRace() { clearUI(raceUI); raceCar = null; raceOpponent = null; raceTrack = null; raceStartLine = null; raceFinishLine = null; raceResultText = null; game.update = function () {}; } // --- Start Game --- showMainMenu();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
garage: [],
crystals: 10,
cash: 5000,
garageSlots: 2
});
/****
* Classes
****/
// Car class
var Car = Container.expand(function () {
var self = Container.call(this);
// Car stats
self.stats = {
horsepower: 100,
weight: 1200,
grip: 1,
turbo: 0,
intake: 0,
tires: 1,
paint: 0x3333cc
};
// Car parts (for upgrades)
self.parts = {
engine: 1,
turbo: 0,
intake: 0,
tires: 1
};
// Cosmetic
self.paint = 0x3333cc;
// Attach car body
var body = self.attachAsset('carBody', {
anchorX: 0.5,
anchorY: 0.5,
tint: self.paint
});
// Attach wheels
var wheel1 = self.attachAsset('carWheel', {
anchorX: 0.5,
anchorY: 0.5,
x: -120,
y: 50
});
var wheel2 = self.attachAsset('carWheel', {
anchorX: 0.5,
anchorY: 0.5,
x: 120,
y: 50
});
// For race animation
self.raceProgress = 0; // 0 to 1
self.isRacing = false;
self.raceSpeed = 0;
// Set car data
self.setData = function (data) {
if (!data) return;
self.stats = data.stats;
self.parts = data.parts;
self.paint = data.paint || 0x3333cc;
body.tint = self.paint;
};
// Get car data for storage
self.getData = function () {
return {
stats: self.stats,
parts: self.parts,
paint: self.paint
};
};
// Start race
self.startRace = function (speed) {
self.isRacing = true;
self.raceProgress = 0;
self.raceSpeed = speed;
};
// Update per tick
self.update = function () {
if (self.isRacing) {
self.raceProgress += self.raceSpeed;
if (self.raceProgress > 1) self.raceProgress = 1;
}
};
return self;
});
// Shop part class
var ShopPart = Container.expand(function () {
var self = Container.call(this);
var icon = self.attachAsset('partIcon', {
anchorX: 0.5,
anchorY: 0.5
});
self.textObj = new Text2('', {
size: 32,
fill: "#222"
});
self.textObj.anchor.set(0.5, 0.5);
self.textObj.y = 60;
self.addChild(self.textObj);
self.setText = function (txt) {
self.textObj.setText(txt);
};
self.down = function (x, y, obj) {
if (self.onBuy) self.onBuy();
};
return self;
});
// Button class
var UIButton = Container.expand(function () {
var self = Container.call(this);
var bg = self.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
self.textObj = new Text2('', {
size: 60,
fill: "#fff"
});
self.textObj.anchor.set(0.5, 0.5);
self.addChild(self.textObj);
self.setText = function (txt) {
self.textObj.setText(txt);
};
self.setColor = function (color) {
bg.tint = color;
};
self.down = function (x, y, obj) {
if (self.onClick) self.onClick();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// Shop part icon
// Button
// Garage slot background
// Finish line
// Start line
// Race track
// Car wheel
// Car body (default)
// --- Global State ---
var GAME_STATE = {
MAIN_MENU: 0,
GARAGE: 1,
RACE: 2,
SHOP: 3
};
var state = GAME_STATE.MAIN_MENU;
// Player data
var garage = storage.garage && storage.garage.length ? storage.garage : [];
var crystals = typeof storage.crystals === 'number' ? storage.crystals : 10;
var cash = typeof storage.cash === 'number' ? storage.cash : 5000;
var garageSlots = typeof storage.garageSlots === 'number' ? storage.garageSlots : 2;
// UI elements
var mainMenuUI = [];
var garageUI = [];
var raceUI = [];
var shopUI = [];
var infoText = null;
var currencyText = null;
// Race state
var raceCar = null;
var raceOpponent = null;
var raceTrack = null;
var raceStartLine = null;
var raceFinishLine = null;
var raceDistance = 500; // meters
var raceStarted = false;
var raceEnded = false;
var raceTimer = 0;
var raceResultText = null;
// --- Utility Functions ---
function savePlayerData() {
storage.garage = garage;
storage.crystals = crystals;
storage.cash = cash;
storage.garageSlots = garageSlots;
}
function clearUI(uiArr) {
for (var i = 0; i < uiArr.length; i++) {
if (uiArr[i].parent) uiArr[i].parent.removeChild(uiArr[i]);
}
uiArr.length = 0;
}
function showInfo(msg, duration) {
if (!infoText) {
infoText = new Text2('', {
size: 60,
fill: "#fff"
});
infoText.anchor.set(0.5, 0);
LK.gui.top.addChild(infoText);
}
infoText.setText(msg);
infoText.visible = true;
if (duration) {
LK.setTimeout(function () {
if (infoText) infoText.visible = false;
}, duration);
}
}
function updateCurrencyText() {
if (!currencyText) {
currencyText = new Text2('', {
size: 48,
fill: "#fff"
});
currencyText.anchor.set(1, 0);
currencyText.x = LK.gui.width - 40;
currencyText.y = 10;
LK.gui.top.addChild(currencyText);
}
currencyText.setText("💎" + crystals + " $" + cash);
}
// --- Main Menu ---
function showMainMenu() {
state = GAME_STATE.MAIN_MENU;
clearUI(garageUI);
clearUI(raceUI);
clearUI(shopUI);
// Title
var title = new Text2('Drag Race Legends', {
size: 120,
fill: 0xFFD700
});
title.anchor.set(0.5, 0);
title.x = 2048 / 2;
title.y = 180;
LK.gui.top.addChild(title);
mainMenuUI.push(title);
// Play button
var playBtn = new UIButton();
playBtn.x = 2048 / 2;
playBtn.y = 600;
playBtn.setText("Race");
playBtn.onClick = function () {
showGarage();
};
LK.gui.center.addChild(playBtn);
mainMenuUI.push(playBtn);
// Garage button
var garageBtn = new UIButton();
garageBtn.x = 2048 / 2;
garageBtn.y = 800;
garageBtn.setText("Garage");
garageBtn.onClick = function () {
showGarage();
};
LK.gui.center.addChild(garageBtn);
mainMenuUI.push(garageBtn);
// Shop button
var shopBtn = new UIButton();
shopBtn.x = 2048 / 2;
shopBtn.y = 1000;
shopBtn.setText("Shop");
shopBtn.onClick = function () {
showShop();
};
LK.gui.center.addChild(shopBtn);
mainMenuUI.push(shopBtn);
updateCurrencyText();
}
function clearMainMenu() {
clearUI(mainMenuUI);
}
// --- Garage ---
function showGarage() {
state = GAME_STATE.GARAGE;
clearMainMenu();
clearUI(raceUI);
clearUI(shopUI);
// Title
var title = new Text2('Garage', {
size: 100,
fill: 0xFFD700
});
title.anchor.set(0.5, 0);
title.x = 2048 / 2;
title.y = 120;
LK.gui.top.addChild(title);
garageUI.push(title);
// Show car slots
var slotY = 350;
var slotGap = 200;
for (var i = 0; i < garageSlots; i++) {
var slotBg = LK.getAsset('garageSlot', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: slotY + i * slotGap
});
LK.gui.center.addChild(slotBg);
garageUI.push(slotBg);
if (garage[i]) {
// Show car
var car = new Car();
car.setData(garage[i]);
car.x = 2048 / 2;
car.y = slotY + i * slotGap;
car.scaleX = 0.7;
car.scaleY = 0.7;
LK.gui.center.addChild(car);
garageUI.push(car);
// Show stats
var statTxt = new Text2("HP: " + car.stats.horsepower + " Turbo: " + car.parts.turbo + " Tires: " + car.parts.tires, {
size: 36,
fill: "#fff"
});
statTxt.anchor.set(0.5, 0);
statTxt.x = 2048 / 2;
statTxt.y = slotY + i * slotGap + 80;
LK.gui.center.addChild(statTxt);
garageUI.push(statTxt);
// Select for race button
(function (carIdx) {
var selectBtn = new UIButton();
selectBtn.x = 2048 / 2 + 300;
selectBtn.y = slotY + i * slotGap;
selectBtn.setText("Race");
selectBtn.setColor(0x00cc44);
selectBtn.onClick = function () {
startRaceWithCar(carIdx);
};
LK.gui.center.addChild(selectBtn);
garageUI.push(selectBtn);
})(i);
// Upgrade button
(function (carIdx) {
var upgradeBtn = new UIButton();
upgradeBtn.x = 2048 / 2 - 300;
upgradeBtn.y = slotY + i * slotGap;
upgradeBtn.setText("Upgrade");
upgradeBtn.setColor(0x0080cc);
upgradeBtn.onClick = function () {
showShop(carIdx);
};
LK.gui.center.addChild(upgradeBtn);
garageUI.push(upgradeBtn);
})(i);
} else {
// Empty slot
var addBtn = new UIButton();
addBtn.x = 2048 / 2;
addBtn.y = slotY + i * slotGap;
addBtn.setText("Buy Car ($2000)");
addBtn.setColor(0xcccc00);
addBtn.onClick = function () {
if (cash >= 2000) {
cash -= 2000;
var newCar = {
stats: {
horsepower: 100,
weight: 1200,
grip: 1,
turbo: 0,
intake: 0,
tires: 1,
paint: 0x3333cc
},
parts: {
engine: 1,
turbo: 0,
intake: 0,
tires: 1
},
paint: 0x3333cc
};
garage.push(newCar);
savePlayerData();
showGarage();
} else {
showInfo("Not enough cash!", 1200);
}
};
LK.gui.center.addChild(addBtn);
garageUI.push(addBtn);
}
}
// Add slot button (if < 10)
if (garageSlots < 10) {
var addSlotBtn = new UIButton();
addSlotBtn.x = 2048 / 2;
addSlotBtn.y = slotY + garageSlots * slotGap + 40;
addSlotBtn.setText("Add Slot (5💎)");
addSlotBtn.setColor(0x00cccc);
addSlotBtn.onClick = function () {
if (crystals >= 5) {
crystals -= 5;
garageSlots += 1;
savePlayerData();
showGarage();
} else {
showInfo("Not enough crystals!", 1200);
}
};
LK.gui.center.addChild(addSlotBtn);
garageUI.push(addSlotBtn);
}
// Back button
var backBtn = new UIButton();
backBtn.x = 2048 / 2;
backBtn.y = 2600;
backBtn.setText("Back");
backBtn.onClick = function () {
showMainMenu();
};
LK.gui.bottom.addChild(backBtn);
garageUI.push(backBtn);
updateCurrencyText();
}
// --- Shop ---
function showShop(carIdx) {
state = GAME_STATE.SHOP;
clearMainMenu();
clearUI(garageUI);
clearUI(raceUI);
clearUI(shopUI);
// Title
var title = new Text2('Shop', {
size: 100,
fill: 0xFFD700
});
title.anchor.set(0.5, 0);
title.x = 2048 / 2;
title.y = 120;
LK.gui.top.addChild(title);
shopUI.push(title);
// If carIdx is set, show upgrades for that car
var shopY = 400;
var shopGap = 220;
var shopParts = [{
name: "Engine +20HP",
stat: "horsepower",
cost: 1000,
part: "engine",
value: 20
}, {
name: "Turbo +30HP",
stat: "turbo",
cost: 1500,
part: "turbo",
value: 1
}, {
name: "Intake +10HP",
stat: "intake",
cost: 800,
part: "intake",
value: 1
}, {
name: "Tires +Grip",
stat: "tires",
cost: 600,
part: "tires",
value: 1
}, {
name: "Spray Paint",
stat: "paint",
cost: 2,
part: "paint",
value: 0,
isCrystal: true
}];
for (var i = 0; i < shopParts.length; i++) {
(function (part, idx) {
var partBtn = new ShopPart();
partBtn.x = 2048 / 2;
partBtn.y = shopY + idx * shopGap;
partBtn.setText(part.name + " - " + (part.isCrystal ? part.cost + "💎" : "$" + part.cost));
partBtn.onBuy = function () {
if (carIdx === undefined) {
showInfo("Select a car in Garage to upgrade!", 1200);
return;
}
var car = garage[carIdx];
if (!car) return;
if (part.isCrystal) {
if (crystals >= part.cost) {
crystals -= part.cost;
// Random paint
var newColor = 0x100000 + Math.floor(Math.random() * 0xEFFFFF);
car.paint = newColor;
car.stats.paint = newColor;
savePlayerData();
showInfo("Paint applied!", 1000);
showShop(carIdx);
} else {
showInfo("Not enough crystals!", 1200);
}
} else {
if (cash >= part.cost) {
cash -= part.cost;
if (part.stat === "horsepower") {
car.stats.horsepower += part.value;
car.parts.engine += 1;
} else if (part.stat === "turbo") {
car.stats.horsepower += 30;
car.parts.turbo += 1;
} else if (part.stat === "intake") {
car.stats.horsepower += 10;
car.parts.intake += 1;
} else if (part.stat === "tires") {
car.stats.grip += 0.2;
car.parts.tires += 1;
}
savePlayerData();
showInfo("Upgrade applied!", 1000);
showShop(carIdx);
} else {
showInfo("Not enough cash!", 1200);
}
}
};
LK.gui.center.addChild(partBtn);
shopUI.push(partBtn);
})(shopParts[i], i);
}
// Back button
var backBtn = new UIButton();
backBtn.x = 2048 / 2;
backBtn.y = 2600;
backBtn.setText("Back");
backBtn.onClick = function () {
showGarage();
};
LK.gui.bottom.addChild(backBtn);
shopUI.push(backBtn);
updateCurrencyText();
}
// --- Race ---
function startRaceWithCar(carIdx) {
state = GAME_STATE.RACE;
clearMainMenu();
clearUI(garageUI);
clearUI(shopUI);
clearUI(raceUI);
// Set up race track
var trackY = 1800;
raceTrack = LK.getAsset('track', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: trackY
});
game.addChild(raceTrack);
raceUI.push(raceTrack);
// Start line
raceStartLine = LK.getAsset('startLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 400,
y: trackY
});
game.addChild(raceStartLine);
raceUI.push(raceStartLine);
// Finish line
raceFinishLine = LK.getAsset('finishLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 1800,
y: trackY
});
game.addChild(raceFinishLine);
raceUI.push(raceFinishLine);
// Player car
raceCar = new Car();
raceCar.setData(garage[carIdx]);
raceCar.x = 400;
raceCar.y = trackY - 80;
raceCar.scaleX = 0.8;
raceCar.scaleY = 0.8;
game.addChild(raceCar);
raceUI.push(raceCar);
// Opponent car (random stats)
raceOpponent = new Car();
var oppStats = {
stats: {
horsepower: 90 + Math.floor(Math.random() * 60),
weight: 1200,
grip: 1 + Math.random() * 0.5,
turbo: Math.floor(Math.random() * 2),
intake: Math.floor(Math.random() * 2),
tires: 1 + Math.floor(Math.random() * 2),
paint: 0x880000 + Math.floor(Math.random() * 0x77ffff)
},
parts: {
engine: 1 + Math.floor(Math.random() * 2),
turbo: Math.floor(Math.random() * 2),
intake: Math.floor(Math.random() * 2),
tires: 1 + Math.floor(Math.random() * 2)
},
paint: 0x880000 + Math.floor(Math.random() * 0x77ffff)
};
raceOpponent.setData(oppStats);
raceOpponent.x = 400;
raceOpponent.y = trackY + 80;
raceOpponent.scaleX = 0.8;
raceOpponent.scaleY = 0.8;
game.addChild(raceOpponent);
raceUI.push(raceOpponent);
// Race state
raceStarted = false;
raceEnded = false;
raceTimer = 0;
// Countdown text
var countdownText = new Text2('3', {
size: 200,
fill: "#fff"
});
countdownText.anchor.set(0.5, 0.5);
countdownText.x = 2048 / 2;
countdownText.y = 600;
LK.gui.center.addChild(countdownText);
raceUI.push(countdownText);
// Start countdown
var countdown = 3;
var countdownInterval = LK.setInterval(function () {
countdown -= 1;
if (countdown > 0) {
countdownText.setText(countdown + '');
} else if (countdown === 0) {
countdownText.setText("GO!");
// Start race
raceStarted = true;
// Calculate speed per tick (distance: 1400px, 500m)
var pxPerMeter = 1400 / raceDistance;
var carSpeed = raceCar.stats.horsepower / 100 * pxPerMeter / 60; // px per tick
var oppSpeed = raceOpponent.stats.horsepower / 100 * pxPerMeter / 60;
raceCar.startRace(carSpeed / 1400); // progress per tick
raceOpponent.startRace(oppSpeed / 1400);
} else {
LK.clearInterval(countdownInterval);
countdownText.visible = false;
}
}, 800);
// Race timer text
var timerText = new Text2('0.00s', {
size: 80,
fill: "#fff"
});
timerText.anchor.set(0.5, 0);
timerText.x = 2048 / 2;
timerText.y = 100;
LK.gui.top.addChild(timerText);
raceUI.push(timerText);
// Back button (disabled during race)
var backBtn = new UIButton();
backBtn.x = 2048 / 2;
backBtn.y = 2600;
backBtn.setText("Back");
backBtn.onClick = function () {
endRace();
showGarage();
};
LK.gui.bottom.addChild(backBtn);
raceUI.push(backBtn);
// Result text
raceResultText = new Text2('', {
size: 120,
fill: 0xFFD700
});
raceResultText.anchor.set(0.5, 0.5);
raceResultText.x = 2048 / 2;
raceResultText.y = 900;
LK.gui.center.addChild(raceResultText);
raceResultText.visible = false;
raceUI.push(raceResultText);
updateCurrencyText();
// Race update
game.update = function () {
if (state !== GAME_STATE.RACE) return;
if (raceStarted && !raceEnded) {
raceTimer += 1 / 60;
timerText.setText(raceTimer.toFixed(2) + "s");
// Move cars
if (raceCar.isRacing) {
raceCar.x = 400 + raceCar.raceProgress * 1400;
if (raceCar.raceProgress >= 1) {
raceCar.isRacing = false;
}
}
if (raceOpponent.isRacing) {
raceOpponent.x = 400 + raceOpponent.raceProgress * 1400;
if (raceOpponent.raceProgress >= 1) {
raceOpponent.isRacing = false;
}
}
// Check finish
if (!raceEnded && (raceCar.raceProgress >= 1 || raceOpponent.raceProgress >= 1)) {
raceEnded = true;
var playerWin = raceCar.raceProgress >= 1 && raceCar.raceProgress >= raceOpponent.raceProgress;
var reward = playerWin ? 1000 : 200;
var rewardCrystals = playerWin ? 1 : 0;
if (playerWin) {
raceResultText.setText("You Win!\n+$" + reward + (rewardCrystals ? " +1💎" : ""));
} else {
raceResultText.setText("You Lose!\n+$" + reward);
}
raceResultText.visible = true;
cash += reward;
crystals += rewardCrystals;
savePlayerData();
updateCurrencyText();
LK.setTimeout(function () {
endRace();
showGarage();
}, 2200);
}
}
};
}
function endRace() {
clearUI(raceUI);
raceCar = null;
raceOpponent = null;
raceTrack = null;
raceStartLine = null;
raceFinishLine = null;
raceResultText = null;
game.update = function () {};
}
// --- Start Game ---
showMainMenu();