Shops and money¶
A shop zone that opens the shop page when a player walks in, purchases checked and charged by the server, and a working Pay 'n' Spray.
shops.ts
import { Server, Weapon, GarageType, type Player } from "@sagl/server";
const items: Record<string, { name: string; price: number; give: (p: Player) => void }> = {
pistol: { name: "Pistol", price: 200, give: (p) => p.giveWeapon(Weapon.Pistol, 60) },
shotgun: { name: "Shotgun", price: 600, give: (p) => p.giveWeapon(Weapon.Shotgun, 30) },
armour: { name: "Body armour", price: 100, give: (p) => (p.armour = 100) },
};
export function shops(server: Server) {
// Inside Ammu-Nation: the game's door marker (downtown, 1368 -1279) takes
// players in, and they arrive here.
const ammu = server.createZone({ center: { x: 286, y: -40, z: 1001.5 }, radius: 5 });
ammu.data.shop = "ammu";
server.map.setMarker("ammu", { x: 1368, y: -1279, icon: 6, label: "Ammu-Nation" });
const shop = server.channel("shop");
server.on("zoneEnter", (player, zone) => {
if (zone.data.shop) {
shop.send(player, {
open: true,
items: Object.entries(items).map(([id, i]) => ({ id, name: i.name, price: i.price })),
});
}
});
server.on("zoneLeave", (player, zone) => {
if (zone.data.shop) shop.send(player, { open: false });
});
// The client asks; the server decides.
shop.on((player, data) => {
const item = items[(data as { buy?: string }).buy ?? ""];
if (!item) return;
if (player.money < item.price) {
shop.send(player, { status: "You can't afford that." });
return;
}
player.giveMoney(-item.price);
item.give(player);
shop.send(player, { status: `Bought: ${item.name}` });
});
// Pay 'n' Spray: $100 for a repair and a new colour, and two stars off.
for (const garage of server.garages.filter((g) => g.type === GarageType.PayNSpray)) {
garage.open();
server.on("zoneEnter", (player, zone) => {
const car = player.vehicle;
if (zone !== garage.zone || !car || player.seat !== 0) return;
if (player.money < 100) return player.send("You need $100.");
player.giveMoney(-100);
car.repair();
car.colors = [Math.floor(Math.random() * 127), Math.floor(Math.random() * 127)];
player.wantedLevel = Math.max(0, player.wantedLevel - 2);
});
}
}