AI Core
API-ReferenzLib: Client

Gemeinsame UI

notify, progress, menu, input, Prompts, skillCheck, lockpick und itemPicker - die Spieler-UI, die ai-lib für jede Ressource zeichnet.

ai-lib zeichnet eine gemeinsame UI für jede Ressource: Benachrichtigungskarten, den Fortschrittsbalken, die „Drücke E“-Prompts, Menüs, Eingabedialoge und Minispiele. Dein Skript fordert sie nur an, daher sieht jedes Skript auf dem Server gleich aus, und du schreibst dafür keine NUI. Die Item-Auswahl zeichnet ai-core.

Texte, die du übergibst, werden so angezeigt, wie sie sind: Übersetze sie zuerst (Locale("KEY") in TypeScript, t('KEY') in Lua).

Benachrichtigungen

notify

Zeigt eine Benachrichtigungskarte auf dem Bildschirm dieses Spielers.

Seite: Client · Wartet: nein

Signatur
notify(text: string, kind?: NotificationKind, duration?: number): void
Lua
aiLib.notify(text, kind, duration)

Parameter

Prop

Type

Der Typ von kind ist NotificationKind. Um einen Spieler vom Server aus zu benachrichtigen, nutze das Server-notify(source, text, kind).

Rückgabewert

void.

Beispiel

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

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

notify(Locale("WELCOME"));
notify(Locale("NO_MONEY"), "error");
notify(Locale("SAVED"), "success", 2000);

Fortschrittsbalken

progress

Führt eine zeitgesteuerte Aktion mit dem Fortschrittsbalken von ai-lib und einer optionalen Animation oder einem Szenario aus und wartet, bis sie endet. Sie endet vorzeitig (abgebrochen), wenn der Spieler stirbt, in ein Fahrzeug steigt, die Abbruchtaste von ai-lib drückt (Standard Backspace, nur mit canCancel) oder dein Skript cancelProgress() aufruft. Ein Fortschritt pro Ressource zur Zeit.

Seite: Client · Wartet: ja (Promise in TypeScript, hält in Lua den Thread an)

Signatur
progress(options: ProgressOptions): Promise<boolean>
Lua
local finished = aiLib.progress(options) -- boolean, innerhalb eines Threads aufrufen

Parameter

Prop

Type

ProgressOptions

Prop

Type

Rückgabewert

Promise<boolean>: true, wenn der Balken abgelaufen ist; false, wenn er abgebrochen wurde, oder sofort, wenn noch ein anderer Fortschritt deiner Ressource läuft. Lua gibt denselben Boolean zurück.

Beispiel

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

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

export async function repair(): Promise<void> {
  const vehicle = nearestVehicle(4, { notOwn: true });
  if (!vehicle) return notify(Locale("NO_VEHICLE"), "error");
  const [x = 0, y = 0, z = 0] = GetEntityCoords(vehicle, false);
  const done = await progress({
    label: Locale("REPAIRING"),
    duration: 8000,
    face: { x, y, z },
    anim: { dict: "mini@repair", clip: "fixing_a_player" },
    canCancel: true,
    disable: { move: true, combat: true },
  });
  if (!done) return notify(Locale("CANCELLED"), "error");
  await callAction("mymech:repair", [NetworkGetNetworkIdFromEntity(vehicle)], { fallback: "REPAIR_FAILED" });
}

Das Ergebnis ist das Wort des Clients: Ein Cheater kann den Balken überspringen. Wenn der Server für die Aktion bezahlt (Items, Geld, eine Reparatur), starte den Balken vom Server mit progressFor(source, options) (Client-Eingaben): Es antwortet nur mit true, wenn auch auf dem Server genug Zeit vergangen ist.

cancelProgress

Bricht den laufenden Fortschritt deiner Ressource ab, egal ob der Spieler ihn abbrechen darf. Der progress-Aufruf wird dann mit false erfüllt. Es passiert nichts, wenn kein Fortschritt läuft.

Seite: Client · Wartet: nein

Signatur
cancelProgress(): void
Lua
aiLib.progress.cancel()

Rückgabewert

void.

Beispiel

import { cancelProgress, onServerEvent } from "@aicore-fivem/lib/client";

// Der Server sagt, das Ziel wurde von jemand anderem ausgeraubt.
onServerEvent("myrob:taken", () => cancelProgress());

isProgressRunning

Ob gerade ein Fortschritt deiner Ressource läuft.

Seite: Client · Wartet: nein

Signatur
isProgressRunning(): boolean
Lua
local running = aiLib.progress.isRunning()

Rückgabewert

boolean: true, solange der Balken deiner Ressource läuft. Balken anderer Ressourcen zählen nicht.

Beispiel

import { isProgressRunning, menu } from "@aicore-fivem/lib/client";

export async function openMenu(): Promise<void> {
  if (isProgressRunning()) return; // beschäftigt
  await menu({ title: "Actions", items: [{ id: "a", label: "Action" }] });
}

Menüs und Dialoge

Zeigt das Menü von ai-lib und wartet auf die Wahl des Spielers. Pfeiltasten bewegen, Enter wählt. Einträge mit checked sind Schalterzeilen, die an Ort und Stelle umschalten, ohne das Menü zu schließen. In einem Untermenü (back: true) ergibt ein kurzes Escape MENU_BACK, damit du das vorherige Menü öffnen kannst; Escape gedrückt halten schließt.

Seite: Client · Wartet: ja (Promise in TypeScript, hält in Lua den Thread an)

Signatur
menu(request: MenuRequest, listeners?: ((item: string) => void) | MenuListeners): Promise<string | null>
Lua
local picked = aiLib.menu.show(request, listeners) -- string oder nil, innerhalb eines Threads aufrufen

Parameter

Prop

Type

Prop

Type

Prop

Type

Die Typen sind MenuRequest und MenuItem.

Prop

Type

Rückgabewert

Promise<string | null>: die gewählte Eintrags-ID; "__back" (MENU_BACK aus @aicore-fivem/shared) nach einem kurzen Escape in einem Untermenü; null, wenn der Spieler das Menü geschlossen hat. Lua gibt dasselbe zurück, nil für geschlossen.

Beispiel

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

const Locale = createLocale<typeof import("../../locales/en.json")>();
let engineOn = true;

export async function vehicleMenu(vehicle: number): Promise<void> {
  const pick = await menu({
    title: Locale("VEHICLE"),
    items: [
      { id: "doors", label: Locale("DOORS"), icon: "door-open" },
      { id: "engine", label: Locale("ENGINE"), icon: "power-off", checked: engineOn },
    ],
  }, {
    onToggle: (item, checked) => {
      if (item !== "engine") return;
      engineOn = checked;
      SetVehicleEngineOn(vehicle, checked, false, true);
    },
  });
  if (pick === "doors") await doorsMenu(vehicle);
}

async function doorsMenu(vehicle: number): Promise<void> {
  const pick = await menu({
    title: Locale("DOORS"),
    back: true,
    items: [0, 1, 2, 3].map((door) => ({ id: String(door), label: Locale("DOOR", door + 1) })),
  });
  if (pick === MENU_BACK) return vehicleMenu(vehicle);
  if (pick !== null) SetVehicleDoorOpen(vehicle, Number(pick), false, false);
}

Die gewählte ID kommt vom Client des Spielers: Der Server muss prüfen, dass sie eine gültige Wahl ist.

input

Zeigt den Eingabedialog von ai-lib und wartet auf die Werte. Jedes Feld ist ein Zahlenfeld oder ein Textfeld.

Seite: Client · Wartet: ja (Promise in TypeScript, hält in Lua den Thread an)

Signatur
input(request: InputRequest): Promise<Record<string, string | number> | null>
Lua
local values = aiLib.input(request) -- table oder nil, innerhalb eines Threads aufrufen

Parameter

Prop

Type

InputRequest

Prop

Type

InputField: number

Prop

Type

InputField: text

Prop

Type

Die Typen sind InputRequest und InputField.

Rückgabewert

Promise<Record<string, string | number> | null>: die eingegebenen Werte nach Feld-name, wenn der Spieler bestätigt hat; null, wenn er den Dialog geschlossen hat. Werte, die weder String noch Zahl sind, werden weggelassen. Lua gibt die Tabelle oder nil zurück.

Beispiel

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

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

export async function sendBill(target: number): Promise<void> {
  const values = await input({
    title: Locale("BILL"),
    confirm: Locale("SEND"),
    fields: [
      { name: "amount", label: Locale("AMOUNT"), type: "number", min: 1, max: 100000 },
      { name: "reason", label: Locale("REASON"), type: "text", maxLength: 80 },
    ],
  });
  if (!values) return;
  await callAction("mybill:send", [target, values.amount, values.reason], { fallback: "BILL_FAILED" });
}

min, max und maxLength leiten den Spieler nur. Der Server muss jeden Wert erneut prüfen (readInt, readText im Server-Handler).

itemPicker

Admin-Werkzeuge: zeigt die Item-Auswahl von ai-core. Sie listet jedes Item des aktiven Inventars mit Bild und einer Suche; Klicks füllen eine Liste rechts (1 pro Klick, 10 mit Strg, 100 mit Shift), die am Gewichtslimit des Ziels stoppt. Die Item-Liste wird nur an Admins gesendet.

Seite: Client · Wartet: ja (Promise in TypeScript, hält in Lua den Thread an)

Signatur
itemPicker(request: ItemPickerRequest): Promise<PickedItem[] | null>
Lua
local list = aiLib.itemPicker(request) -- table oder nil, innerhalb eines Threads aufrufen

Parameter

Prop

Type

ItemPickerRequest

Prop

Type

Rückgabewert

Promise<PickedItem[] | null>: die Liste, die der Admin genommen hat, jede Zeile { name: string; count: number } (count eine ganze Zahl über 0); null, wenn die Auswahl geschlossen wurde. Lua gibt die Liste oder nil zurück. Die Typen sind ItemPickerRequest und PickedItem.

Beispiel

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

export async function giveItems(target: number): Promise<void> {
  const list = await itemPicker({ title: "Give items", target });
  if (!list || list.length === 0) return;
  await callAction("myadmin:give", [target, list], { fallback: "GIVE_FAILED" });
}

Dein Server-Handler gibt die Items: Prüfe, dass der Aufrufer ein Admin ist (Guard isAdmin), und prüfe jeden Namen und jede Anzahl erneut. Die Auswahl gibt nie selbst etwas.

Prompts von Hand

Punkte zeigen und verbergen Prompts für dich. Nutze diese nur für einen Prompt, der nicht an einen Ort gebunden ist.

showPrompt

Zeigt einen der Prompts deiner Ressource an (oder ändert seinen Text): das „Drücke E“-Feld rechts auf dem Bildschirm. Mit at schwebt der Prompt stattdessen über einem Weltpunkt oder einer Stelle an einer Entity.

Seite: Client · Wartet: nein

Signatur
showPrompt(id: string, text: string, at?: PromptAnchor): void
Lua
aiLib.showPrompt(id, text) -- in Lua kein Weltanker

Parameter

Prop

Type

Rückgabewert

void.

Beispiel

Siehe onPromptPressed.

hidePrompt

Verbirgt einen der Prompts deiner Ressource. Eine ID, die nicht angezeigt wird, tut nichts.

Seite: Client · Wartet: nein

Signatur
hidePrompt(id: string): void
Lua
aiLib.hidePrompt(id)

Parameter

Prop

Type

Rückgabewert

void.

onPromptPressed

Führt einen Handler aus, wenn der Spieler die Interaktionstaste drückt (Standard E), während einer der Prompts deiner Ressource der oberste ist.

Seite: Client · Wartet: nein

Signatur
onPromptPressed(handler: (id: string) => void): void
Lua
aiLib.onPromptPressed(function(id) end)

Parameter

Prop

Type

Rückgabewert

void.

Beispiel

import { createLocale, hidePrompt, onCache, onPromptPressed, showPrompt } from "@aicore-fivem/lib/client";

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

// „Drücke E zum Hinauswerfen“, während man auf dem Beifahrersitz sitzt.
onCache("seat", (seat) => {
  if (seat === 0) showPrompt("eject", Locale("EJECT"));
  else hidePrompt("eject");
});

onPromptPressed((id) => {
  if (id !== "eject") return;
  TaskLeaveVehicle(PlayerPedId(), GetVehiclePedIsIn(PlayerPedId(), false), 4160);
});

Minispiele

skillCheck

Der Skill-Check von ai-lib: Eine Nadel läuft pro Stufe einmal um einen Ring, und der Spieler drückt die angezeigte Taste, während sie im grünen Bogen ist. Er hat die übliche FiveM-Skill-Check-Form ("easy" | "medium" | "hard" oder { areaSize, speedMultiplier }), daher funktioniert Code, der für den Skill-Check von ox_lib geschrieben wurde, genauso.

Seite: Client · Wartet: ja (Promise in TypeScript, hält in Lua den Thread an)

Signatur
skillCheck(difficulty: SkillDifficulty | SkillDifficulty[], keys?: string[]): Promise<boolean>
Lua
local passed = aiLib.skillCheck(difficulty, keys) -- boolean, innerhalb eines Threads aufrufen

Parameter

Prop

Type

SkillDifficulty

SkillDifficulty ist eines von:

Prop

Type

Rückgabewert

Promise<boolean>: true, wenn jede Stufe getroffen wurde; false bei einem Fehlschlag, einer falschen Taste oder Escape.

Beispiel

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

export async function hotwire(netId: number): Promise<void> {
  const passed = await skillCheck(["easy", "easy", "medium", { areaSize: 30, speedMultiplier: 1.5 }], ["w", "a", "s", "d"]);
  await callAction("mykeys:hotwire", [netId, passed], { fallback: "HOTWIRE_FAILED" });
}

Das Ergebnis ist das Wort des Clients. Wenn das Minispiel etwas einbringt (ein Auto, ein Raub), spiele es vom Server mit minigameFor(source, { game: "skillCheck", ... }), das außerdem einen Sieg ablehnt, der schneller ist als ein echtes Spiel (Client-Eingaben).

lockpick

Das Schlossknacken von ai-lib: Der Spieler bewegt den Dietrich mit A / D (oder den Pfeiltasten) und hält W, um das Schloss zu drehen. Das Schloss dreht sich umso weiter, je näher der Dietrich am versteckten Sweet Spot ist; Gewalt nutzt den Dietrich ab.

Seite: Client · Wartet: ja (Promise in TypeScript, hält in Lua den Thread an)

Signatur
lockpick(options?: LockpickOptions): Promise<LockpickResult>
Lua
local result = aiLib.lockpick(options) -- 'opened' | 'broken' | 'cancelled', innerhalb eines Threads aufrufen

Parameter

Prop

Type

LockpickOptions

Prop

Type

Rückgabewert

Promise<LockpickResult> (LockpickResult): "opened", wenn jeder Stift gedreht wurde; "broken", wenn der letzte Dietrich zerbrochen ist (nimm das Dietrich-Item weg); "cancelled", wenn der Spieler Escape gedrückt hat.

Beispiel

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

export async function pickDoor(doorId: string): Promise<void> {
  const result = await lockpick({ difficulty: "hard", pins: 3, picks: 2 });
  if (result === "cancelled") return;
  await callAction("mydoors:picked", [doorId, result], { fallback: "PICK_FAILED" });
}

Auf dieser Seite