AI Core
Référence de l’APILib : client

Points d’interaction

Points : chaque endroit qu’un joueur utilise (comptoirs, boutiques, portes, PNJ, distributeurs, coffres de véhicules) sous forme d’invite ou d’option de ciblage, avec peds, blips et points construits à partir des enregistrements du panneau.

Un point est tout endroit qu’un joueur utilise : un comptoir de boutique, une porte, un PNJ, un lieu de restitution, chaque distributeur, le coffre de chaque voiture. Vous le décrivez une fois (où, quel texte, ce qui se passe) et la lib le dessine de la manière choisie par le propriétaire du serveur.

Invite ou ciblage

Le propriétaire choisit le style d’interaction dans le panneau d’administration d’ai-core (Interaction). Le même onPressed s’exécute dans les deux cas, votre script n’a donc jamais à se soucier du style actif.

StyleCe que voit le joueur
Prompt (invite)Une boîte « appuyez sur E » sur le côté droit de l’écran tant que le joueur se trouve dans le rayon du point. La touche est un unique raccourci clavier réassignable dans ai-lib (E par défaut), si bien que rien ne scrute les touches à chaque frame.
Target (ciblage)Une option dans l’œil de ciblage sur l’emplacement, le PNJ, l’objet ou le véhicule. Il faut qu’un des scripts de ciblage pour lesquels la lib a un adaptateur tourne. Quand plusieurs tournent, le premier dans cet ordre l’emporte : ai-target, tgiann-radialmenu-target, tgiann-target, sleepless_interact, ox_target, qb-target, interact.

Quand le propriétaire a choisi le ciblage mais qu’aucun script de ciblage ne tourne, chaque point est une invite. Un point peut passer outre le choix du propriétaire : promptOnly garde une invite (actions faites depuis un véhicule : pompes à essence, stations de lavage), preferTarget utilise une option de ciblage dès qu’un script de ciblage tourne (sièges). Quand un script de ciblage démarre ou s’arrête, ou que le propriétaire change de style, chaque point bascule de lui-même.

Une seule action de point de votre ressource s’exécute à la fois. Quand onPressed renvoie une promesse, les autres points de votre ressource ignorent les appuis jusqu’à ce qu’elle soit réglée, vous n’avez donc jamais besoin de votre propre drapeau busy. Le joueur doit aussi passer canShow au moment de l’appui.

new Points

Crée un groupe de points. Créez un groupe par fonctionnalité en haut du fichier et ajoutez-y des points.

Côté : client · Attend : non

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

Retour

Un groupe Points avec les méthodes ci-dessous.

Exemple

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

const points = new Points();

points.add

Ajoute un point à un endroit, ou remplace le point portant le même id. Un point peut porter son propre PNJ (ped) et son blip sur la carte (blip).

Côté : client · Attend : non

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

Paramètres

Prop

Type

PointOptions

Prop

Type

PointPed

Le champ ped accepte les options de LocalPed sauf onSpawn et onDespawn (le point les utilise) :

Prop

Type

PointBlip

Le champ blip accepte les options de blip ; coords vaut par défaut le point :

Prop

Type

PromptAnchor

Ce que renvoie une fonction world (PromptAnchor), l’un de :

Prop

Type

Retour

void.

Exemple

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();

// Un comptoir de boutique avec un commerçant et un blip sur la carte.
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" }),
});

// Un coffre que seule la police peut ouvrir ; le texte suit un état.
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 lit le métier : revérifiez-le quand les métiers, le service ou les gangs changent.
onMembershipChange(() => points.refresh());

canShow décide seulement de ce que voit le joueur. Le serveur doit revérifier (métier, distance, stock) dans son propre gestionnaire. Un tricheur peut appeler votre événement serveur sans aucun point.

points.addModel

Ajoute un point sur chaque objet du monde de certains modèles (distributeurs, distributeurs automatiques, bennes, chaises), ou remplace le point portant le même id. En mode ciblage, c’est une option de ciblage sur ces modèles ; en mode invite, une invite s’affiche près du plus proche. La recherche de l’objet le plus proche s’exécute une seule fois dans ai-lib pour toutes les ressources, deux fois par seconde, et se met en pause quand le joueur reste immobile.

Côté : client · Attend : non

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

Paramètres

Prop

Type

ModelPointOptions

Prop

Type

Retour

void.

Exemple

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" }),
});

Deux états d’un même objet (amarrer et détacher un bateau) sont deux points de modèle avec des canShow opposés, pas une seule fonction de libellé. Un point de plus de 6 modèles trouve ses objets via un index du pool d’objets, si bien que des centaines de modèles coûtent autant que quelques-uns.

points.addVehicle

Ajoute un point sur chaque véhicule en réseau (coffre, capot, pousser, réparer), ou remplace le point portant le même id. En mode ciblage, c’est l’option globale de véhicule du script de ciblage ; en mode invite, une invite s’affiche au véhicule en réseau le plus proche dans le rayon radius.

Côté : client · Attend : non

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

Paramètres

Prop

Type

Retour

void.

Exemple

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

Retire un point avec son invite, son option de ciblage, son PNJ et son blip. Fonctionne pour les points créés avec add, addModel et addVehicle. Un id inconnu ne fait rien.

Côté : client · Attend : non

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

Paramètres

Prop

Type

Retour

void.

Exemple

points.remove("shop");

points.clear

Retire tous les points du groupe.

Côté : client · Attend : non

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

Retour

void.

Exemple

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

Ne reconstruisez jamais les points du panneau avec clear() puis en ajoutant tout à chaque modification du panneau : avec des milliers d’enregistrements, une seule modification les reconstruirait tous sur chaque client. Utilisez RecordObjects : une modification ne reconstruit que son propre enregistrement.

points.refresh

Relit canShow et les libellés sous forme de fonction. Appelez-la quand une chose qu’ils lisent a changé : le métier, le service, un véhicule, un état envoyé par le serveur. Les invites se mettent à jour immédiatement ; les options de ciblage lisent canShow quand l’œil s’ouvre, et un libellé modifié est réappliqué.

Côté : client · Attend : non

Signature
points.refresh(id?: string): void
Lua
points:refresh() -- tous les points du groupe

Paramètres

Prop

Type

Retour

void.

Exemple

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());
// Une caisse a changé : ne rafraîchir que son point.
robbed.onChange((netId) => points.refresh(`register:${netId}`));

Dans un AddStateBagChangeHandler brut, la nouvelle valeur n’est pas encore lisible : appelez-y refresh dans un setTimeout(..., 0). Les classes d’état de la lib (EntityState.onChange) vous donnent déjà la nouvelle valeur.

points.isInside

Indique si le joueur se trouve en ce moment dans le rayon d’un point (points créés avec add).

Côté : client · Attend : non

Signature
points.isInside(id: string): boolean

Pas d’équivalent Lua.

Paramètres

Prop

Type

Retour

boolean : true tant que le joueur est à l’intérieur ; false à l’extérieur, ou pour un id inconnu ou un point de modèle.

Exemple

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

points.restyle

Applique le style d’interaction actuel à tous les points du groupe. La lib l’appelle elle-même quand le propriétaire change de style ou qu’un script de ciblage démarre ou s’arrête ; vous n’en avez jamais besoin.

Côté : client · Attend : non

Signature
points.restyle(): void

Pas d’équivalent Lua (Lua change aussi de style tout seul).

Retour

void.

Points à partir des enregistrements du panneau

La plupart des points viennent d’un fichier d’enregistrements du panneau d’administration de votre script (boutiques, garages, comptoirs de location). Reflétez le fichier sur le client avec AdminWorld, puis construisez les points de chaque enregistrement avec RecordObjects : un enregistrement modifié ne reconstruit que ses propres points, un enregistrement supprimé les retire.

new RecordObjects

Garde les objets du monde d’un fichier d’enregistrements (points, blips, peds, zones) synchronisés avec le fichier, un enregistrement à la fois. build(key, record) crée les objets d’un enregistrement et renvoie la manière de les retirer. Il construit chaque enregistrement dans son constructeur.

Côté : client et serveur (depuis @aicore-fivem/lib/shared) · Attend : non

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

Pas d’équivalent Lua.

Paramètres

Prop

Type

Membres

Prop

Type

Exemple

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);
});

// Un réglage lu par toutes les boutiques a changé : les reconstruire toutes.
world.onChange("settings", () => shops.rebuildAll());

RecordObjects construit dans son constructeur. Créez-le après tout ce que build lit, et dans build utilisez l’argument record : un champ de classe qui contient le RecordObjects n’est pas encore assigné pendant cette première construction.

new AdminWorld

La copie client des fichiers de monde du panneau de votre script (les fichiers dont le schéma indique sync: "all", voir Panneau d’administration). Elle lit la copie que FiveM a mise en cache au démarrage, demande une seule fois au serveur les enregistrements modifiés depuis, puis suit les changements en direct. Elle n’interroge jamais en boucle.

Côté : client · Attend : non (la promesse ready indique quand les changements depuis le démarrage sont arrivés)

Signature
new AdminWorld(files: string[])

Pas d’équivalent Lua.

Paramètres

Prop

Type

Membres

Prop

Type

Exemple

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));

Sur cette page