/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { coins: 0, bestDistance: 0, upgrade_fuel: 0, upgrade_armor: 0, upgrade_power: 0, upgrade_weapon: 0 }); /**** * Classes ****/ // Bullet (for armed vehicle) var Bullet = Container.expand(function () { var self = Container.call(this); var bulletSprite = self.attachAsset('fuelBarFG', { anchorX: 0.5, anchorY: 0.5, width: 36, height: 16 }); bulletSprite.tint = 0xffe066; self.width = bulletSprite.width; self.height = bulletSprite.height; self.speed = 22; self.damage = 20 + (storage.upgrade_weapon > 0 ? 20 * storage.upgrade_weapon : 0); self.update = function () { self.x += self.speed; }; return self; }); // Car (player vehicle) var Car = Container.expand(function () { var self = Container.call(this); // --- Car Skin System --- function applyCarSkin(sprite) { // Defensive: always check storage var skinKey = storage && storage.selectedCarSkin ? storage.selectedCarSkin : "default"; if (!skinKey) skinKey = "default"; // Find skin definition var skinDef = null; var carSkinsArr = [{ key: "default", asset: "car" }, { key: "red", asset: "car", tint: 0xff4444 }, { key: "blue", asset: "car", tint: 0x4488ff }, { key: "green", asset: "car", tint: 0x44ff44 }]; for (var i = 0; i < carSkinsArr.length; ++i) { if (carSkinsArr[i].key === skinKey) { skinDef = carSkinsArr[i]; break; } } if (!skinDef) skinDef = carSkinsArr[0]; // Set tint if needed if (skinDef.tint) { sprite.tint = skinDef.tint; } else { sprite.tint = 0xffffff; } } // Always use selected skin var carSprite = self.attachAsset('car', { anchorX: 0.5, anchorY: 0.5 }); applyCarSkin(carSprite); self.width = carSprite.width; self.height = carSprite.height; self.fuel = 100; self.maxFuel = 100; self.health = 100; self.maxHealth = 100; self.speed = 1.3; self.alive = true; // For drag self.isDragging = false; // For upgrades self.applyUpgrades = function () { self.maxFuel = 100 + storage.upgrade_fuel * 30; self.fuel = self.maxFuel; self.maxHealth = 100 + storage.upgrade_armor * 40; self.health = self.maxHealth; self.speed = 16 + storage.upgrade_power * 2; // Weapon upgrade: if purchased, enable weapon and set damage self.hasWeapon = storage.upgrade_weapon > 0; self.weaponDamage = 20 + (storage.upgrade_weapon > 0 ? 20 * storage.upgrade_weapon : 0); // Re-apply skin in case it changed applyCarSkin(carSprite); }; self.takeDamage = function (amount) { self.health -= amount; if (self.health < 0) self.health = 0; if (self.health === 0) { self.alive = false; } }; self.refuel = function (amount) { self.fuel += amount; if (self.fuel > self.maxFuel) self.fuel = self.maxFuel; }; self.update = function () { // Move car forward if alive and runActive if (self.alive && typeof runActive !== "undefined" && runActive) { self.x += self.speed * 0.22; if (self.x > 800) self.x = 800; } }; return self; }); // Coin var Coin = Container.expand(function () { var self = Container.call(this); var coinSprite = self.attachAsset('coin', { anchorX: 0.5, anchorY: 0.5 }); self.width = coinSprite.width; self.height = coinSprite.height; self.speed = 1.5; self.collected = false; self.update = function () { self.x -= self.speed; }; return self; }); // Zombie var Zombie = Container.expand(function () { var self = Container.call(this); var zombieSprite = self.attachAsset('zombie', { anchorX: 0.5, anchorY: 0.5 }); self.width = zombieSprite.width; self.height = zombieSprite.height; self.speed = 0.7 + Math.random() * 0.3; self.alive = true; self.update = function () { self.x -= self.speed; }; return self; }); // Zombie2 (new type) var Zombie2 = Container.expand(function () { var self = Container.call(this); var zombieSprite = self.attachAsset('zombie2', { anchorX: 0.5, anchorY: 0.5 }); self.width = zombieSprite.width; self.height = zombieSprite.height; // Slightly faster and smaller than regular zombie self.speed = 1.2 + Math.random() * 0.4; self.alive = true; self.update = function () { self.x -= self.speed; }; return self; }); // Zombie3 (can also come from the road) var Zombie3 = Container.expand(function () { var self = Container.call(this); var zombieSprite = self.attachAsset('zombie3', { anchorX: 0.5, anchorY: 0.5 }); self.width = zombieSprite.width; self.height = zombieSprite.height; // Speed and alive state self.speed = 1.0 + Math.random() * 0.5; self.alive = true; self.update = function () { self.x -= self.speed; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Pause menu removed // Sound effects // Road // Health Bar FG // Health Bar BG // Fuel Bar FG // Fuel Bar BG // Coin // Zombie // Car (player vehicle) // Desert background var desertBG = LK.getAsset('road', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 2732 }); desertBG.tint = 0xF7E9A0; // pale desert sand color game.addChild(desertBG); // Road var road = LK.getAsset('road', { anchorX: 0, anchorY: 0, x: 0, y: 2200 }); road.tint = 0xE2C97B; // sandy yellow tint for desert game.addChild(road); // Car var car = new Car(); car.x = 400; car.y = 2200 + 100; // Centered on road car.applyUpgrades(); game.addChild(car); // Arrays for zombies, coins, and bullets var zombies = []; var coins = []; var bullets = []; // Distance tracking var distance = 0; var runActive = true; // Shooting state var shootCooldown = 0; // GUI: Speed (to the left of distance) var speedTxt = new Text2('0 km/h', { size: 90, fill: 0xFFFFFF }); speedTxt.anchor.set(1, 0); // right edge, top speedTxt.x = -120; // move it further to the left of distanceTxt (will be set after distanceTxt is placed) speedTxt.y = 0; // GUI: Distance var distanceTxt = new Text2('0 m', { size: 90, fill: 0xFFFFFF }); distanceTxt.anchor.set(0.5, 0); LK.gui.top.addChild(distanceTxt); LK.gui.top.addChild(speedTxt); // GUI: Coins var coinsTxt = new Text2(storage.coins + '', { size: 90, fill: 0xFFD700 }); coinsTxt.anchor.set(0.5, 0); // Place coins counter next to distance counter (right side) coinsTxt.x = 220; coinsTxt.y = 0; LK.gui.top.addChild(coinsTxt); // Add 'Coins' label to the right of the coins counter var coinsLabel = new Text2("Coins", { size: 38, fill: 0xFFD700 }); coinsLabel.anchor.set(0, 0); // left-top anchor coinsLabel.x = coinsTxt.x + coinsTxt.width / 2 + 18; // 18px right of coins counter coinsLabel.y = coinsTxt.y + 18; // align baseline with coinsTxt LK.gui.top.addChild(coinsLabel); // --- Bullet Counter GUI --- // Show remaining bullets (right of coins, but before edge) var bulletCounterTxt = new Text2("", { size: 48, fill: 0xFFE066 }); bulletCounterTxt.anchor.set(0.5, 0); bulletCounterTxt.x = coinsLabel.x + coinsLabel.width + 140; // moved 40px to the right, now 40px more left bulletCounterTxt.y = 18; // moved up a little bit LK.gui.top.addChild(bulletCounterTxt); // Pause menu removed // GUI: Fuel Bar (moved to top left) var fuelBarBG = LK.getAsset('fuelBarBG', { anchorX: 0, anchorY: 0.5, x: 60, // 60px from left edge, to avoid menu button y: 120 }); var fuelBarFG = LK.getAsset('fuelBarFG', { anchorX: 0, anchorY: 0.5, x: 60, y: 120 }); LK.gui.top.addChild(fuelBarBG); LK.gui.top.addChild(fuelBarFG); // Add 'Fuel' label next to the fuel bar (to the left) var fuelLabel = new Text2("Fuel", { size: 60, fill: "#fff" }); fuelLabel.anchor.set(1, 0.5); fuelLabel.x = 50; // 10px left of the new bar's left edge fuelLabel.y = 120; LK.gui.top.addChild(fuelLabel); // Add remaining fuel text to the right of the fuel bar var fuelRemainTxt = new Text2("", { size: 48, fill: "#fff" }); fuelRemainTxt.anchor.set(0, 0.5); fuelRemainTxt.x = 60 + 400 + 18; // right of new bar + 18px fuelRemainTxt.y = 120; LK.gui.top.addChild(fuelRemainTxt); // GUI: Health Bar (moved under fuel bar, top left) var healthBarBG = LK.getAsset('healthBarBG', { anchorX: 0, anchorY: 0.5, x: 60, y: 200 }); var healthBarFG = LK.getAsset('healthBarFG', { anchorX: 0, anchorY: 0.5, x: 60, y: 200 }); LK.gui.top.addChild(healthBarBG); LK.gui.top.addChild(healthBarFG); // Add 'Health' label next to the health bar (to the left) var healthLabel = new Text2("Health", { size: 60, fill: "#fff" }); healthLabel.anchor.set(1, 0.5); healthLabel.x = 50; healthLabel.y = 200; LK.gui.top.addChild(healthLabel); // Add remaining health text to the right of the health bar var healthRemainTxt = new Text2("", { size: 48, fill: "#fff" }); healthRemainTxt.anchor.set(0, 0.5); healthRemainTxt.x = 60 + 400 + 18; // right of new bar + 18px healthRemainTxt.y = 200; LK.gui.top.addChild(healthRemainTxt); // PC controls: keyboard arrow keys or WASD for car movement var keyState = { up: false, down: false }; // Touch/click to shoot (mobile-friendly) game.down = function (x, y, obj) { // Only shoot if car is alive, has weapon, run is active, and has bullets left if (car.alive && car.hasWeapon && runActive && shootCooldown <= 0 && car.bulletsLeft > 0) { var bullet = new Bullet(); bullet.x = car.x + car.width / 2 + 30; bullet.y = car.y; bullets.push(bullet); game.addChild(bullet); shootCooldown = 12; // frames between shots (~0.2s at 60fps) car.bulletsLeft = Math.max(0, car.bulletsLeft - 1); if (typeof bulletCounterTxt !== "undefined") { bulletCounterTxt.setText("Bullets: " + car.bulletsLeft); } } }; // Listen for keydown and keyup events using LK.on (simulated for PC) // LK does not provide keyboard events, so we simulate with a global object for testing // NOTE: In the FRVR/LK environment, keyboard events are not natively supported. // For PC testing, you may need to use a custom event system or test with touch/mouse events. // Here, we provide a fallback for environments that do support document events. if (typeof document !== "undefined" && typeof document.addEventListener === "function") { document.addEventListener('keydown', function (e) { if (!runActive) return; if (e.code === 'ArrowUp' || e.code === 'KeyW') keyState.up = true; if (e.code === 'ArrowDown' || e.code === 'KeyS') keyState.down = true; }); document.addEventListener('keyup', function (e) { if (e.code === 'ArrowUp' || e.code === 'KeyW') keyState.up = false; if (e.code === 'ArrowDown' || e.code === 'KeyS') keyState.down = false; }); } // Spawn zombies function spawnZombie() { // Randomly pick zombie type: 1/3 each var zombieType = Math.floor(Math.random() * 3); var zombie; if (zombieType === 0) { zombie = new Zombie(); } else if (zombieType === 1) { zombie = new Zombie2(); } else { zombie = new Zombie3(); } zombie.x = 2048 + 100; // Random vertical position on road var minY = road.y + zombie.height / 2; var maxY = road.y + road.height - zombie.height / 2; zombie.y = minY + Math.random() * (maxY - minY); zombies.push(zombie); game.addChild(zombie); } // Spawn coins function spawnCoin() { var coin = new Coin(); coin.x = 2048 - 300; // Random vertical position on road var minY = road.y + coin.height / 2; var maxY = road.y + road.height - coin.height / 2; coin.y = minY + Math.random() * (maxY - minY); coins.push(coin); game.addChild(coin); } // Reset run function startRun() { // Remove all zombies and coins for (var i = 0; i < zombies.length; ++i) zombies[i].destroy(); for (var i = 0; i < coins.length; ++i) coins[i].destroy(); zombies = []; coins = []; // Reset car car.applyUpgrades(); car.x = 400; car.y = 2200 + 100; car.alive = true; // Set bullet count based on weapon level if (typeof car !== "undefined" && car.hasWeapon) { if (storage.upgrade_weapon === 1) { car.bulletsLeft = 5; } else if (storage.upgrade_weapon === 2) { car.bulletsLeft = 10; } else if (storage.upgrade_weapon === 3) { car.bulletsLeft = 15; } else if (storage.upgrade_weapon === 4) { car.bulletsLeft = 20; } else if (storage.upgrade_weapon >= 5) { car.bulletsLeft = 25; } else { car.bulletsLeft = 0; } } else { car.bulletsLeft = 0; } // Reset distance distance = 0; runActive = true; // Update GUI distanceTxt.setText('0 m'); coinsTxt.setText(storage.coins + ''); // Reset bars updateBars(); // Play music LK.playMusic('bgmusic'); } // Update fuel and health bars function updateBars() { // Fuel var fuelFrac = car.fuel / car.maxFuel; if (fuelFrac < 0) fuelFrac = 0; if (fuelFrac > 1) fuelFrac = 1; fuelBarFG.width = 400 * fuelFrac; if (typeof fuelRemainTxt !== "undefined") { fuelRemainTxt.setText(Math.round(car.fuel) + " / " + car.maxFuel); } // Health var healthFrac = car.health / car.maxHealth; if (healthFrac < 0) healthFrac = 0; if (healthFrac > 1) healthFrac = 1; // Health bar should decrease as health decreases healthBarFG.width = 400 * healthFrac; if (typeof healthRemainTxt !== "undefined") { healthRemainTxt.setText(Math.round(car.health) + " / " + car.maxHealth); } } // Game update game.update = function () { if (!runActive) return; // PC controls: move car up/down with keyboard var moveAmount = 18 + storage.upgrade_power * 1.5; if (keyState.up) { car.y -= moveAmount; } if (keyState.down) { car.y += moveAmount; } // Clamp car to road var minY = road.y + car.height / 2; var maxY = road.y + road.height - car.height / 2; if (car.y < minY) car.y = minY; if (car.y > maxY) car.y = maxY; // Move car forward car.x += car.speed * 0.22; if (car.x > 800) car.x = 800; // Car stays at 800, world scrolls // Scroll world: move all objects left by car.speed for (var i = 0; i < zombies.length; ++i) zombies[i].x -= car.speed * 0.13; for (var i = 0; i < coins.length; ++i) coins[i].x -= car.speed * 0.13; // Fuel consumption (depletes 1.5x faster) car.fuel -= (0.022 + 0.0015 * car.speed) * 1.5; if (car.fuel < 0) car.fuel = 0; // Distance distance += car.speed * 0.09; distanceTxt.setText(parseInt(distance / 10) + ' m'); // Update speed text (show as integer, km/h style) if (typeof speedTxt !== "undefined") { speedTxt.setText(Math.round(car.speed * 7.5) + " km/h"); // Position speedTxt further to the left of distanceTxt and vertically aligned speedTxt.x = distanceTxt.x - distanceTxt.width / 2 - 100; speedTxt.y = distanceTxt.y + 6; } // Spawn zombies if (LK.ticks % 45 === 0) { spawnZombie(); } // Spawn coins if (LK.ticks % 70 === 0) { spawnCoin(); } // Update zombies for (var i = zombies.length - 1; i >= 0; --i) { var z = zombies[i]; z.update(); // Remove if off screen if (z.x < -200) { z.destroy(); zombies.splice(i, 1); continue; } // Bullet collision for (var j = bullets.length - 1; j >= 0; --j) { var b = bullets[j]; if (z.alive && b && b.intersects(z)) { z.alive = false; tween(z, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { z.destroy(); } }); zombies.splice(i, 1); b.destroy(); bullets.splice(j, 1); // Optionally, add a visual effect for weapon hit LK.effects.flashObject(z, 0xffff00, 200); break; } } // Collision with car if (car.alive && z.alive && car.intersects(z)) { z.alive = false; LK.getSound('zombiehit').play(); tween(z, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { z.destroy(); } }); zombies.splice(i, 1); // Decrease car health when zombies hit the car if (car.hasWeapon) { // Instantly destroy zombie (already handled above), reduce car damage car.takeDamage(10); // Always decrease by 10 // Optionally, add a visual effect for weapon hit LK.effects.flashObject(z, 0xffff00, 200); // Flash zombie yellow for weapon hit } else { // Normal damage car.takeDamage(10); // Always decrease by 10 } // Flash car LK.effects.flashObject(car, 0xff0000, 300); // Bump car back tween(car, { x: car.x - 40 }, { duration: 120, easing: tween.easeOut }); // If car dead, end run if (!car.alive) { endRun(); return; } } } // Update bullets for (var i = bullets.length - 1; i >= 0; --i) { var b = bullets[i]; b.update(); if (b.x > 2048 + 100) { b.destroy(); bullets.splice(i, 1); } } // Update coins for (var i = coins.length - 1; i >= 0; --i) { var c = coins[i]; c.update(); // Remove if off screen if (c.x < -100) { c.destroy(); coins.splice(i, 1); continue; } // Collect coin if (!c.collected && car.intersects(c)) { c.collected = true; LK.getSound('coin').play(); storage.coins += 1; coinsTxt.setText(storage.coins + ''); tween(c, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 200, onFinish: function onFinish() { c.destroy(); } }); coins.splice(i, 1); } } // Update bars updateBars(); // Update bullet counter GUI if (typeof bulletCounterTxt !== "undefined" && typeof car !== "undefined" && car.hasWeapon) { bulletCounterTxt.setText("Bullets: " + car.bulletsLeft); } else if (typeof bulletCounterTxt !== "undefined") { bulletCounterTxt.setText(""); } // Decrement shoot cooldown if (shootCooldown > 0) shootCooldown--; // Out of fuel if (car.fuel <= 0) { car.fuel = 0; endRun(); return; } }; // End run function endRun() { runActive = false; // Save best distance var meters = parseInt(distance / 10); if (meters > storage.bestDistance) storage.bestDistance = meters; // Show game over LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); LK.stopMusic(); } // On game over, reset run LK.on('gameover', function () { startRun(); }); // On you win (not used, but for completeness) LK.on('youwin', function () { startRun(); }); // Garage menu logic function showGarageMenu(onDone) { // Pause run and world when garage menu is open var wasActive = runActive; runActive = false; // Simple garage menu using Text2 and GUI overlay // Remove any previous garage UI if (typeof garageUI !== "undefined" && garageUI) { LK.gui.center.removeChild(garageUI); garageUI = null; } var garageUI = new Container(); // Title var title = new Text2("Garage", { size: 120, fill: "#fff" }); title.anchor.set(0.5, 0); title.x = 0; title.y = 0; garageUI.addChild(title); // Coin display var coinsLabel = new Text2("Coins: " + storage.coins, { size: 80, fill: 0xFFD700 }); coinsLabel.anchor.set(0.5, 0); coinsLabel.x = 0; coinsLabel.y = 140; garageUI.addChild(coinsLabel); // Upgrade buttons and labels var upgrades = [{ key: "upgrade_fuel", label: "Fuel", cost: 10, y: 300, desc: "+30 max fuel" }, { key: "upgrade_armor", label: "Armor", cost: 15, y: 500, desc: "+40 max health" }, { key: "upgrade_power", label: "Power", cost: 20, y: 700, desc: "+2 speed" }, { key: "upgrade_weapon", label: "Weapon", cost: 30, y: 900, desc: "Add weapon, +damage to zombies", descSize: 54 // Make this description smaller }]; var upgradeButtons = []; for (var i = 0; i < upgrades.length; ++i) { (function (i) { var upg = upgrades[i]; // Label var labelSize = upg.key === "upgrade_weapon" && upg.descSize ? upg.descSize : 70; var upgLabel = new Text2(upg.label + " Lv." + (storage[upg.key] || 0) + " (" + upg.desc + ")", { size: labelSize, fill: "#fff" }); upgLabel.anchor.set(0.5, 0); upgLabel.x = 0; upgLabel.y = upg.y; garageUI.addChild(upgLabel); // Cost var costLabel = new Text2("Cost: " + upg.cost, { size: 60, fill: 0xFFD700 }); costLabel.anchor.set(0.5, 0); costLabel.x = -200; costLabel.y = upg.y + 90; garageUI.addChild(costLabel); // Upgrade button var btn = LK.getAsset('fuelBarFG', { anchorX: 0.5, anchorY: 0.5, x: 200, y: upg.y + 120, width: 220, height: 80 }); btn.tint = 0x44ff44; garageUI.addChild(btn); // Button label var btnLabel = new Text2("Upgrade", { size: 50, fill: "#222" }); btnLabel.anchor.set(0.5, 0.5); btnLabel.x = btn.x; btnLabel.y = btn.y; garageUI.addChild(btnLabel); // Touch/click handler btn.interactive = true; btn.down = function (x, y, obj) { if (storage.coins >= upg.cost) { storage.coins -= upg.cost; storage[upg.key] = (storage[upg.key] || 0) + 1; coinsLabel.setText("Coins: " + storage.coins); upgLabel.setText(upg.label + " Lv." + storage[upg.key] + " (" + upg.desc + ")"); } }; upgradeButtons.push(btn); })(i); } // (Start Run button removed) // Back button for garage var garageBackBtn = LK.getAsset('fuelBarFG', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 1250, width: 400, height: 120 }); garageBackBtn.tint = 0xcccccc; garageUI.addChild(garageBackBtn); var garageBackLabel = new Text2("Back", { size: 70, fill: "#222" }); garageBackLabel.anchor.set(0.5, 0.5); garageBackLabel.x = garageBackBtn.x; garageBackLabel.y = garageBackBtn.y; garageUI.addChild(garageBackLabel); garageBackBtn.interactive = true; garageBackBtn.down = function (x, y, obj) { LK.gui.center.removeChild(garageUI); garageUI = null; // Resume previous state (main menu or game paused) if (typeof onDone === "function") onDone(); }; // Center garage UI garageUI.x = 0; garageUI.y = -350; // Move the garage menu up by 350px (higher than before) LK.gui.center.addChild(garageUI); } // --- Main Menu UI --- var mainMenuUI = new Container(); // Title var mainTitle = new Text2("Drive to Survive", { size: 150, fill: 0xF7E9A0 }); mainTitle.anchor.set(0.5, 0); mainTitle.x = 0; mainTitle.y = 0; mainMenuUI.addChild(mainTitle); // Play Button var playBtn = LK.getAsset('fuelBarFG', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 400, width: 500, height: 140 }); playBtn.tint = 0x44bbff; mainMenuUI.addChild(playBtn); var playLabel = new Text2("Play", { size: 90, fill: "#fff" }); playLabel.anchor.set(0.5, 0.5); playLabel.x = playBtn.x; playLabel.y = playBtn.y; mainMenuUI.addChild(playLabel); // Garage Button var menuGarageBtn = LK.getAsset('fuelBarFG', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 600, width: 500, height: 140 }); menuGarageBtn.tint = 0x8888ff; mainMenuUI.addChild(menuGarageBtn); var menuGarageLabel = new Text2("Garage", { size: 90, fill: "#fff" }); menuGarageLabel.anchor.set(0.5, 0.5); menuGarageLabel.x = menuGarageBtn.x; menuGarageLabel.y = menuGarageBtn.y; mainMenuUI.addChild(menuGarageLabel); // Shop Button var shopBtn = LK.getAsset('fuelBarFG', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 800, width: 500, height: 140 }); shopBtn.tint = 0xffb347; mainMenuUI.addChild(shopBtn); var shopLabel = new Text2("Shop", { size: 90, fill: "#fff" }); shopLabel.anchor.set(0.5, 0.5); shopLabel.x = shopBtn.x; shopLabel.y = shopBtn.y; mainMenuUI.addChild(shopLabel); // Settings Button var settingsBtn = LK.getAsset('fuelBarFG', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 1000, width: 500, height: 140 }); settingsBtn.tint = 0xcccccc; mainMenuUI.addChild(settingsBtn); var settingsLabel = new Text2("Settings", { size: 90, fill: "#222" }); settingsLabel.anchor.set(0.5, 0.5); settingsLabel.x = settingsBtn.x; settingsLabel.y = settingsBtn.y; mainMenuUI.addChild(settingsLabel); // --- Shop Menu UI --- var shopUI = new Container(); var shopTitle = new Text2("Car Skins", { size: 120, fill: "#fff" }); shopTitle.anchor.set(0.5, 0); shopTitle.x = 0; shopTitle.y = -260; // moved up by 40px (was -220) shopUI.addChild(shopTitle); // Define available skins var carSkins = [{ key: "default", name: "Classic", asset: "car", price: 0 }, { key: "red", name: "Red Racer", asset: "car", price: 80, tint: 0xff4444 }, { key: "blue", name: "Blue Bolt", asset: "car", price: 120, tint: 0x4488ff }, { key: "green", name: "Green Machine", asset: "car", price: 160, tint: 0x44ff44 }]; // Persistent storage for owned skins and selected skin if (!storage.carSkins) storage.carSkins = { "default": true }; if (!storage.selectedCarSkin) storage.selectedCarSkin = "default"; // Coin display in shop var shopCoinsLabel = new Text2("Coins: " + storage.coins, { size: 80, fill: 0xFFD700 }); shopCoinsLabel.anchor.set(0.5, 0); shopCoinsLabel.x = 0; shopCoinsLabel.y = -120; // moved up by 60px (was -60) shopUI.addChild(shopCoinsLabel); // Skin buttons var skinButtons = []; for (var i = 0; i < carSkins.length; ++i) { (function (i) { var skin = carSkins[i]; // Increase vertical spacing between skins var y = 60 + i * 220; // moved all skins up by 80px (was 140 + i * 220) // Car preview var preview = LK.getAsset(skin.asset, { anchorX: 0.5, anchorY: 0.5, x: -320, y: y, width: 320, height: 120 }); if (skin.tint) preview.tint = skin.tint; shopUI.addChild(preview); // Skin name var nameLabel = new Text2(skin.name, { size: skin.key === "green" ? 56 : 70, fill: "#fff" }); nameLabel.anchor.set(0, 0.5); nameLabel.x = -120; nameLabel.y = y; shopUI.addChild(nameLabel); // Price or "Owned" var priceLabel = new Text2(storage.carSkins[skin.key] ? "Owned" : "Price: " + skin.price, { size: 60, fill: storage.carSkins[skin.key] ? "#44ff44" : "#FFD700" }); priceLabel.anchor.set(0.5, 1); priceLabel.x = 400; priceLabel.y = y - 50; // 50px above the button shopUI.addChild(priceLabel); // Select/Buy button var btn = LK.getAsset('fuelBarFG', { anchorX: 0.5, anchorY: 0.5, x: 400, y: y, width: 220, height: 80 }); btn.tint = storage.selectedCarSkin === skin.key ? 0x44bbff : 0xcccccc; shopUI.addChild(btn); var btnLabel = new Text2(storage.carSkins[skin.key] ? storage.selectedCarSkin === skin.key ? "Selected" : "Select" : "Buy", { size: 50, fill: "#222" }); btnLabel.anchor.set(0.5, 0.5); btnLabel.x = btn.x; btnLabel.y = btn.y; shopUI.addChild(btnLabel); btn.interactive = true; btn.down = function (x, y, obj) { if (storage.carSkins[skin.key]) { // Select skin storage.selectedCarSkin = skin.key; // Update all button tints and labels for (var j = 0; j < skinButtons.length; ++j) { var b = skinButtons[j]; b.btn.tint = carSkins[j].key === skin.key ? 0x44bbff : 0xcccccc; b.btnLabel.setText(storage.carSkins[carSkins[j].key] ? carSkins[j].key === skin.key ? "Selected" : "Select" : "Buy"); } } else if (storage.coins >= skin.price) { // Buy skin storage.coins -= skin.price; storage.carSkins[skin.key] = true; shopCoinsLabel.setText("Coins: " + storage.coins); priceLabel.setText("Owned"); priceLabel.setStyle({ fill: 0x44FF44 }); btnLabel.setText("Select"); } }; skinButtons.push({ btn: btn, btnLabel: btnLabel }); })(i); } // Back button for shop var shopBackBtn = LK.getAsset('fuelBarFG', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -20 + carSkins.length * 220, // moved up by 80px (was 60 + carSkins.length * 220) width: 400, height: 120 }); shopBackBtn.tint = 0xcccccc; shopUI.addChild(shopBackBtn); var shopBackLabel = new Text2("Back", { size: 70, fill: "#222" }); shopBackLabel.anchor.set(0.5, 0.5); shopBackLabel.x = shopBackBtn.x; shopBackLabel.y = shopBackBtn.y; shopUI.addChild(shopBackLabel); shopBackBtn.interactive = true; shopBackBtn.down = function (x, y, obj) { LK.gui.center.removeChild(shopUI); LK.gui.center.addChild(mainMenuUI); }; // Music On/Off Button (in settings menu) var settingsMenuUI = new Container(); var musicOn = true; // Reset Button (above music) var resetBtn = LK.getAsset('fuelBarFG', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 60, // 140px above music button width: 400, height: 120 }); resetBtn.tint = 0xff4444; settingsMenuUI.addChild(resetBtn); var resetLabel = new Text2("Reset Progress", { size: 70, fill: "#fff" }); resetLabel.anchor.set(0.5, 0.5); resetLabel.x = resetBtn.x; resetLabel.y = resetBtn.y; settingsMenuUI.addChild(resetLabel); resetBtn.interactive = true; resetBtn.down = function (x, y, obj) { // Show confirmation dialog before resetting progress var confirmDialog = new Container(); // Dialog background var dialogBG = LK.getAsset('fuelBarBG', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0, width: 900, height: 420 }); dialogBG.tint = 0x222222; confirmDialog.addChild(dialogBG); // Confirmation text var confirmText = new Text2("Are you sure?\nAll your progress will be reset", { size: 64, fill: "#fff" }); confirmText.anchor.set(0.5, 0.5); confirmText.x = 0; confirmText.y = -60; confirmDialog.addChild(confirmText); // Yes button var yesBtn = LK.getAsset('fuelBarFG', { anchorX: 0.5, anchorY: 0.5, x: -180, y: 100, width: 300, height: 100 }); yesBtn.tint = 0xff4444; confirmDialog.addChild(yesBtn); var yesLabel = new Text2("Yes", { size: 60, fill: "#fff" }); yesLabel.anchor.set(0.5, 0.5); yesLabel.x = yesBtn.x; yesLabel.y = yesBtn.y; confirmDialog.addChild(yesLabel); // No button var noBtn = LK.getAsset('fuelBarFG', { anchorX: 0.5, anchorY: 0.5, x: 180, y: 100, width: 300, height: 100 }); noBtn.tint = 0x44bbff; confirmDialog.addChild(noBtn); var noLabel = new Text2("No", { size: 60, fill: "#fff" }); noLabel.anchor.set(0.5, 0.5); noLabel.x = noBtn.x; noLabel.y = noBtn.y; confirmDialog.addChild(noLabel); // Yes button handler yesBtn.interactive = true; yesBtn.down = function (x, y, obj) { // Reset all persistent progress storage.coins = 0; storage.bestDistance = 0; storage.upgrade_fuel = 0; storage.upgrade_armor = 0; storage.upgrade_power = 0; storage.upgrade_weapon = 0; storage.carSkins = { "default": true }; storage.selectedCarSkin = "default"; LK.effects.flashScreen(0xff4444, 400); // Remove dialog LK.gui.center.removeChild(confirmDialog); }; // No button handler noBtn.interactive = true; noBtn.down = function (x, y, obj) { LK.gui.center.removeChild(confirmDialog); }; // Center dialog confirmDialog.x = 0; confirmDialog.y = 0; LK.gui.center.addChild(confirmDialog); }; // Music Button var musicBtn = LK.getAsset('fuelBarFG', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 200, width: 400, height: 120 }); musicBtn.tint = 0x44ff88; settingsMenuUI.addChild(musicBtn); var musicLabel = new Text2("Music: On", { size: 70, fill: "#222" }); musicLabel.anchor.set(0.5, 0.5); musicLabel.x = musicBtn.x; musicLabel.y = musicBtn.y; settingsMenuUI.addChild(musicLabel); // Language selection button var languageBtn = LK.getAsset('fuelBarFG', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 340, width: 400, height: 120 }); languageBtn.tint = 0xffe066; settingsMenuUI.addChild(languageBtn); var languageOptions = ["English", "Türkçe"]; var languageIndex = 0; var languageLabel = new Text2("Language: " + languageOptions[languageIndex], { size: 70, fill: "#222" }); languageLabel.anchor.set(0.5, 0.5); languageLabel.x = languageBtn.x; languageLabel.y = languageBtn.y; settingsMenuUI.addChild(languageLabel); languageBtn.interactive = true; languageBtn.down = function (x, y, obj) { languageIndex = (languageIndex + 1) % languageOptions.length; languageLabel.setText("Language: " + languageOptions[languageIndex]); // Here you could add logic to update UI text based on language selection }; // Back button for settings var settingsBackBtn = LK.getAsset('fuelBarFG', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 520, width: 400, height: 120 }); settingsBackBtn.tint = 0xcccccc; settingsMenuUI.addChild(settingsBackBtn); var settingsBackLabel = new Text2("Back", { size: 70, fill: "#222" }); settingsBackLabel.anchor.set(0.5, 0.5); settingsBackLabel.x = settingsBackBtn.x; settingsBackLabel.y = settingsBackBtn.y; settingsMenuUI.addChild(settingsBackLabel); // Center main menu UI mainMenuUI.x = 0; mainMenuUI.y = -350; LK.gui.center.addChild(mainMenuUI); // Hide all game UI and pause game while in menu runActive = false; // Play button handler playBtn.interactive = true; playBtn.down = function (x, y, obj) { LK.gui.center.removeChild(mainMenuUI); runActive = true; startRun(); }; // Garage button handler menuGarageBtn.interactive = true; menuGarageBtn.down = function (x, y, obj) { LK.gui.center.removeChild(mainMenuUI); showGarageMenu(function () { // After garage, return to main menu LK.gui.center.addChild(mainMenuUI); runActive = false; }); }; // Shop button handler shopBtn.interactive = true; shopBtn.down = function (x, y, obj) { LK.gui.center.removeChild(mainMenuUI); LK.gui.center.addChild(shopUI); }; // Settings button handler settingsBtn.interactive = true; settingsBtn.down = function (x, y, obj) { LK.gui.center.removeChild(mainMenuUI); LK.gui.center.addChild(settingsMenuUI); }; // Music on/off handler musicBtn.interactive = true; musicBtn.down = function (x, y, obj) { musicOn = !musicOn; if (musicOn) { musicLabel.setText("Music: On"); LK.playMusic('bgmusic'); } else { musicLabel.setText("Music: Off"); LK.stopMusic(); } }; // Settings back button handler settingsBackBtn.interactive = true; settingsBackBtn.down = function (x, y, obj) { LK.gui.center.removeChild(settingsMenuUI); LK.gui.center.addChild(mainMenuUI); }; // --- End Main Menu UI --- // Remove original auto-start garage menu and run // Show garage menu before starting run // showGarageMenu(function () { // startRun(); // });;
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
coins: 0,
bestDistance: 0,
upgrade_fuel: 0,
upgrade_armor: 0,
upgrade_power: 0,
upgrade_weapon: 0
});
/****
* Classes
****/
// Bullet (for armed vehicle)
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('fuelBarFG', {
anchorX: 0.5,
anchorY: 0.5,
width: 36,
height: 16
});
bulletSprite.tint = 0xffe066;
self.width = bulletSprite.width;
self.height = bulletSprite.height;
self.speed = 22;
self.damage = 20 + (storage.upgrade_weapon > 0 ? 20 * storage.upgrade_weapon : 0);
self.update = function () {
self.x += self.speed;
};
return self;
});
// Car (player vehicle)
var Car = Container.expand(function () {
var self = Container.call(this);
// --- Car Skin System ---
function applyCarSkin(sprite) {
// Defensive: always check storage
var skinKey = storage && storage.selectedCarSkin ? storage.selectedCarSkin : "default";
if (!skinKey) skinKey = "default";
// Find skin definition
var skinDef = null;
var carSkinsArr = [{
key: "default",
asset: "car"
}, {
key: "red",
asset: "car",
tint: 0xff4444
}, {
key: "blue",
asset: "car",
tint: 0x4488ff
}, {
key: "green",
asset: "car",
tint: 0x44ff44
}];
for (var i = 0; i < carSkinsArr.length; ++i) {
if (carSkinsArr[i].key === skinKey) {
skinDef = carSkinsArr[i];
break;
}
}
if (!skinDef) skinDef = carSkinsArr[0];
// Set tint if needed
if (skinDef.tint) {
sprite.tint = skinDef.tint;
} else {
sprite.tint = 0xffffff;
}
}
// Always use selected skin
var carSprite = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
applyCarSkin(carSprite);
self.width = carSprite.width;
self.height = carSprite.height;
self.fuel = 100;
self.maxFuel = 100;
self.health = 100;
self.maxHealth = 100;
self.speed = 1.3;
self.alive = true;
// For drag
self.isDragging = false;
// For upgrades
self.applyUpgrades = function () {
self.maxFuel = 100 + storage.upgrade_fuel * 30;
self.fuel = self.maxFuel;
self.maxHealth = 100 + storage.upgrade_armor * 40;
self.health = self.maxHealth;
self.speed = 16 + storage.upgrade_power * 2;
// Weapon upgrade: if purchased, enable weapon and set damage
self.hasWeapon = storage.upgrade_weapon > 0;
self.weaponDamage = 20 + (storage.upgrade_weapon > 0 ? 20 * storage.upgrade_weapon : 0);
// Re-apply skin in case it changed
applyCarSkin(carSprite);
};
self.takeDamage = function (amount) {
self.health -= amount;
if (self.health < 0) self.health = 0;
if (self.health === 0) {
self.alive = false;
}
};
self.refuel = function (amount) {
self.fuel += amount;
if (self.fuel > self.maxFuel) self.fuel = self.maxFuel;
};
self.update = function () {
// Move car forward if alive and runActive
if (self.alive && typeof runActive !== "undefined" && runActive) {
self.x += self.speed * 0.22;
if (self.x > 800) self.x = 800;
}
};
return self;
});
// Coin
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinSprite = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = coinSprite.width;
self.height = coinSprite.height;
self.speed = 1.5;
self.collected = false;
self.update = function () {
self.x -= self.speed;
};
return self;
});
// Zombie
var Zombie = Container.expand(function () {
var self = Container.call(this);
var zombieSprite = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = zombieSprite.width;
self.height = zombieSprite.height;
self.speed = 0.7 + Math.random() * 0.3;
self.alive = true;
self.update = function () {
self.x -= self.speed;
};
return self;
});
// Zombie2 (new type)
var Zombie2 = Container.expand(function () {
var self = Container.call(this);
var zombieSprite = self.attachAsset('zombie2', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = zombieSprite.width;
self.height = zombieSprite.height;
// Slightly faster and smaller than regular zombie
self.speed = 1.2 + Math.random() * 0.4;
self.alive = true;
self.update = function () {
self.x -= self.speed;
};
return self;
});
// Zombie3 (can also come from the road)
var Zombie3 = Container.expand(function () {
var self = Container.call(this);
var zombieSprite = self.attachAsset('zombie3', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = zombieSprite.width;
self.height = zombieSprite.height;
// Speed and alive state
self.speed = 1.0 + Math.random() * 0.5;
self.alive = true;
self.update = function () {
self.x -= self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Pause menu removed
// Sound effects
// Road
// Health Bar FG
// Health Bar BG
// Fuel Bar FG
// Fuel Bar BG
// Coin
// Zombie
// Car (player vehicle)
// Desert background
var desertBG = LK.getAsset('road', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
});
desertBG.tint = 0xF7E9A0; // pale desert sand color
game.addChild(desertBG);
// Road
var road = LK.getAsset('road', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 2200
});
road.tint = 0xE2C97B; // sandy yellow tint for desert
game.addChild(road);
// Car
var car = new Car();
car.x = 400;
car.y = 2200 + 100; // Centered on road
car.applyUpgrades();
game.addChild(car);
// Arrays for zombies, coins, and bullets
var zombies = [];
var coins = [];
var bullets = [];
// Distance tracking
var distance = 0;
var runActive = true;
// Shooting state
var shootCooldown = 0;
// GUI: Speed (to the left of distance)
var speedTxt = new Text2('0 km/h', {
size: 90,
fill: 0xFFFFFF
});
speedTxt.anchor.set(1, 0); // right edge, top
speedTxt.x = -120; // move it further to the left of distanceTxt (will be set after distanceTxt is placed)
speedTxt.y = 0;
// GUI: Distance
var distanceTxt = new Text2('0 m', {
size: 90,
fill: 0xFFFFFF
});
distanceTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(distanceTxt);
LK.gui.top.addChild(speedTxt);
// GUI: Coins
var coinsTxt = new Text2(storage.coins + '', {
size: 90,
fill: 0xFFD700
});
coinsTxt.anchor.set(0.5, 0);
// Place coins counter next to distance counter (right side)
coinsTxt.x = 220;
coinsTxt.y = 0;
LK.gui.top.addChild(coinsTxt);
// Add 'Coins' label to the right of the coins counter
var coinsLabel = new Text2("Coins", {
size: 38,
fill: 0xFFD700
});
coinsLabel.anchor.set(0, 0); // left-top anchor
coinsLabel.x = coinsTxt.x + coinsTxt.width / 2 + 18; // 18px right of coins counter
coinsLabel.y = coinsTxt.y + 18; // align baseline with coinsTxt
LK.gui.top.addChild(coinsLabel);
// --- Bullet Counter GUI ---
// Show remaining bullets (right of coins, but before edge)
var bulletCounterTxt = new Text2("", {
size: 48,
fill: 0xFFE066
});
bulletCounterTxt.anchor.set(0.5, 0);
bulletCounterTxt.x = coinsLabel.x + coinsLabel.width + 140; // moved 40px to the right, now 40px more left
bulletCounterTxt.y = 18; // moved up a little bit
LK.gui.top.addChild(bulletCounterTxt);
// Pause menu removed
// GUI: Fuel Bar (moved to top left)
var fuelBarBG = LK.getAsset('fuelBarBG', {
anchorX: 0,
anchorY: 0.5,
x: 60,
// 60px from left edge, to avoid menu button
y: 120
});
var fuelBarFG = LK.getAsset('fuelBarFG', {
anchorX: 0,
anchorY: 0.5,
x: 60,
y: 120
});
LK.gui.top.addChild(fuelBarBG);
LK.gui.top.addChild(fuelBarFG);
// Add 'Fuel' label next to the fuel bar (to the left)
var fuelLabel = new Text2("Fuel", {
size: 60,
fill: "#fff"
});
fuelLabel.anchor.set(1, 0.5);
fuelLabel.x = 50; // 10px left of the new bar's left edge
fuelLabel.y = 120;
LK.gui.top.addChild(fuelLabel);
// Add remaining fuel text to the right of the fuel bar
var fuelRemainTxt = new Text2("", {
size: 48,
fill: "#fff"
});
fuelRemainTxt.anchor.set(0, 0.5);
fuelRemainTxt.x = 60 + 400 + 18; // right of new bar + 18px
fuelRemainTxt.y = 120;
LK.gui.top.addChild(fuelRemainTxt);
// GUI: Health Bar (moved under fuel bar, top left)
var healthBarBG = LK.getAsset('healthBarBG', {
anchorX: 0,
anchorY: 0.5,
x: 60,
y: 200
});
var healthBarFG = LK.getAsset('healthBarFG', {
anchorX: 0,
anchorY: 0.5,
x: 60,
y: 200
});
LK.gui.top.addChild(healthBarBG);
LK.gui.top.addChild(healthBarFG);
// Add 'Health' label next to the health bar (to the left)
var healthLabel = new Text2("Health", {
size: 60,
fill: "#fff"
});
healthLabel.anchor.set(1, 0.5);
healthLabel.x = 50;
healthLabel.y = 200;
LK.gui.top.addChild(healthLabel);
// Add remaining health text to the right of the health bar
var healthRemainTxt = new Text2("", {
size: 48,
fill: "#fff"
});
healthRemainTxt.anchor.set(0, 0.5);
healthRemainTxt.x = 60 + 400 + 18; // right of new bar + 18px
healthRemainTxt.y = 200;
LK.gui.top.addChild(healthRemainTxt);
// PC controls: keyboard arrow keys or WASD for car movement
var keyState = {
up: false,
down: false
};
// Touch/click to shoot (mobile-friendly)
game.down = function (x, y, obj) {
// Only shoot if car is alive, has weapon, run is active, and has bullets left
if (car.alive && car.hasWeapon && runActive && shootCooldown <= 0 && car.bulletsLeft > 0) {
var bullet = new Bullet();
bullet.x = car.x + car.width / 2 + 30;
bullet.y = car.y;
bullets.push(bullet);
game.addChild(bullet);
shootCooldown = 12; // frames between shots (~0.2s at 60fps)
car.bulletsLeft = Math.max(0, car.bulletsLeft - 1);
if (typeof bulletCounterTxt !== "undefined") {
bulletCounterTxt.setText("Bullets: " + car.bulletsLeft);
}
}
};
// Listen for keydown and keyup events using LK.on (simulated for PC)
// LK does not provide keyboard events, so we simulate with a global object for testing
// NOTE: In the FRVR/LK environment, keyboard events are not natively supported.
// For PC testing, you may need to use a custom event system or test with touch/mouse events.
// Here, we provide a fallback for environments that do support document events.
if (typeof document !== "undefined" && typeof document.addEventListener === "function") {
document.addEventListener('keydown', function (e) {
if (!runActive) return;
if (e.code === 'ArrowUp' || e.code === 'KeyW') keyState.up = true;
if (e.code === 'ArrowDown' || e.code === 'KeyS') keyState.down = true;
});
document.addEventListener('keyup', function (e) {
if (e.code === 'ArrowUp' || e.code === 'KeyW') keyState.up = false;
if (e.code === 'ArrowDown' || e.code === 'KeyS') keyState.down = false;
});
}
// Spawn zombies
function spawnZombie() {
// Randomly pick zombie type: 1/3 each
var zombieType = Math.floor(Math.random() * 3);
var zombie;
if (zombieType === 0) {
zombie = new Zombie();
} else if (zombieType === 1) {
zombie = new Zombie2();
} else {
zombie = new Zombie3();
}
zombie.x = 2048 + 100;
// Random vertical position on road
var minY = road.y + zombie.height / 2;
var maxY = road.y + road.height - zombie.height / 2;
zombie.y = minY + Math.random() * (maxY - minY);
zombies.push(zombie);
game.addChild(zombie);
}
// Spawn coins
function spawnCoin() {
var coin = new Coin();
coin.x = 2048 - 300;
// Random vertical position on road
var minY = road.y + coin.height / 2;
var maxY = road.y + road.height - coin.height / 2;
coin.y = minY + Math.random() * (maxY - minY);
coins.push(coin);
game.addChild(coin);
}
// Reset run
function startRun() {
// Remove all zombies and coins
for (var i = 0; i < zombies.length; ++i) zombies[i].destroy();
for (var i = 0; i < coins.length; ++i) coins[i].destroy();
zombies = [];
coins = [];
// Reset car
car.applyUpgrades();
car.x = 400;
car.y = 2200 + 100;
car.alive = true;
// Set bullet count based on weapon level
if (typeof car !== "undefined" && car.hasWeapon) {
if (storage.upgrade_weapon === 1) {
car.bulletsLeft = 5;
} else if (storage.upgrade_weapon === 2) {
car.bulletsLeft = 10;
} else if (storage.upgrade_weapon === 3) {
car.bulletsLeft = 15;
} else if (storage.upgrade_weapon === 4) {
car.bulletsLeft = 20;
} else if (storage.upgrade_weapon >= 5) {
car.bulletsLeft = 25;
} else {
car.bulletsLeft = 0;
}
} else {
car.bulletsLeft = 0;
}
// Reset distance
distance = 0;
runActive = true;
// Update GUI
distanceTxt.setText('0 m');
coinsTxt.setText(storage.coins + '');
// Reset bars
updateBars();
// Play music
LK.playMusic('bgmusic');
}
// Update fuel and health bars
function updateBars() {
// Fuel
var fuelFrac = car.fuel / car.maxFuel;
if (fuelFrac < 0) fuelFrac = 0;
if (fuelFrac > 1) fuelFrac = 1;
fuelBarFG.width = 400 * fuelFrac;
if (typeof fuelRemainTxt !== "undefined") {
fuelRemainTxt.setText(Math.round(car.fuel) + " / " + car.maxFuel);
}
// Health
var healthFrac = car.health / car.maxHealth;
if (healthFrac < 0) healthFrac = 0;
if (healthFrac > 1) healthFrac = 1;
// Health bar should decrease as health decreases
healthBarFG.width = 400 * healthFrac;
if (typeof healthRemainTxt !== "undefined") {
healthRemainTxt.setText(Math.round(car.health) + " / " + car.maxHealth);
}
}
// Game update
game.update = function () {
if (!runActive) return;
// PC controls: move car up/down with keyboard
var moveAmount = 18 + storage.upgrade_power * 1.5;
if (keyState.up) {
car.y -= moveAmount;
}
if (keyState.down) {
car.y += moveAmount;
}
// Clamp car to road
var minY = road.y + car.height / 2;
var maxY = road.y + road.height - car.height / 2;
if (car.y < minY) car.y = minY;
if (car.y > maxY) car.y = maxY;
// Move car forward
car.x += car.speed * 0.22;
if (car.x > 800) car.x = 800; // Car stays at 800, world scrolls
// Scroll world: move all objects left by car.speed
for (var i = 0; i < zombies.length; ++i) zombies[i].x -= car.speed * 0.13;
for (var i = 0; i < coins.length; ++i) coins[i].x -= car.speed * 0.13;
// Fuel consumption (depletes 1.5x faster)
car.fuel -= (0.022 + 0.0015 * car.speed) * 1.5;
if (car.fuel < 0) car.fuel = 0;
// Distance
distance += car.speed * 0.09;
distanceTxt.setText(parseInt(distance / 10) + ' m');
// Update speed text (show as integer, km/h style)
if (typeof speedTxt !== "undefined") {
speedTxt.setText(Math.round(car.speed * 7.5) + " km/h");
// Position speedTxt further to the left of distanceTxt and vertically aligned
speedTxt.x = distanceTxt.x - distanceTxt.width / 2 - 100;
speedTxt.y = distanceTxt.y + 6;
}
// Spawn zombies
if (LK.ticks % 45 === 0) {
spawnZombie();
}
// Spawn coins
if (LK.ticks % 70 === 0) {
spawnCoin();
}
// Update zombies
for (var i = zombies.length - 1; i >= 0; --i) {
var z = zombies[i];
z.update();
// Remove if off screen
if (z.x < -200) {
z.destroy();
zombies.splice(i, 1);
continue;
}
// Bullet collision
for (var j = bullets.length - 1; j >= 0; --j) {
var b = bullets[j];
if (z.alive && b && b.intersects(z)) {
z.alive = false;
tween(z, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
z.destroy();
}
});
zombies.splice(i, 1);
b.destroy();
bullets.splice(j, 1);
// Optionally, add a visual effect for weapon hit
LK.effects.flashObject(z, 0xffff00, 200);
break;
}
}
// Collision with car
if (car.alive && z.alive && car.intersects(z)) {
z.alive = false;
LK.getSound('zombiehit').play();
tween(z, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
z.destroy();
}
});
zombies.splice(i, 1);
// Decrease car health when zombies hit the car
if (car.hasWeapon) {
// Instantly destroy zombie (already handled above), reduce car damage
car.takeDamage(10); // Always decrease by 10
// Optionally, add a visual effect for weapon hit
LK.effects.flashObject(z, 0xffff00, 200); // Flash zombie yellow for weapon hit
} else {
// Normal damage
car.takeDamage(10); // Always decrease by 10
}
// Flash car
LK.effects.flashObject(car, 0xff0000, 300);
// Bump car back
tween(car, {
x: car.x - 40
}, {
duration: 120,
easing: tween.easeOut
});
// If car dead, end run
if (!car.alive) {
endRun();
return;
}
}
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; --i) {
var b = bullets[i];
b.update();
if (b.x > 2048 + 100) {
b.destroy();
bullets.splice(i, 1);
}
}
// Update coins
for (var i = coins.length - 1; i >= 0; --i) {
var c = coins[i];
c.update();
// Remove if off screen
if (c.x < -100) {
c.destroy();
coins.splice(i, 1);
continue;
}
// Collect coin
if (!c.collected && car.intersects(c)) {
c.collected = true;
LK.getSound('coin').play();
storage.coins += 1;
coinsTxt.setText(storage.coins + '');
tween(c, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
onFinish: function onFinish() {
c.destroy();
}
});
coins.splice(i, 1);
}
}
// Update bars
updateBars();
// Update bullet counter GUI
if (typeof bulletCounterTxt !== "undefined" && typeof car !== "undefined" && car.hasWeapon) {
bulletCounterTxt.setText("Bullets: " + car.bulletsLeft);
} else if (typeof bulletCounterTxt !== "undefined") {
bulletCounterTxt.setText("");
}
// Decrement shoot cooldown
if (shootCooldown > 0) shootCooldown--;
// Out of fuel
if (car.fuel <= 0) {
car.fuel = 0;
endRun();
return;
}
};
// End run
function endRun() {
runActive = false;
// Save best distance
var meters = parseInt(distance / 10);
if (meters > storage.bestDistance) storage.bestDistance = meters;
// Show game over
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
LK.stopMusic();
}
// On game over, reset run
LK.on('gameover', function () {
startRun();
});
// On you win (not used, but for completeness)
LK.on('youwin', function () {
startRun();
});
// Garage menu logic
function showGarageMenu(onDone) {
// Pause run and world when garage menu is open
var wasActive = runActive;
runActive = false;
// Simple garage menu using Text2 and GUI overlay
// Remove any previous garage UI
if (typeof garageUI !== "undefined" && garageUI) {
LK.gui.center.removeChild(garageUI);
garageUI = null;
}
var garageUI = new Container();
// Title
var title = new Text2("Garage", {
size: 120,
fill: "#fff"
});
title.anchor.set(0.5, 0);
title.x = 0;
title.y = 0;
garageUI.addChild(title);
// Coin display
var coinsLabel = new Text2("Coins: " + storage.coins, {
size: 80,
fill: 0xFFD700
});
coinsLabel.anchor.set(0.5, 0);
coinsLabel.x = 0;
coinsLabel.y = 140;
garageUI.addChild(coinsLabel);
// Upgrade buttons and labels
var upgrades = [{
key: "upgrade_fuel",
label: "Fuel",
cost: 10,
y: 300,
desc: "+30 max fuel"
}, {
key: "upgrade_armor",
label: "Armor",
cost: 15,
y: 500,
desc: "+40 max health"
}, {
key: "upgrade_power",
label: "Power",
cost: 20,
y: 700,
desc: "+2 speed"
}, {
key: "upgrade_weapon",
label: "Weapon",
cost: 30,
y: 900,
desc: "Add weapon, +damage to zombies",
descSize: 54 // Make this description smaller
}];
var upgradeButtons = [];
for (var i = 0; i < upgrades.length; ++i) {
(function (i) {
var upg = upgrades[i];
// Label
var labelSize = upg.key === "upgrade_weapon" && upg.descSize ? upg.descSize : 70;
var upgLabel = new Text2(upg.label + " Lv." + (storage[upg.key] || 0) + " (" + upg.desc + ")", {
size: labelSize,
fill: "#fff"
});
upgLabel.anchor.set(0.5, 0);
upgLabel.x = 0;
upgLabel.y = upg.y;
garageUI.addChild(upgLabel);
// Cost
var costLabel = new Text2("Cost: " + upg.cost, {
size: 60,
fill: 0xFFD700
});
costLabel.anchor.set(0.5, 0);
costLabel.x = -200;
costLabel.y = upg.y + 90;
garageUI.addChild(costLabel);
// Upgrade button
var btn = LK.getAsset('fuelBarFG', {
anchorX: 0.5,
anchorY: 0.5,
x: 200,
y: upg.y + 120,
width: 220,
height: 80
});
btn.tint = 0x44ff44;
garageUI.addChild(btn);
// Button label
var btnLabel = new Text2("Upgrade", {
size: 50,
fill: "#222"
});
btnLabel.anchor.set(0.5, 0.5);
btnLabel.x = btn.x;
btnLabel.y = btn.y;
garageUI.addChild(btnLabel);
// Touch/click handler
btn.interactive = true;
btn.down = function (x, y, obj) {
if (storage.coins >= upg.cost) {
storage.coins -= upg.cost;
storage[upg.key] = (storage[upg.key] || 0) + 1;
coinsLabel.setText("Coins: " + storage.coins);
upgLabel.setText(upg.label + " Lv." + storage[upg.key] + " (" + upg.desc + ")");
}
};
upgradeButtons.push(btn);
})(i);
}
// (Start Run button removed)
// Back button for garage
var garageBackBtn = LK.getAsset('fuelBarFG', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 1250,
width: 400,
height: 120
});
garageBackBtn.tint = 0xcccccc;
garageUI.addChild(garageBackBtn);
var garageBackLabel = new Text2("Back", {
size: 70,
fill: "#222"
});
garageBackLabel.anchor.set(0.5, 0.5);
garageBackLabel.x = garageBackBtn.x;
garageBackLabel.y = garageBackBtn.y;
garageUI.addChild(garageBackLabel);
garageBackBtn.interactive = true;
garageBackBtn.down = function (x, y, obj) {
LK.gui.center.removeChild(garageUI);
garageUI = null;
// Resume previous state (main menu or game paused)
if (typeof onDone === "function") onDone();
};
// Center garage UI
garageUI.x = 0;
garageUI.y = -350; // Move the garage menu up by 350px (higher than before)
LK.gui.center.addChild(garageUI);
}
// --- Main Menu UI ---
var mainMenuUI = new Container();
// Title
var mainTitle = new Text2("Drive to Survive", {
size: 150,
fill: 0xF7E9A0
});
mainTitle.anchor.set(0.5, 0);
mainTitle.x = 0;
mainTitle.y = 0;
mainMenuUI.addChild(mainTitle);
// Play Button
var playBtn = LK.getAsset('fuelBarFG', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 400,
width: 500,
height: 140
});
playBtn.tint = 0x44bbff;
mainMenuUI.addChild(playBtn);
var playLabel = new Text2("Play", {
size: 90,
fill: "#fff"
});
playLabel.anchor.set(0.5, 0.5);
playLabel.x = playBtn.x;
playLabel.y = playBtn.y;
mainMenuUI.addChild(playLabel);
// Garage Button
var menuGarageBtn = LK.getAsset('fuelBarFG', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 600,
width: 500,
height: 140
});
menuGarageBtn.tint = 0x8888ff;
mainMenuUI.addChild(menuGarageBtn);
var menuGarageLabel = new Text2("Garage", {
size: 90,
fill: "#fff"
});
menuGarageLabel.anchor.set(0.5, 0.5);
menuGarageLabel.x = menuGarageBtn.x;
menuGarageLabel.y = menuGarageBtn.y;
mainMenuUI.addChild(menuGarageLabel);
// Shop Button
var shopBtn = LK.getAsset('fuelBarFG', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 800,
width: 500,
height: 140
});
shopBtn.tint = 0xffb347;
mainMenuUI.addChild(shopBtn);
var shopLabel = new Text2("Shop", {
size: 90,
fill: "#fff"
});
shopLabel.anchor.set(0.5, 0.5);
shopLabel.x = shopBtn.x;
shopLabel.y = shopBtn.y;
mainMenuUI.addChild(shopLabel);
// Settings Button
var settingsBtn = LK.getAsset('fuelBarFG', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 1000,
width: 500,
height: 140
});
settingsBtn.tint = 0xcccccc;
mainMenuUI.addChild(settingsBtn);
var settingsLabel = new Text2("Settings", {
size: 90,
fill: "#222"
});
settingsLabel.anchor.set(0.5, 0.5);
settingsLabel.x = settingsBtn.x;
settingsLabel.y = settingsBtn.y;
mainMenuUI.addChild(settingsLabel);
// --- Shop Menu UI ---
var shopUI = new Container();
var shopTitle = new Text2("Car Skins", {
size: 120,
fill: "#fff"
});
shopTitle.anchor.set(0.5, 0);
shopTitle.x = 0;
shopTitle.y = -260; // moved up by 40px (was -220)
shopUI.addChild(shopTitle);
// Define available skins
var carSkins = [{
key: "default",
name: "Classic",
asset: "car",
price: 0
}, {
key: "red",
name: "Red Racer",
asset: "car",
price: 80,
tint: 0xff4444
}, {
key: "blue",
name: "Blue Bolt",
asset: "car",
price: 120,
tint: 0x4488ff
}, {
key: "green",
name: "Green Machine",
asset: "car",
price: 160,
tint: 0x44ff44
}];
// Persistent storage for owned skins and selected skin
if (!storage.carSkins) storage.carSkins = {
"default": true
};
if (!storage.selectedCarSkin) storage.selectedCarSkin = "default";
// Coin display in shop
var shopCoinsLabel = new Text2("Coins: " + storage.coins, {
size: 80,
fill: 0xFFD700
});
shopCoinsLabel.anchor.set(0.5, 0);
shopCoinsLabel.x = 0;
shopCoinsLabel.y = -120; // moved up by 60px (was -60)
shopUI.addChild(shopCoinsLabel);
// Skin buttons
var skinButtons = [];
for (var i = 0; i < carSkins.length; ++i) {
(function (i) {
var skin = carSkins[i];
// Increase vertical spacing between skins
var y = 60 + i * 220; // moved all skins up by 80px (was 140 + i * 220)
// Car preview
var preview = LK.getAsset(skin.asset, {
anchorX: 0.5,
anchorY: 0.5,
x: -320,
y: y,
width: 320,
height: 120
});
if (skin.tint) preview.tint = skin.tint;
shopUI.addChild(preview);
// Skin name
var nameLabel = new Text2(skin.name, {
size: skin.key === "green" ? 56 : 70,
fill: "#fff"
});
nameLabel.anchor.set(0, 0.5);
nameLabel.x = -120;
nameLabel.y = y;
shopUI.addChild(nameLabel);
// Price or "Owned"
var priceLabel = new Text2(storage.carSkins[skin.key] ? "Owned" : "Price: " + skin.price, {
size: 60,
fill: storage.carSkins[skin.key] ? "#44ff44" : "#FFD700"
});
priceLabel.anchor.set(0.5, 1);
priceLabel.x = 400;
priceLabel.y = y - 50; // 50px above the button
shopUI.addChild(priceLabel);
// Select/Buy button
var btn = LK.getAsset('fuelBarFG', {
anchorX: 0.5,
anchorY: 0.5,
x: 400,
y: y,
width: 220,
height: 80
});
btn.tint = storage.selectedCarSkin === skin.key ? 0x44bbff : 0xcccccc;
shopUI.addChild(btn);
var btnLabel = new Text2(storage.carSkins[skin.key] ? storage.selectedCarSkin === skin.key ? "Selected" : "Select" : "Buy", {
size: 50,
fill: "#222"
});
btnLabel.anchor.set(0.5, 0.5);
btnLabel.x = btn.x;
btnLabel.y = btn.y;
shopUI.addChild(btnLabel);
btn.interactive = true;
btn.down = function (x, y, obj) {
if (storage.carSkins[skin.key]) {
// Select skin
storage.selectedCarSkin = skin.key;
// Update all button tints and labels
for (var j = 0; j < skinButtons.length; ++j) {
var b = skinButtons[j];
b.btn.tint = carSkins[j].key === skin.key ? 0x44bbff : 0xcccccc;
b.btnLabel.setText(storage.carSkins[carSkins[j].key] ? carSkins[j].key === skin.key ? "Selected" : "Select" : "Buy");
}
} else if (storage.coins >= skin.price) {
// Buy skin
storage.coins -= skin.price;
storage.carSkins[skin.key] = true;
shopCoinsLabel.setText("Coins: " + storage.coins);
priceLabel.setText("Owned");
priceLabel.setStyle({
fill: 0x44FF44
});
btnLabel.setText("Select");
}
};
skinButtons.push({
btn: btn,
btnLabel: btnLabel
});
})(i);
}
// Back button for shop
var shopBackBtn = LK.getAsset('fuelBarFG', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -20 + carSkins.length * 220,
// moved up by 80px (was 60 + carSkins.length * 220)
width: 400,
height: 120
});
shopBackBtn.tint = 0xcccccc;
shopUI.addChild(shopBackBtn);
var shopBackLabel = new Text2("Back", {
size: 70,
fill: "#222"
});
shopBackLabel.anchor.set(0.5, 0.5);
shopBackLabel.x = shopBackBtn.x;
shopBackLabel.y = shopBackBtn.y;
shopUI.addChild(shopBackLabel);
shopBackBtn.interactive = true;
shopBackBtn.down = function (x, y, obj) {
LK.gui.center.removeChild(shopUI);
LK.gui.center.addChild(mainMenuUI);
};
// Music On/Off Button (in settings menu)
var settingsMenuUI = new Container();
var musicOn = true;
// Reset Button (above music)
var resetBtn = LK.getAsset('fuelBarFG', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 60,
// 140px above music button
width: 400,
height: 120
});
resetBtn.tint = 0xff4444;
settingsMenuUI.addChild(resetBtn);
var resetLabel = new Text2("Reset Progress", {
size: 70,
fill: "#fff"
});
resetLabel.anchor.set(0.5, 0.5);
resetLabel.x = resetBtn.x;
resetLabel.y = resetBtn.y;
settingsMenuUI.addChild(resetLabel);
resetBtn.interactive = true;
resetBtn.down = function (x, y, obj) {
// Show confirmation dialog before resetting progress
var confirmDialog = new Container();
// Dialog background
var dialogBG = LK.getAsset('fuelBarBG', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
width: 900,
height: 420
});
dialogBG.tint = 0x222222;
confirmDialog.addChild(dialogBG);
// Confirmation text
var confirmText = new Text2("Are you sure?\nAll your progress will be reset", {
size: 64,
fill: "#fff"
});
confirmText.anchor.set(0.5, 0.5);
confirmText.x = 0;
confirmText.y = -60;
confirmDialog.addChild(confirmText);
// Yes button
var yesBtn = LK.getAsset('fuelBarFG', {
anchorX: 0.5,
anchorY: 0.5,
x: -180,
y: 100,
width: 300,
height: 100
});
yesBtn.tint = 0xff4444;
confirmDialog.addChild(yesBtn);
var yesLabel = new Text2("Yes", {
size: 60,
fill: "#fff"
});
yesLabel.anchor.set(0.5, 0.5);
yesLabel.x = yesBtn.x;
yesLabel.y = yesBtn.y;
confirmDialog.addChild(yesLabel);
// No button
var noBtn = LK.getAsset('fuelBarFG', {
anchorX: 0.5,
anchorY: 0.5,
x: 180,
y: 100,
width: 300,
height: 100
});
noBtn.tint = 0x44bbff;
confirmDialog.addChild(noBtn);
var noLabel = new Text2("No", {
size: 60,
fill: "#fff"
});
noLabel.anchor.set(0.5, 0.5);
noLabel.x = noBtn.x;
noLabel.y = noBtn.y;
confirmDialog.addChild(noLabel);
// Yes button handler
yesBtn.interactive = true;
yesBtn.down = function (x, y, obj) {
// Reset all persistent progress
storage.coins = 0;
storage.bestDistance = 0;
storage.upgrade_fuel = 0;
storage.upgrade_armor = 0;
storage.upgrade_power = 0;
storage.upgrade_weapon = 0;
storage.carSkins = {
"default": true
};
storage.selectedCarSkin = "default";
LK.effects.flashScreen(0xff4444, 400);
// Remove dialog
LK.gui.center.removeChild(confirmDialog);
};
// No button handler
noBtn.interactive = true;
noBtn.down = function (x, y, obj) {
LK.gui.center.removeChild(confirmDialog);
};
// Center dialog
confirmDialog.x = 0;
confirmDialog.y = 0;
LK.gui.center.addChild(confirmDialog);
};
// Music Button
var musicBtn = LK.getAsset('fuelBarFG', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 200,
width: 400,
height: 120
});
musicBtn.tint = 0x44ff88;
settingsMenuUI.addChild(musicBtn);
var musicLabel = new Text2("Music: On", {
size: 70,
fill: "#222"
});
musicLabel.anchor.set(0.5, 0.5);
musicLabel.x = musicBtn.x;
musicLabel.y = musicBtn.y;
settingsMenuUI.addChild(musicLabel);
// Language selection button
var languageBtn = LK.getAsset('fuelBarFG', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 340,
width: 400,
height: 120
});
languageBtn.tint = 0xffe066;
settingsMenuUI.addChild(languageBtn);
var languageOptions = ["English", "Türkçe"];
var languageIndex = 0;
var languageLabel = new Text2("Language: " + languageOptions[languageIndex], {
size: 70,
fill: "#222"
});
languageLabel.anchor.set(0.5, 0.5);
languageLabel.x = languageBtn.x;
languageLabel.y = languageBtn.y;
settingsMenuUI.addChild(languageLabel);
languageBtn.interactive = true;
languageBtn.down = function (x, y, obj) {
languageIndex = (languageIndex + 1) % languageOptions.length;
languageLabel.setText("Language: " + languageOptions[languageIndex]);
// Here you could add logic to update UI text based on language selection
};
// Back button for settings
var settingsBackBtn = LK.getAsset('fuelBarFG', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 520,
width: 400,
height: 120
});
settingsBackBtn.tint = 0xcccccc;
settingsMenuUI.addChild(settingsBackBtn);
var settingsBackLabel = new Text2("Back", {
size: 70,
fill: "#222"
});
settingsBackLabel.anchor.set(0.5, 0.5);
settingsBackLabel.x = settingsBackBtn.x;
settingsBackLabel.y = settingsBackBtn.y;
settingsMenuUI.addChild(settingsBackLabel);
// Center main menu UI
mainMenuUI.x = 0;
mainMenuUI.y = -350;
LK.gui.center.addChild(mainMenuUI);
// Hide all game UI and pause game while in menu
runActive = false;
// Play button handler
playBtn.interactive = true;
playBtn.down = function (x, y, obj) {
LK.gui.center.removeChild(mainMenuUI);
runActive = true;
startRun();
};
// Garage button handler
menuGarageBtn.interactive = true;
menuGarageBtn.down = function (x, y, obj) {
LK.gui.center.removeChild(mainMenuUI);
showGarageMenu(function () {
// After garage, return to main menu
LK.gui.center.addChild(mainMenuUI);
runActive = false;
});
};
// Shop button handler
shopBtn.interactive = true;
shopBtn.down = function (x, y, obj) {
LK.gui.center.removeChild(mainMenuUI);
LK.gui.center.addChild(shopUI);
};
// Settings button handler
settingsBtn.interactive = true;
settingsBtn.down = function (x, y, obj) {
LK.gui.center.removeChild(mainMenuUI);
LK.gui.center.addChild(settingsMenuUI);
};
// Music on/off handler
musicBtn.interactive = true;
musicBtn.down = function (x, y, obj) {
musicOn = !musicOn;
if (musicOn) {
musicLabel.setText("Music: On");
LK.playMusic('bgmusic');
} else {
musicLabel.setText("Music: Off");
LK.stopMusic();
}
};
// Settings back button handler
settingsBackBtn.interactive = true;
settingsBackBtn.down = function (x, y, obj) {
LK.gui.center.removeChild(settingsMenuUI);
LK.gui.center.addChild(mainMenuUI);
};
// --- End Main Menu UI ---
// Remove original auto-start garage menu and run
// Show garage menu before starting run
// showGarageMenu(function () {
// startRun();
// });;