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.
| Style | Ce 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
new Points()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
points.add(id: string, options: PointOptions): voidpoints: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
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
points.addModel(id: string, options: ModelPointOptions): voidpoints: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
points.addVehicle(id: string, options: VehiclePointOptions): voidpoints: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
points.remove(id: string): voidpoints: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
points.clear(): voidpoints: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
points.refresh(id?: string): voidpoints:refresh() -- tous les points du groupeParamè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
points.isInside(id: string): booleanPas 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
points.restyle(): voidPas 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
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
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)
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));Le joueur local
Le ped, le véhicule, le siège et l’arme en cache, l’objet player (position, téléportation, animations), les fondus d’écran, les peds des autres joueurs et le joueur le plus proche.
Zones
Zones : des zones avec des callbacks d’entrée et de sortie (sphère, cylindre, polygone), vérifiées à leur propre intervalle, jamais à chaque frame.