Système de véhicules¶
Le système de véhicules transporte des unités terrestres entières par voie aérienne : un transport récupère un véhicule pilotable dans une zone logistique, le convoie, et le repose sur le terrain — sans jamais le décomposer en crates. C'est le pendant du pipeline de crates (voir crates), et il sous-tend également le spawn des JTAC terrestres (voir Cycle de vie Troop + JTAC).
Source : src/CTLD_vehicle.lua
Entité : CTLDVehicle
Singleton : CTLDVehicleSpawner
CTLDVehicleSpawner suit l'idiome de manager standard (X = class() + getInstance() — voir
Architecture) : une unique _instance, allouée paresseusement au premier
appel de CTLDVehicleSpawner.getInstance(). CTLDVehicle est une simple entité créée avec
:new(data).
CTLDVehicle = class()
CTLDVehicleSpawner = class()
CTLDVehicleSpawner._instance = nil
function CTLDVehicleSpawner.getInstance()
if not CTLDVehicleSpawner._instance then
local o = setmetatable({}, CTLDVehicleSpawner)
o:init()
CTLDVehicleSpawner._instance = o
end
return CTLDVehicleSpawner._instance
end
À l'init(), le singleton enregistre sa section du menu F10 (order = 30), s'abonne au pont DCS
S_EVENT_DEAD, s'abonne à plusieurs événements du bus, et démarre trois timers périodiques
(détection de chargement natif, rafraîchissement de l'atterrissage pour packing, indice de vol
stationnaire).
Machine à états¶
CTLDVehicle.STATE déclare trois états :
| État | Signification |
|---|---|
WAITING |
Au sol, suivi, prêt à être chargé |
LOADED |
Transporté par un transport (unité DCS détruite pour menu_ctld, ou physiquement liée pour dcs_native) |
DELIVERED |
Déclaré mais actuellement non assigné — réservé à un futur état de livraison dédié |
Les transitions réellement effectuées par le code actuel sont :
spawn / register loadVehicle
(none) ───────────────► WAITING ──────────────► LOADED
▲ │
│ unloadVehicle │
└────────────────────────┘
│ parachuteVehicle
└─── (drop, then respawn) ─► WAITING
unloadVehicle comme parachuteVehicle ramènent le véhicule à WAITING (et non DELIVERED)
afin qu'il puisse être rechargé une fois de retour au sol. setState(newState) n'effectue aucune
validation — la correction incombe aux appelants.
Méthodes de chargement et déchargement¶
Le chargement et le déchargement portent chacun une chaîne method :
| Sens | Méthode | Déclencheur |
|---|---|---|
| Load | menu_ctld |
Le joueur choisit un véhicule dans le sous-menu F10 Load / Extract Vehicles ; l'unité DCS est détruite au chargement |
| Load | dcs_native |
Un véhicule entier entre dans la bounding box d'un transport à cargo natif (classe C-130 / Il-76) ; DCS garde l'unité physiquement liée |
| Unload | menu_ctld |
Le joueur choisit un véhicule dans Unload Vehicles ; l'unité est respawnée près du transport |
| Unload | dcs_native |
DCS a déjà posé l'unité au sol ; CTLD ne récupère que la référence vivante |
| Unload | parachute |
Largage en vol via Parachute Vehicle |
D'où vient un véhicule WAITING¶
Un CTLDVehicle chargeable en état WAITING est enregistré par l'un de ces chemins :
| Source | Mécanisme |
|---|---|
| Véhicule pré-placé par le MM | INIT-D scanMMVehicles() scanne les groupes Group.Category.GROUND des deux coalitions et appelle _registerMMVehicleUnit(unit) pour chaque unité vivante dont le type possède un descripteur de crate CTLD (findDescriptorByUnitType) |
| Activation retardée | S_EVENT_BIRTH → onBirth diffère d'une frame → _onBirthDeferred → _registerMMVehicleUnit (ou rattache une référence d'unité vivante à un véhicule existant après un déchargement) |
| Spawn programmatique | spawnVehicleForTransport(vehicleType, spawner, logisticZone) spawne une unité neuve près d'un transport et l'enregistre WAITING |
| Unpack de crate | spawnVehicleAt(spawnData, position) / registerJTACVehicle(...) enregistrent une unité produite par le flux d'unpack de crate/JTAC |
_registerMMVehicleUnit est idempotent : il ignore une unité déjà présente dans la table de
recherche inverse _unitToVehicle.
spawnVehicleForTransport¶
Spawne une unité pilotable dans le secteur avant du transport via ctld.utils.dynAdd, crée le
CTLDVehicle en WAITING, et publie OnVehicleSpawnedForTransport. Les noms de groupe et d'unité
sont attribués sous la forme CTLD_VEH_<type>_<id> (les espaces et slashes étant remplacés par
_), et conservés dans spawnData afin que l'unité réapparaisse sous le même nom après tout
déchargement ultérieur. Le chemin JTAC terrestre (spawnJTACFromDescriptor) appelle cette méthode,
puis CTLDJTACManager:startLase(...).
La branche Feature-Q « Request Equipment spawns a whole vehicle » existe dans
CTLDCrateManager:refreshRequestEquipmentSectionmais est actuellement inerte : le constructeur de menu fixespawnAsVehicle = falsepour chaque entrée, de sorte que Request Equipment (order 25) ne spawne jamais que des crates. Le chargement de véhicules entiers est piloté par le menu Vehicle Commands dédié, à l'encontre de véhicules déjàWAITING.
Pipeline de chargement — loadVehicle(vehicle, transport, player, method)¶
- Rejeter sauf si le véhicule est
WAITING. - Garde-fou de capacité
menu_ctld: lirecaps.maxWholeVehiclesOnboard(défaut1) depuis l'entréecapabilitiesByTypedu transport, compterfindLoadedVehicles(transport), et refuser avec un message au joueur si la soute est pleine. (La capacitédcs_nativeest laissée à DCS.) - Si le véhicule est un JTAC enregistré, suspendre le lasing (
setJTACInTransit). menu_ctld: détruire l'unité DCS, publierOnGroundUnitRemoved(reason = "loaded"), et vider la recherche inverse.dcs_native: garder l'unité vivante mais la retirer de la recherche inverse afin qu'unS_EVENT_DEADobsolète ne se déclenche pas à tort.- Enregistrer
loadMethod,loadTransportName,loadTime; passer à l'étatLOADED. menu_ctlduniquement : recalculer le poids de cargo interne du transport (_updateVehicleCargo) et envoyer un message de confirmation.- Publier
OnVehicleLoaded.
La distance de ramassage maximale n'est pas appliquée dans loadVehicle ; elle l'est en amont
dans findLoadableVehicles (≤ maximumDistancePackableUnitsSearch, défaut 200 m).
Pipeline de déchargement — unloadVehicle(vehicle, transport, player, method, rearSector)¶
- Rejeter sauf si le véhicule est
LOADED. - Calculer une position de spawn avec
_computeSpawnPosition(transport, rearSector). menu_ctld/parachute: respawner l'unité près du transport avecctld.utils.dynAdd, en réutilisantspawnData.groupName/spawnData.unitName.dcs_native: récupérer l'unité vivante que DCS a déjà posée viaGroup.getByName(spawnData.groupName).- Repasser à l'état
WAITING; ré-enregistrer la recherche inverse. - Non-
dcs_native: recalculer le poids de cargo. Reprendre le lasing JTAC le cas échéant. menu_ctlduniquement : envoyer un message de confirmation.- Publier
OnVehicleUnloaded.
Le brouillon du guide développeur décrivait le déchargement comme un respawn via
CTLDObjectRegistry.spawnObject; le code actuel respawne viactld.utils.dynAddet n'utilise pas l'object registry ici.
Positionnement au spawn¶
_computeSpawnPosition(transport, rearSector) (wrapper public computeSafeDropPos) place l'unité
dans un secteur de ±45° par rapport au cap du transport — le secteur avant par défaut, ou le
secteur arrière (hdg + π) quand rearSector vaut vrai, ce qui dégage le drop de la
trajectoire de décollage d'un hélicoptère IA. Le décalage radial provient de _secureOffset, la
diagonale de la bounding box sqrt(halfLen² + halfWid²) × 2 + 10 m, avec un repli à 60 m lorsque
getDesc().box est indisponible.
Chargement de véhicule entier natif DCS — _checkNativeLoading (tick 1 s)¶
Pour les transports à cargo natif, le chargement est détecté géométriquement plutôt que via un menu. À chaque tick :
- Collecter les véhicules
WAITINGavec unités vivantes et les véhiculesLOADEDdontloadMethod == "dcs_native". - Itérer sur les groupes
Group.Category.AIRPLANEdes deux coalitions ; pour chaque unité qui est_isNativeCargoCapable(son entréecapabilitiesByTypefixecanTransportWholeVehicle == true), liregetTransformation()etgetDesc().box. - Pour chaque véhicule
WAITING, convertir sa position monde dans le repère local du transport (_worldToLocal) et la tester contre la box (_isInBbox). À l'entrée, appelerloadVehicle(veh, transport, nil, "dcs_native")et enregistrer le transport dans_nativeTrackedpour éviter un double déclenchement.
La branche de sortie de bbox est présente mais actuellement inopérante (la position de l'unité
chargée ne peut plus être interrogée une fois détruite) ; le déchargement natif est géré via le
chemin de déchargement dcs_native plutôt que par cette boucle.
Filtrage des chargeables — findLoadableVehicles / _isTypeLoadable¶
findLoadableVehicles(transport) retourne les véhicules WAITING qu'un transport donné peut
charger, en appliquant trois filtres par-dessus la porte de capacité :
- L'entrée
capabilitiesByTypedu transport doit fixercanTransportWholeVehicle, sinon{}. - Coalition (GAP-Q1) :
veh.spawnData.coalitionIddoit être égal à la coalition du transport. - Type (GAP-Q2) :
_isTypeLoadableexige queveh.vehicleTypefigure dans la listeloadableVehiclesRED(coalition 1) ouloadableVehiclesBLUE(coalition 2) du transport. - Distance : ≤
maximumDistancePackableUnitsSearch.
Les références d'unité des véhicules sont résolues paresseusement dans la boucle (Group.getByName
peut avoir une frame de retard sur coalition.addGroup).
Capacités de transport¶
Le comportement des véhicules entiers est piloté par l'entrée capabilitiesByType du transport
(ctld.gs("capabilitiesByType")), plus le flag par joueur canCarryVehicles :
| Champ | Effet |
|---|---|
canTransportWholeVehicle |
Active le chargement/déchargement de véhicule entier pour ce type |
maxWholeVehiclesOnboard |
Capacité simultanée en véhicules entiers (0 = aucune) |
loadableVehiclesRED / loadableVehiclesBLUE |
Noms de type DCS que cet appareil peut charger, par coalition |
canParachuteDrop |
Active l'entrée Parachute Vehicle |
useNativeDcsCargoSystem |
Marque le type comme porteur à cargo natif |
Packing d'un véhicule en crates — packVehicle¶
findPackableVehicles(transport) liste les véhicules WAITING gérés par CTLD situés à moins de
maximumDistancePackableUnitsSearch (uniquement les véhicules suivis — jamais des props de scène
arbitraires). packVehicle(transportUnitName, packableUnitName, playerObj) :
- Résoudre le descripteur pour le type d'unité (
findDescriptorByUnitType) ; refuser s'il n'y en a aucun. - Désenregistrer silencieusement tout JTAC sur l'unité, puis la
destroy()et publierOnGroundUnitRemoved(reason = "packed"). - Spawner
descriptor.cratesRequiredcrates viaCTLDCrateManager:spawnCratesAligned(...)avecCTLDCrate.SPAWN_METHOD.VEHICLE_PACK, dans le secteur avant (hélicoptère) ou le secteur arrière (appareil à cargo natif). - Publier
OnVehiclePacked, puis reconstruire le menu du joueur une frame plus tard (pour éviter la frame de retard decoalition.getGroups()aprèsdestroy()).
Le rafraîchissement du menu de pack déclenché à l'atterrissage (_checkPackingLanding, tick 3 s)
est conditionné à ctld.gs("enablePackingVehicles").
Largage en parachute — parachuteVehicle¶
Requiert caps.canParachuteDrop. Vérifie l'AGL par rapport à parachuteMinAltitudeVehicles
(défaut 30 m) et refuse si trop bas. Résout le véhicule (id explicite, sinon le premier véhicule
chargé sur ce transport), calcule le point d'atterrissage avec ctld.utils.calcDropPosition en
utilisant parachuteDescentRateVehicles (défaut 8 m/s), repasse le véhicule à WAITING, publie
OnVehicleParachuting, et après le délai de descente le respawne à la position d'atterrissage et
publie OnVehicleParachuteLanded (en reprenant le lasing JTAC le cas échéant).
Poids de cargo¶
getLoadedVehicleWeight(transportUnitName) somme ctld.gs("groundVehicleWeights") pour les
véhicules LOADED en menu_ctld uniquement (défaut 2500 kg par type inconnu) ; le poids
dcs_native est laissé à DCS. _updateVehicleCargo délègue à ctld.utils.updateTransportWeight,
qui agrège troops, crates et véhicules en un unique appel setUnitInternalCargo.
Menu F10 — buildMenuSection¶
Ajouté uniquement lorsque l'unité du joueur possède canCarryVehicles. Il crée Vehicle Commands
(order = 30) avec trois branches dynamiques :
- Load / Extract Vehicles — reconstruit par
refreshLoadSection; désactivé en vol ; liste le résultat defindLoadableVehicles. Sélectionner une entrée appelleloadVehicle(..., "menu_ctld"). - Unload Vehicles — reconstruit par
refreshUnloadSection; masqué quand rien n'est chargé, affiche « Land to unload vehicles » en vol, sinon liste les véhicules chargés. - Parachute Vehicle — présent uniquement quand
caps.canParachuteDrop; activé uniquement en vol avec un véhicule chargé.
Les rafraîchissements sont pilotés par le détecteur d'atterrissage (_checkPackingLanding), les
événements de bus de spawn/retrait d'unité terrestre (OnGroundUnitSpawned / OnGroundUnitRemoved),
et les événements de load/unload eux-mêmes. Un timer d'indice de vol stationnaire
(_checkVehicleHoverHint, tick 5 s, cooldown de 30 s par joueur) incite un porteur en vol
stationnaire à se poser au-dessus d'un véhicule WAITING voisin.
Gestion des événements DCS¶
onDead(S_EVENT_DEAD) — si l'unité morte est un véhicule suivi, la retirer et publierOnVehicleDead+OnGroundUnitRemoved. Sinon, s'il s'agit d'un transport qui portait des véhiculesLOADED, chaque véhicule transporté est considéré perdu : son JTAC est désenregistré etOnVehicleDeadest publié pour lui.onBirth(S_EVENT_BIRTH) — voir la source d'activation retardée ci-dessus.
Événements publiés¶
| Événement | Quand |
|---|---|
OnVehicleSpawnedForTransport |
spawnVehicleForTransport a créé un véhicule WAITING |
OnVehicleLoaded |
Véhicule chargé dans un transport |
OnVehicleUnloaded |
Véhicule déchargé / largué d'un transport |
OnVehicleDead |
Véhicule suivi détruit, ou perdu avec son transport |
OnVehiclePacked |
Véhicule packé en crates |
OnVehicleParachuting |
Largage en parachute démarré |
OnVehicleParachuteLanded |
Véhicule parachuté arrivé au sol |
OnGroundUnitSpawned / OnGroundUnitRemoved |
Une unité terrestre suivie est apparue / a disparu (pilote le rafraîchissement du menu) |
Voir Events pour le catalogue complet du bus et la forme des payloads.