User prompt
Increase the coin drop rate a little more
User prompt
Replace desert one with Drive to Survive
User prompt
zombie3 can also come from the road
User prompt
Add an asset named zombie3
User prompt
Fuel depletes x1.50 faster than normal
User prompt
The vehicle's health will decrease by 10 by 10.
User prompt
Zombies should hit the car 10 times instead of 20 times
User prompt
delete the white button on the top left
User prompt
When you press the pause button on the top left, you will see 2 options: 1. garage, 2. main menu
User prompt
Please fix the bug: 'Uncaught TypeError: LK.reload is not a function' in or related to this line: 'LK.reload();' Line Number: 1223
User prompt
and if the user presses this button reset everything and reload.
User prompt
If the user presses this reset button, an option will be presented saying "Are you sure? All your progress will be reset".
User prompt
Add reset button above music in settings menu
User prompt
Move the car skins text up a little bit
User prompt
Move the text Car Skins and Coins in the Skins menu up a bit
/**** * 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; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Desert background // Car (player vehicle) // Zombie // Coin // Fuel Bar BG // Fuel Bar FG // Health Bar BG // Health Bar FG // Road // Sound effects // Music 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() { // 50% chance for each zombie type var zombie; if (Math.random() < 0.5) { zombie = new Zombie(); } else { zombie = new Zombie2(); } 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 car.fuel -= 0.022 + 0.0015 * car.speed; 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 % 90 === 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(8 - storage.upgrade_armor * 3); // Less damage to car // 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(20 - storage.upgrade_armor * 3); } // 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("Desert Run", { 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 = -220; // moved up further by 40px (was -180) 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; 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;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Desert background
// Car (player vehicle)
// Zombie
// Coin
// Fuel Bar BG
// Fuel Bar FG
// Health Bar BG
// Health Bar FG
// Road
// Sound effects
// Music
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() {
// 50% chance for each zombie type
var zombie;
if (Math.random() < 0.5) {
zombie = new Zombie();
} else {
zombie = new Zombie2();
}
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
car.fuel -= 0.022 + 0.0015 * car.speed;
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 % 90 === 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(8 - storage.upgrade_armor * 3); // Less damage to car
// 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(20 - storage.upgrade_armor * 3);
}
// 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("Desert Run", {
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 = -220; // moved up further by 40px (was -180)
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;
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();
// });;