Interactions AR¶
medusa-interactions centralise la détection, la mise en évidence, le popup AR, la touche d'action et
les notifications. Une seule cible — la plus proche et autorisée — peut être active à la fois.
La mise en évidence choisit son implémentation selon le type d'entité :
- véhicules et objets utilisent
SetEntityDrawOutlinepour une silhouette cyan exacte ; - les peds utilisent
MarkerTypeVerticalCylinder(DrawMarker, type1) sous leurs pieds, sans appeler le native d'outline incompatible.
L'outline est géré par transition lors de l'acquisition, du changement de handle, de la sortie de
portée et de l'arrêt de ressource. Le marker doit, selon le fonctionnement de FiveM, être dessiné
chaque frame ; cette boucle reste endormie à 250 ms lorsqu'aucun ped n'est ciblé et revalide son
handle d'entité après Wait(0) pour éviter les courses avec le scan. Tant que le même handle reste
ciblé, le scan met à jour l'objet existant au lieu de remplacer sa référence ; aucune frame de
marker n'est donc perdue au rythme du scan de proximité.
Les valeurs globales vivent dans medusa-interactions/config.json. La portée de prévisualisation vaut
20 mètres par défaut. highlight.enabled masque tous les highlights ; highlight = false sur une
inscription masque seulement celui de cette interaction. Chaque inscription peut remplacer
highlightDistance et highlightColor. highlight.ped configure l'offset vertical, la largeur,
la profondeur et la hauteur du cylindre.
Cibles prises en charge¶
| Export | Usage |
|---|---|
registerEntity(options) |
handle local : ped, joueur, véhicule ou prop |
registerNetworkEntity(options) |
entité résolue à partir de networkId |
registerPed(options) |
ped local créé et nettoyé par le module |
registerPoint(options) |
position sans entité ni contour |
unregister(id) |
retire l'interaction et supprime le ped géré |
setEnabled(id, bool) |
active ou suspend une interaction |
update(id, changes) |
modifie popup, portée, position ou état |
notify(message, status, title?) |
affiche un toast AR partagé |
registerEntity accepte aussi directement un ped joueur retourné par GetPlayerPed, un véhicule
ou un objet. La nature de l'entité n'est pas imposée.
Pour une zone matérialisée au sol, horizontalDistance = true mesure la portée sur les axes X/Y
et verticalDistance (3 mètres par défaut) conserve une barrière entre étages ou instances
verticales. Le garage utilise ce mode afin que le conducteur assis dans un véhicule haut reste
dans le même cercle que le point au sol ; les autres interactions gardent leur distance 3D.
Exemple¶
local id = exports['medusa-interactions']:registerEntity({
id = 'vehicle-inspection',
entity = vehicle,
distance = 2.2,
highlightDistance = 8.0,
control = 38,
controlLabel = 'E',
popup = {
eyebrow = 'MEDUSA // DIAGNOSTIC',
signal = 'VÉHICULE VERROUILLÉ',
overline = 'UNITÉ MOBILE',
title = 'INSPECTION DU VÉHICULE',
description = 'Consulter les informations mécaniques',
actionLabel = 'LANCER LE SCAN',
actionHint = 'ANALYSE LOCALE',
telemetry = {
{ label = 'PLAQUE', value = 'MEDUSA' },
{ label = 'ÉTAT', value = 'STABLE' },
},
},
payload = { inspectionMode = 'mechanical' },
clientEvent = 'my-resource:inspect',
})
AddEventHandler('my-resource:inspect', function(context)
inspectVehicle(context.entity, context.payload.inspectionMode)
end)
popup peut être une table ou, pour une inscription effectuée dans le même runtime, une fonction
recalculant les données affichées. Une action peut être un callback local action, un clientEvent
ou un serverEvent. En pratique, les ressources consommatrices utilisent les événements à cause
de la frontière de sérialisation des exports. context contient id, entity, distance et le
payload opaque fourni lors de l'inscription.
Verrou d'action asynchrone¶
lockOnAction = true demande au gestionnaire de verrouiller toutes les interactions avant
d'appeler l'action. Le verrou masque immédiatement le popup et le highlight, puis suspend scan,
sélection et entrées à 250 ms. Un callback Lua reçoit context.releaseInteraction() et doit
l'appeler une fois son interface fermée. context.lockToken identifie l'acquisition. Pour un flux
piloté par événement, exports['medusa-interactions']:releaseInteraction(id) libère le verrou actif
du même identifiant.
Les options d'inscription passent par un export FiveM et doivent donc être sérialisables. Une
fonction Lua fournie par une autre ressource dans action, popup ou canInteract n'arrive pas
comme fonction dans medusa-interactions. Pour un consommateur externe, utiliser clientEvent, un
payload composé de valeurs sérialisables, puis appeler l'export releaseInteraction(id, reason)
depuis le gestionnaire d'événement. Avant TriggerEvent, le gestionnaire retire lui-même le
callback local du contexte afin de ne pas tenter de le sérialiser.
La libération est idempotente et vérifie identifiant et jeton lorsque le callback fourni est
utilisé. Une exception synchrone dans action, une inscription supprimée ou désactivée libère
automatiquement le verrou. Sans lockOnAction, le comportement historique reste inchangé.
Diagnostic du cycle¶
Le drapeau debug de medusa-interactions/config.json active des traces F8 horodatées, sans sortie
par frame. Une ouverture normale doit produire dans l'ordre :
control_releasedpuisaction_allowed;lock_acquiredavec l'identifiant et le jeton ;client_event_dispatchedaveclocked=truepour un consommateur externe ;- plus tard,
lock_releasedavec la raison fournie par le consommateur.
lock_release_rejected signale un identifiant ou jeton obsolète. popup_target_changed après une
libération confirme le moment exact auquel le scanner a repris. Le drapeau est temporairement
activé pour le diagnostic courant et doit être remis à false une fois l'incident résolu.
Les champs de popup sont eyebrow, signal, overline, title, description, actionLabel,
actionHint, distanceLabel et telemetry. L'interface n'injecte aucun texte métier : la configuration
defaultPopup complète uniquement les champs omis par une inscription. Les entrées de
telemetry utilisent { label, value }.
Performance¶
- le scan spatial est adaptatif : 1 seconde au repos, 150 ms dans la portée de highlight ;
- les distances sont comparées au carré et
sqrtn'est calculé que dans la portée utile ; - aucun scan d'entités globales ou raycast permanent n'est effectué ;
- le polling de la touche chaque frame ne s'active que lorsqu'une interaction est actionnable ;
- le NUI ne reçoit un message que si cible, distance arrondie ou contenu du popup change ;
- un verrou d'action suspend entièrement les scans au lieu de laisser le popup se recréer derrière une interface active ;
- un seul effet de sélection est actif ; le rendu par frame est limité au marker du ped ciblé ;
- l'outline et les peds gérés sont nettoyés à l'arrêt.
La boucle d'entrée capture son candidat avant Wait(0), puis vérifie que la cible partagée est
toujours identique après la reprise. Cette règle évite qu'un scan concurrent mettant active à
nil interrompe l'action ou provoque une erreur Lua.
Le scan conserve aussi le même objet active tant que l'identifiant et le handle ne changent pas,
ce qui évite de perdre ponctuellement l'appui sur E entre deux rafraîchissements.
Les ressources consommatrices doivent désinscrire leurs interactions à leur arrêt. Pour de très grandes populations dynamiques, elles doivent inscrire seulement les entités pertinentes pour le client plutôt que toutes les entités du serveur.
Fournisseur de cible Alt¶
Le registre registerTargetProvider permet à une ressource de fournir une cible raycast sans
dupliquer la boucle d'interaction E. Le binding +medusatarget utilise Alt gauche par défaut et
reste remappable. Tant que la touche est tenue, une couche NUI plein écran strictement transparente
porte le vrai curseur et le clic local. GetNuiCursorPosition est normalisé avec la résolution
active, puis GetWorldCoordFromScreenCoord fournit l'origine et la normale exactes du pointeur.
Un seul StartShapeTestLosProbe asynchrone, avec le masque explicite 511, est conservé et son
résultat n'est lu qu'au statut final 2. Le candidat le plus prioritaire situé sous ce pointeur,
dans sa distance autorisée et en ligne de vue reçoit l'outline. Un fournisseur peut remplacer
l'entité raycastée par son entité métier, par exemple convertir un Ped occupant en véhicule.
Caméra, roue native du personnage, combat et clics monde sont neutralisés dans les groupes d'entrée
0 à 2 pendant ce mode seulement.
Les fonctions resolve et action peuvent traverser l'export depuis une autre ressource. Cfx les
désérialise alors sous forme de tables appelables portant __cfx_functionReference, et non sous le
type Lua brut function. Un adaptateur privé unique accepte soit une fonction locale, soit cette
référence Cfx avec son métamécanisme __call; il refuse les tables ordinaires et les valeurs
absentes. L'inscription, les deux résolutions (acquisition puis clic) et l'action utilisent ce même
adaptateur dans une fermeture protégée. Une exception rejoint ainsi les erreurs structurées
existantes au lieu d'être ignorée, sans élargir le contrat de données du fournisseur.
cancelTargetMode est la sortie unique utilisée sur relâchement, Échap, éloignement, disparition,
ouverture d'une NUI et arrêt de ressource. Elle retire focus, curseur, outline, candidat et état de
touche. Lors d'un clic accepté, la couche cible se désactive sans libérer le focus avant l'action :
le fournisseur acquitte une seule action et déclare explicitement s'il reprend le focus. La
fermeture est idempotente : un relâchement Alt postérieur à l'ouverture du panneau cible ne peut pas
voler le focus de cette nouvelle NUI.
Chaque refus conserve un motif structuré et un code de corrélation ALT-…. La NUI consomme la
réponse du callback et affiche l'échec en français, y compris en cas de panne CEF. Les diagnostics
F8 tracent uniquement les transitions du mode, du raycast, du candidat, de la sélection et du
transfert de focus ; ils ne journalisent jamais chaque frame. medusa-vehicles enregistre un
fournisseur à 3 m, contrôle le résultat de l'export et le réinscrit après
onClientResourceStart de la dépendance. La version 1.7.4 ajoute une trace bornée de
l'inscription/désinscription ; l'activation debug doit ensuite annoncer providers=1. Il reste
propriétaire de toutes les validations métier ;
le fournisseur générique ne décide jamais d'un verrou, d'une porte ou d'un inventaire.
Stations et équipement¶
medusa-energy enregistre uniquement les points configurés et conserve la validation métier côté
serveur. Le cycle E, le câble, la trappe, les distances et le cleanup sont détaillés dans
Énergie des véhicules.