/**** * 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();