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();
===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,737 @@
-/****
+/****
+* 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: 0x000000
-});
\ No newline at end of file
+ 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();
\ No newline at end of file