Skip to content

Objects, pickups and labels

import { GameObject, Pickup, Label } from "@sagl/server";

The server's own things in the world: objects (any of the game's object models, where you put them: crates, gates, ramps), pickups (weapons, health, armour, money, as the game's spinning ones) and floating text (as over the game's asset pickups).

Each is for everyone, players joining later too, or only visibleTo some players. A mission's are for its party, and go when it ends.

Objects

server.createObject(model, position, rotation?, options?)

createObject(model: number, position: Vector3, rotation?: Rotation, options?: ObjectOptions): GameObject

rotation is in degrees about x, y and z (z is its heading).

Option Type Description
collision boolean Default true.
health number Makes it breakable: players' shots and cars wear it down, and at 0 it's destroyed for everyone. Left out: nothing breaks it.
visibleTo Player[] Who sees it. Default everyone.
interior number The interior it's in (0 outside, the default); -1: every interior.
streamDistance number Made in each player's game only within this far of them (m). Default 300.

Returns a GameObject:

Member Description
id, model
exists False once destroyed.
position, rotation Where it is (or is moving to).
health Breakable ones' health, or Infinity.
move(position, rotation?, ms?) Glides it there over ms (smoothly at both ends, as a gate or a lift). 0 puts it there at once. Resolves when it's there.
collision Set: on or off.
onHit(fn) fn(player, damage, weapon) when a player hurts it (breakable ones), before it counts. Return false to ignore it, or a number for the damage it does instead. Returns it.
onDestroyed(fn) fn(by) when damage destroys it; by is the player who did it. Returns it.
destroy({ shatter? }) Takes it away for everyone. shatter breaks it apart first, as the game breaks glass or a cardboard box.
setMaterial(index, options), setMaterialText(index, text, options?), resetMaterial(index?) One of its model's materials drawn with another texture, tinted, or as text: see Custom models and textures.
// A gate that opens for a while.
const gate = server.createObject(980, { x: 1800, y: -2590, z: 15 }, { x: 0, y: 0, z: 90 });
async function open() {
  await gate.move({ x: 1800, y: -2590, z: 9 }, undefined, 3000);
  await new Promise((done) => setTimeout(done, 10_000));
  await gate.move({ x: 1800, y: -2590, z: 15 }, undefined, 3000);
}

// A crate to shoot open.
server.createObject(1271, { x: 1790, y: -2585, z: 13.9 }, undefined, { health: 100 })
  .onDestroyed((by) => by && (by.money += 500));

How they behave, by design:

  • The server's to move. Players' games never push, knock over or break one on their own. It stays put until move() (a car hits it as it would a wall). Objects moved by physics (a crate you can push along) aren't synced yet.
  • The server's to break. Each player's game reports the damage they do (their shots, their car) to the server, which checks it (they're within 200 m; at most 1000 a time), asks onHit, and at 0 health destroys it for everyone. Bullets do their weapon's damage (weapon is theirs). A crash does 5 for each m/s over 3 the car was going (about 50 at 50 km/h; weapon is 49, the game's "rammed by car").
  • Shattering uses the model's own breaking, as the game's object data has it: glass breaks up, a box smashes into wood chips. A model the game never breaks (a gun crate, a gate) just goes: for a crate to smash, use one the game smashes (1224 wooden box, 1220/1221/1230 cardboard boxes), or play an effect where it was.
  • Streamed. A game holds a few hundred objects at most, its own included, so each player's game makes only the server's objects within their streamDistance (and in the interior it's showing), the nearest 250 at most, and removes them as the player moves away (as SA-MP's streamer plugin). Place thousands; each player has the ones near them.
  • A model must be an object (not a ped's or vehicle's), or one of your own (custom models). Its position is where the model's origin goes, often its bottom, so set z on the ground.

Pickups

server.createPickup(options)

createPickup(options: PickupOptions): Pickup

Floating, spinning and glowing, as the game's. A player walks into it (or drives, with inVehicle), and the server, having checked they're there, gives them what it holds.

Option Type Description
position Vector3 Where it floats.
weapon, ammo number A weapon (Weapon) and its ammo (default 30). Its model, and given.
health number Brings their health up to at least this (the game's heart: 100).
armour number Brings their armour up to at least this.
money number Given.
model number Any model instead, for its look. Gives nothing unless onPickup does.
respawn number Back this long (ms) after it's taken. Left out: taken once, then gone.
inVehicle boolean Vehicles pick it up too. Default false.
visibleTo Player[] Who sees (and can take) it. Default everyone.
interior number The interior it's in (0 outside, the default); -1: every interior.
label string Floating text over it (see labels).

At least one of weapon, health, armour, money or model. The model is the game's: the weapon's, a heart, an armour vest or a cash bundle.

Returns a Pickup:

Member Description
id, options, position
exists False once removed (or taken, without respawn).
taken Taken, and not back yet.
onPickup(fn) fn(player) when a player takes it, before they get what it holds. Return false to refuse: it stays and they get nothing. Returns it.
remove() Takes it away for everyone.
server.createPickup({ position: { x: 2495, y: -1690, z: 13.5 }, health: 100, respawn: 30_000 });
server.createPickup({ position: { x: 2500, y: -1690, z: 13.5 }, weapon: Weapon.Shotgun, ammo: 20, respawn: 60_000 });

// A briefcase, for one player.
server.createPickup({ position: DROP, model: 1210, visibleTo: [player], label: "~y~The briefcase" })
  .onPickup((p) => {
    p.hud.objective("Take the case to ~y~the garage~s~.", 5000);
  });

Labels

server.labels.add(text, at, options?)

add(text: string, at: LabelTarget, options?: LabelOptions): Label

Floating text, as over the game's asset pickups: in the game's font, sized as if it were in the world (larger as you come up to it, smaller further away), fading out at its range and as you reach it. It's part of the world: what's in front of it (a wall, a car, the player) covers it.

at is a spot (Vector3) or something it follows: a Player, Ped, Vehicle, GameObject or Pickup. Game codes (~y~, ~k~~PED_FIREWEAPON~) and gxt() text work. Up to 255 characters.

Option Type Description
color MapColor 0xRRGGBBAA or "#RRGGBB". Default white.
distance number Seen up to this far (m). Default 20.
throughWalls boolean Drawn over everything instead. Default false.
offset Vector3 From what it's on. Default above it: a player's or ped's head, a vehicle's roof.
to Player[] Who sees it. Default everyone.
interior number The interior it's shown in (0 outside); -1: every interior. Default 0 at a spot, -1 on something (it goes where that goes).

Returns a Label:

Member Description
id
text Get, or set to change it.
color Set.
remove() Takes it away.

A label on something is hidden while that isn't in a player's game, and removed with it: a destroyed object or vehicle, a removed pickup, a player who leaves.

server.labels.add("~g~Ammu-Nation", { x: 1368, y: -1279, z: 14.5 }, { distance: 40 });
const tag = server.labels.add("~r~WANTED", player, { color: "#ff3030", throughWalls: true });
// later
tag.remove();