AI Core
API-ReferenzLib: Client

Interaktionspunkte

Points - jeder Ort, den ein Spieler nutzt (Theken, Shops, Türen, NPCs, Geldautomaten, Kofferräume), als Prompt oder Target-Option, mit Peds, Blips und Punkten aus Panel-Einträgen.

Ein Punkt ist jeder Ort, den ein Spieler nutzt: eine Ladentheke, eine Tür, ein NPC, ein Rückgabeplatz, jeder Geldautomat, der Kofferraum jedes Autos. Du beschreibst ihn einmal (wo, welcher Text, was passiert), und die Lib zeichnet ihn so, wie der Serverbetreiber es gewählt hat.

Prompt oder Target

Der Betreiber wählt den Interaktionsstil im Admin-Panel von ai-core (Interaction). Dasselbe onPressed läuft in beiden Fällen, daher ist es deinem Skript egal, welcher aktiv ist.

StilWas der Spieler sieht
PromptEin „Drücke E“-Feld rechts auf dem Bildschirm, solange der Spieler im Radius des Punkts steht. Die Taste ist ein einziges neu belegbares Keybind in ai-lib (Standard E), daher fragt nichts in jedem Frame Tasten ab.
TargetEine Option im Target-Auge auf der Stelle, dem NPC, dem Objekt oder dem Fahrzeug. Dafür muss eines der Target-Skripte laufen, für die die Lib einen Adapter hat. Wenn mehrere laufen, gewinnt das erste in dieser Reihenfolge: ai-target, tgiann-radialmenu-target, tgiann-target, sleepless_interact, ox_target, qb-target, interact.

Wenn der Betreiber Target gewählt hat, aber kein Target-Skript läuft, ist jeder Punkt ein Prompt. Ein Punkt kann die Wahl des Betreibers überschreiben: promptOnly behält einen Prompt (Dinge, die aus einem Fahrzeug heraus getan werden: Zapfsäulen, Waschanlagen), preferTarget nutzt eine Target-Option, sobald ein Target-Skript läuft (Sitze). Wenn ein Target-Skript startet oder stoppt oder der Betreiber den Stil wechselt, zieht jeder Punkt von selbst um.

Es läuft immer nur eine Punktaktion deiner Ressource zur Zeit. Wenn onPressed ein Promise zurückgibt, ignorieren die anderen Punkte deiner Ressource Tastendrücke, bis es abgeschlossen ist, daher brauchst du nie ein eigenes busy-Flag. Der Spieler muss außerdem im Moment des Drückens canShow bestehen.

new Points

Erstellt eine Gruppe von Punkten. Erstelle eine Gruppe pro Feature am Anfang der Datei und füge ihr Punkte hinzu.

Seite: Client · Wartet: nein

Signatur
new Points()
Lua
local points = aiLib.Points.new()

Rückgabewert

Eine Points-Gruppe mit den Methoden unten.

Beispiel

import { Points } from "@aicore-fivem/lib/client";

const points = new Points();

points.add

Fügt einen Punkt an einem Ort hinzu oder ersetzt den Punkt mit derselben ID. Ein Punkt kann seinen eigenen NPC (ped) und Karten-Blip (blip) mitbringen.

Seite: Client · Wartet: nein

Signatur
points.add(id: string, options: PointOptions): void
Lua
points:add(id, options)

Parameter

Prop

Type

PointOptions

Prop

Type

PointPed

Das Feld ped nimmt die Optionen von LocalPed außer onSpawn und onDespawn (die der Punkt nutzt):

Prop

Type

PointBlip

Das Feld blip nimmt die Blip-Optionen; coords ist standardmäßig der Punkt:

Prop

Type

PromptAnchor

Was eine world-Funktion zurückgibt (PromptAnchor), eines von:

Prop

Type

Rückgabewert

void.

Beispiel

resource/client/index.ts
import { callAction, createLocale, hasJob, onMembershipChange, Points } from "@aicore-fivem/lib/client";

const Locale = createLocale<typeof import("../../locales/en.json")>();
const points = new Points();

// Eine Ladentheke mit Ladenbesitzer und Karten-Blip.
points.add("shop", {
  coords: { x: 25.7, y: -1347.3, z: 29.5 },
  label: Locale("OPEN_SHOP"),
  icon: "basket-shopping",
  ped: { model: "mp_m_shopkeep_01", coords: { x: 24.5, y: -1346.6, z: 29.5, heading: 270 } },
  blip: { sprite: 52, color: 2, label: Locale("SHOP") },
  onPressed: () => callAction("myshop:open", [], { fallback: "SHOP_FAILED" }),
});

// Ein Lager, das nur die Polizei öffnen kann; der Text folgt einem Zustand.
let locked = true;
points.add("evidence", {
  coords: { x: 474.9, y: -996.4, z: 26.3 },
  label: () => (locked ? Locale("UNLOCK") : Locale("LOCK")),
  icon: "box-archive",
  canShow: () => hasJob(["police"], 2),
  onPressed: async () => {
    const reply = await callAction("myscript:toggle", [], { fallback: "TOGGLE_FAILED" });
    if (reply) locked = !locked;
    points.refresh("evidence");
  },
});

// canShow liest den Job: erneut prüfen, wenn sich Jobs, Dienst oder Gangs ändern.
onMembershipChange(() => points.refresh());

canShow entscheidet nur, was der Spieler sieht. Der Server muss in seinem eigenen Handler erneut prüfen (Job, Entfernung, Bestand). Ein Cheater kann dein Server-Event ohne jeden Punkt aufrufen.

points.addModel

Fügt einen Punkt auf jedem Weltobjekt bestimmter Modelle hinzu (Geldautomaten, Verkaufsautomaten, Müllcontainer, Stühle) oder ersetzt den Punkt mit derselben ID. Im Target-Modus ist es eine Target-Option auf diesen Modellen; im Prompt-Modus erscheint ein Prompt in der Nähe des nächsten. Die Suche nach dem nächsten Objekt läuft einmal in ai-lib für jede Ressource, zweimal pro Sekunde, und pausiert, solange der Spieler stillsteht.

Seite: Client · Wartet: nein

Signatur
points.addModel(id: string, options: ModelPointOptions): void
Lua
points:addModel(id, options)

Parameter

Prop

Type

ModelPointOptions

Prop

Type

Rückgabewert

void.

Beispiel

import { callAction, createLocale, Points } from "@aicore-fivem/lib/client";

const Locale = createLocale<typeof import("../../locales/en.json")>();
const points = new Points();

points.addModel("atm", {
  models: ["prop_atm_01", "prop_atm_02", "prop_atm_03", "prop_fleeca_atm"],
  label: Locale("USE_ATM"),
  icon: "credit-card",
  onPressed: (atm) => callAction("mybank:atm", [NetworkGetNetworkIdFromEntity(atm)], { fallback: "ATM_FAILED" }),
});

Zwei Zustände eines Objekts (ein Boot festmachen und losmachen) sind zwei Modellpunkte mit gegensätzlichem canShow, nicht eine Label-Funktion. Ein Punkt mit mehr als 6 Modellen findet seine Objekte über einen Index des Objekt-Pools, daher kosten Hunderte von Modellen so viel wie ein paar.

points.addVehicle

Fügt einen Punkt auf jedem vernetzten Fahrzeug hinzu (Kofferraum, Motorhaube, schieben, reparieren) oder ersetzt den Punkt mit derselben ID. Im Target-Modus ist es die globale Fahrzeugoption des Target-Skripts; im Prompt-Modus erscheint ein Prompt am nächsten vernetzten Fahrzeug innerhalb von radius.

Seite: Client · Wartet: nein

Signatur
points.addVehicle(id: string, options: VehiclePointOptions): void
Lua
points:addVehicle(id, options)

Parameter

Prop

Type

Rückgabewert

void.

Beispiel

import { cache, createLocale, Points } from "@aicore-fivem/lib/client";

const Locale = createLocale<typeof import("../../locales/en.json")>();
const points = new Points();

points.addVehicle("trunk", {
  label: Locale("OPEN_TRUNK"),
  icon: "box-open",
  radius: 2.5,
  canShow: (vehicle) => vehicle !== 0 && cache.vehicle === false && GetVehicleDoorLockStatus(vehicle) < 2,
  onPressed: (vehicle) => SetVehicleDoorOpen(vehicle, 5, false, false),
});

points.remove

Entfernt einen Punkt mit seinem Prompt, seiner Target-Option, seinem NPC und Blip. Funktioniert für Punkte, die mit add, addModel und addVehicle erstellt wurden. Eine unbekannte ID tut nichts.

Seite: Client · Wartet: nein

Signatur
points.remove(id: string): void
Lua
points:remove(id)

Parameter

Prop

Type

Rückgabewert

void.

Beispiel

points.remove("shop");

points.clear

Entfernt jeden Punkt der Gruppe.

Seite: Client · Wartet: nein

Signatur
points.clear(): void
Lua
points:clear()

Rückgabewert

void.

Beispiel

onServerEvent("myscript:jobEnded", () => points.clear());

Baue Panel-Punkte nie bei jeder Panel-Änderung mit clear() + alles neu hinzufügen auf: Bei Tausenden von Einträgen würde eine Änderung auf jedem Client alle neu aufbauen. Nutze RecordObjects: Eine Änderung baut nur ihren eigenen Eintrag neu auf.

points.refresh

Liest canShow und Label-Funktionen erneut. Rufe es auf, wenn sich etwas geändert hat, das sie lesen: der Job, der Dienst, ein Fahrzeug, ein Zustand, den der Server gesendet hat. Prompts aktualisieren sich sofort; Target-Optionen lesen canShow, wenn das Auge öffnet, und ein geändertes Label wird neu gesetzt.

Seite: Client · Wartet: nein

Signatur
points.refresh(id?: string): void
Lua
points:refresh() -- jeder Punkt der Gruppe

Parameter

Prop

Type

Rückgabewert

void.

Beispiel

import { EntityState, onCache, onMembershipChange, Points, stateBoolean } from "@aicore-fivem/lib/client";

const points = new Points();
const robbed = new EntityState("robbed", stateBoolean);

onCache("vehicle", () => points.refresh());
onMembershipChange(() => points.refresh());
// Eine Kasse hat sich geändert: nur ihren Punkt aktualisieren.
robbed.onChange((netId) => points.refresh(`register:${netId}`));

In einem rohen AddStateBagChangeHandler ist der neue Wert noch nicht lesbar: Rufe refresh dort in einem setTimeout(..., 0) auf. Die State-Klassen der Lib (EntityState.onChange) geben dir den neuen Wert bereits.

points.isInside

Ob der Spieler gerade im Radius eines Punkts steht (Punkte, die mit add erstellt wurden).

Seite: Client · Wartet: nein

Signatur
points.isInside(id: string): boolean

Kein Lua-Zwilling.

Parameter

Prop

Type

Rückgabewert

boolean: true, solange der Spieler drin ist; false draußen, oder bei einer unbekannten ID oder einem Modellpunkt.

Beispiel

if (points.isInside("garage")) notify(Locale("PARK_HERE"), "info");

points.restyle

Stellt jeden Punkt der Gruppe auf den aktuellen Interaktionsstil um. Die Lib ruft es selbst auf, wenn der Betreiber den Stil wechselt oder ein Target-Skript startet oder stoppt; du brauchst es nie.

Seite: Client · Wartet: nein

Signatur
points.restyle(): void

Kein Lua-Zwilling (Lua stellt ebenfalls von selbst um).

Rückgabewert

void.

Punkte aus Panel-Einträgen

Die meisten Punkte kommen aus einer Records-Datei des Admin-Panels deines Skripts (Shops, Garagen, Mietschalter). Spiegle die Datei auf dem Client mit AdminWorld und baue dann die Punkte jedes Eintrags mit RecordObjects: Ein bearbeiteter Eintrag baut nur seine eigenen Punkte neu auf, ein gelöschter entfernt sie.

new RecordObjects

Hält die Weltobjekte einer Records-Datei (Punkte, Blips, Peds, Zonen) Eintrag für Eintrag im Gleichschritt mit der Datei. build(key, record) erstellt die Objekte eines Eintrags und gibt zurück, wie sie entfernt werden. Es baut jeden Eintrag in seinem Konstruktor.

Seite: Client und Server (aus @aicore-fivem/lib/shared) · Wartet: nein

Signatur
new RecordObjects<T>(
  source: RecordSource,
  file: string,
  records: RecordReader<T> | ReadonlyMap<string, T>,
  build: (key: string, record: T) => (() => void) | void,
)

Kein Lua-Zwilling.

Parameter

Prop

Type

Mitglieder

Prop

Type

Beispiel

resource/client/index.ts
import { AdminWorld, callAction, createLocale, Points } from "@aicore-fivem/lib/client";
import { readVec3, RecordObjects, str } from "@aicore-fivem/lib/shared";

const Locale = createLocale<typeof import("../../locales/en.json")>();
const world = new AdminWorld(["settings", "shops"]);
const points = new Points();

interface Shop { label: string; coords: { x: number; y: number; z: number } }
const readShop = (_key: string, raw: Record<string, unknown>): Shop | null => {
  const coords = readVec3(raw.coords);
  return coords ? { label: str(raw.label, "Shop"), coords } : null;
};

const shops = new RecordObjects(world, "shops", readShop, (id, shop) => {
  points.add(id, {
    coords: shop.coords,
    label: Locale("OPEN_SHOP", shop.label),
    onPressed: () => callAction("myshop:open", [id], { fallback: "SHOP_FAILED" }),
  });
  return () => points.remove(id);
});

// Eine Einstellung, die jeder Shop liest, hat sich geändert: alle neu bauen.
world.onChange("settings", () => shops.rebuildAll());

RecordObjects baut in seinem Konstruktor. Erstelle es nach allem, was build liest, und nutze in build das Argument record: Ein Klassenfeld, das das RecordObjects hält, ist während dieses ersten Aufbaus noch nicht zugewiesen.

new AdminWorld

Die Client-Kopie der Welt-Panel-Dateien deines Skripts (die Dateien, deren Schema sync: "all" sagt, siehe Admin-Panel). Sie liest die Kopie, die FiveM beim Start gecacht hat, fragt den Server einmal nach den seitdem geänderten Einträgen und folgt dann Live-Änderungen. Sie fragt nie zyklisch ab.

Seite: Client · Wartet: nein (das ready-Promise sagt, wann die Änderungen seit dem Start angekommen sind)

Signatur
new AdminWorld(files: string[])

Kein Lua-Zwilling.

Parameter

Prop

Type

Mitglieder

Prop

Type

Beispiel

import { AdminWorld } from "@aicore-fivem/lib/client";
import { num, record } from "@aicore-fivem/lib/shared";

const world = new AdminWorld(["settings"]);
const settings = world.view("settings", (raw) => ({
  radius: num(record(raw).radius, 2),
}));

world.onChange("settings", () => console.log("new radius", settings.value.radius));

Auf dieser Seite