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