Skip to content

Crashes and vehicle hits

Everything that hits a vehicle comes through vehicleHit: crashes into other vehicles, players and peds, objects and walls (and the ground, when a landing did harm), and bullets, fire and explosions. Return false to undo a hit, or a number for the health it costs instead.

A no-crash zone

Vehicles inside a zone (a showroom, a spawn area) can't be damaged by anything.

no-crash.ts
import type { Server, Vehicle } from "@sagl/server";

export function noCrashZone(server: Server) {
  const showroom = server.createZone({ min: { x: 540, y: -1300, z: 10 }, max: { x: 570, y: -1270, z: 30 } });
  const inside = new Set<Vehicle>();

  server.on("zoneEnter", (player, zone) => { if (zone === showroom && player.vehicle) inside.add(player.vehicle); });
  server.on("zoneLeave", (player, zone) => { if (zone === showroom && player.vehicle) inside.delete(player.vehicle); });

  server.on("vehicleHit", (vehicle) => {
    if (inside.has(vehicle)) return false;   // health and dents put back
  });
}

Demolition derby

Only ramming other cars counts: walls and the ground do nothing, and the rammer scores what they did.

derby.ts
import type { Server, Player } from "@sagl/server";

export function derby(server: Server) {
  const score = new Map<Player, number>();

  server.on("vehicleHit", (vehicle, hit) => {
    if (hit.cause === "weapon") return false;             // no guns in the arena
    if (hit.with !== "vehicle") return false;             // walls are free
    if (hit.player && hit.player !== vehicle.driver) {
      score.set(hit.player, (score.get(hit.player) ?? 0) + Math.round(hit.loss));
    }
    return hit.loss * 1.5;                                // and hurt more
  });

  server.on("vehicleDeath", (vehicle, killer) => {
    const victim = vehicle.driver;
    if (killer && victim) server.broadcast(`${killer.name} wrecked ${victim.name}!`);
  });

  setInterval(() => {
    const top = [...score.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
    if (top.length) server.broadcast(`Leaders: ${top.map(([p, s]) => `${p.name} ${s}`).join(", ")}`);
  }, 30_000);
}

Hit-and-run reports

server.on("vehicleHit", (vehicle, hit) => {
  const driver = vehicle.driver;
  if (hit.cause === "collision" && hit.with === "ped" && hit.player && driver) {
    hit.player.send(`You were hit by ${driver.name}'s car.`);
    driver.wantedLevel = Math.max(driver.wantedLevel, 1);
  }
});

Bullet-proof cars

server.on("vehicleHit", (vehicle, hit) => {
  if (vehicle.data.armoured && hit.cause === "weapon") return false;
});