Skip to content

Map markers

Markers from a data file

Shops listed in a client file, put on the map once when the script runs.

client/data/places.json
[
  { "id": "ammu-downtown", "name": "Ammu-Nation", "x": 1368, "y": -1279, "icon": 6 },
  { "id": "burger-mall",   "name": "Burger Shot", "x": 1199, "y": -918,  "icon": 10 },
  { "id": "pns-idlewood",  "name": "Pay 'n' Spray", "x": 2073, "y": -1831, "icon": 63 }
]
client/scripts/places.js
const places = JSON.parse(sagl.files.read("data/places.json") ?? "[]");
for (const p of places) {
  sagl.map.setMarker(p.id, { x: p.x, y: p.y, icon: p.icon, label: p.name });
}

Teammates

The gamemode sends teammates' positions every few seconds; the script keeps one marker per teammate.

client/scripts/team-map.js
let shown = new Set();
sagl.on("team:positions", (mates) => {
  const now = new Set();
  for (const m of mates) {
    sagl.map.setMarker(`mate-${m.id}`, { x: m.x, y: m.y, color: "#40c040", label: m.name });
    now.add(`mate-${m.id}`);
  }
  for (const id of shown) if (!now.has(id)) sagl.map.removeMarker(id);
  shown = now;
});
index.ts (gamemode)
setInterval(() => {
  for (const p of server.players) {
    const mates = server.players
      .filter((o) => o !== p && o.data.team === p.data.team)
      .map((o) => ({ id: o.id, name: o.name, x: o.position.x, y: o.position.y }));
    p.emit("team:positions", mates);
  }
}, 3000);

An interior map

Inside a building, show only its floor plan, GTA V style; put the world back when they leave.

client/scripts/interior.js
sagl.on("interior", ({ inside, plan }) => {
  if (inside) {
    sagl.map.setWorld(false, "#000000");
    sagl.map.setOverlay("floor", { ...plan, file: plan.file });
  } else {
    sagl.map.removeOverlay("floor");
    sagl.map.setWorld(true);
  }
});
index.ts (gamemode)
player.emit("interior", {
  inside: true,
  plan: { x1: 2300, y1: -20, x2: 2330, y2: 10, file: "maps/bank-interior.png" },
});