Aller au contenu

veafCombatZone — Zones de combat activables

Module ID: COMBATZONE | Fichier: veafCombatZone.lua

Voir en jeu : étapes 04 « Entraînement de Khoni : trois niveaux », 07 « Mission chaînée : le port d'Ochamchire », 08 « Opération Tkvarcheli : tâches et dépendances » et 09 « Convoi en mouvement » de la mission de démo.


Objectif

Définit des zones de combat nommées dans l'éditeur de mission que les joueurs peuvent activer et désactiver via le menu radio F10. Chaque zone suit l'état des unités ennemies, déclenche des événements de completion d'objectif, supporte la notation et peut contenir plusieurs groupes d'unités avec des règles de spawn.


Dépendances

  • veafRadio — menu F10
  • veafSpawn — backend de spawn d'unités
  • veafMarkers — commandes de marqueur optionnelles

Activation

veafCombatZone.initialize()

Les zones individuelles sont créées et initialisées séparément (voir ci-dessous).


Configuration (mission.yaml)

modules:
  COMBATZONE:
    enabled: true          # défaut : true
    logLevel: info        # surcharge optionnelle du niveau de log
    combat_zone_settings: # surcharges globales optionnelles
      event_message_combatzonecomplete: "Objectif de zone atteint !"  # null = supprimer
      watchdog_check_interval: 30          # secondes entre les sondages du watchdog (défaut : 60)
      radio_menu_name: "Zones de combat"   # libellé du menu F10
      combat_zone_menu_name: "Opérations Zone de Combat"
      operation_menu_name: "Opérations"
    combat_zones:         # définitions de zones et d'opérations
      - type: zone                          # zone | operation
        zone_name: "CZ-Alpha"              # nom de la zone de trigger DCS
        friendly_name: "Zone Alpha"        # libellé dans le menu radio
        radio_group_name: "Nord"           # regroupe les zones de même nom sous un sous-menu commun
        radio_menu_prefix: "BLEU"          # préfixe affiché devant le libellé de la zone
        briefing: "Détruire la colonne blindée."  # affiché dans les infos mission
        training: false                     # true = pas de sécurité, statut verbeux
        active_at_start: true               # active automatiquement la zone au démarrage de la mission
        chained_zones:                      # zones à déclencher quand celle-ci se termine
          - "CZ-Bravo"
        chained_delay: 60                   # secondes avant le déclenchement des zones chaînées
        includes:                           # emprunte les éléments d'autres zones (niveaux de difficulté)
          - "CZ-Alpha-Facile"
      - type: operation
        zone_name: "Op-Tonnerre"
        friendly_name: "Opération Tonnerre"
        tasking_orders:
          - zone_name: "CZ-Alpha"           # première tâche (sans dépendances)
          - zone_name: "CZ-Bravo"
            dependencies:                   # CZ-Bravo se débloque après CZ-Alpha
              - "CZ-Alpha"

Champs de combat_zone_settings

Champ Type Défaut Description
event_message_combatzonecomplete string | null (défaut module) Message diffusé quand une zone se termine. null le supprime.
watchdog_check_interval entier 60 Secondes entre les sondages du watchdog
radio_menu_name string "COMBAT ZONES" Libellé du menu de premier niveau F10
combat_zone_menu_name string (défaut) Libellé du sous-menu des opérations de zone
operation_menu_name string (défaut) Libellé du sous-menu des opérations

Champs de combat_zones[] — type zone

Champ Type Défaut Requis Description
type string zone Non zone ou operation
zone_name string — Oui Nom de la zone de trigger DCS
friendly_name string — Non Libellé affiché dans le menu F10
radio_group_name string — Non Regroupe cette zone (et toutes celles portant le même nom) sous un sous-menu radio commun
radio_menu_prefix string — Non Préfixe affiché devant le libellé de la zone dans le menu
briefing string — Non Texte de briefing affiché aux joueurs
training booléen false Non Mode entraînement : pas de sécurité, statut verbeux
completable booléen true Non false : la zone ne se termine (et ne se désactive) jamais d'elle-même
show_units_list booléen true Non false : le rapport F10 n'énumère pas les unités restantes
show_zone_position_info booléen true Non false : le rapport F10 n'affiche ni les coordonnées de la zone ni sa météo
smoke_and_flare booléen true Non false : la zone ne propose ni fumigène ni fusée éclairante pour se signaler
radio_menu_disabled booléen false Non true : la zone n'apparaît pas du tout dans le menu F10
rename_units_sequentially booléen true Non false : les unités gardent leur nom d'origine à l'apparition, au lieu d'être renommées en séquence. Voir ci-dessous
enemy_coalition RED | BLUE RED Non Coalition hostile : ses unités sont celles qu'il faut détruire pour terminer la zone, et celles que le rapport F10 annonce comme « ennemis ». BLUE pour une zone jouée côté rouge (voir ci-dessous)
radio_menu_coalition RED | BLUE | ALL (camp qui joue la zone) Non Coalition à qui le menu F10 de la zone est proposé. Par défaut : le camp opposé à enemy_coalition. ALL le montre aux deux camps (voir ci-dessous)
active_at_start booléen false Non Active automatiquement la zone au démarrage de la mission (veafCombatZone.ActivateZone après initialize())
chained_zones string[] [] Non Noms des zones à déclencher à la completion
chained_delay entier 0 Non Secondes avant le déclenchement des zones chaînées
includes string[] [] Non Zones dont celle-ci emprunte les éléments : les activer avec elle, transitivement. Voir ci-dessous
scenery_targets entier[] [] Non Identifiants d'objets de la carte (ponts, bâtiments de la carte) qu'il faut aussi détruire pour terminer la zone. Voir ci-dessous

rename_units_sequentially — garder les noms d'unités d'origine

À l'apparition d'un groupe, une zone de combat renomme ses unités en séquence. C'est utile sur une carte terminée — les noms deviennent lisibles et cohérents — et gênant pendant la mise au point d'un .miz : le nom que vous avez donné à l'unité dans l'éditeur de mission a disparu, et vous ne pouvez plus la retrouver dans les journaux.

combat_zones:
  - zone_name: CZ-Alpha
    rename_units_sequentially: false   # les unités gardent le nom de l'éditeur

Le réglage est par zone, et non un interrupteur global de débogage : c'est ce que demandait la demande d'origine, et un interrupteur global serait une chose de plus à penser à remettre avant de livrer.

Le défaut reste true, donc aucune mission existante ne change.

Niveaux de difficulté sur les mêmes cibles — includes

Un champ de tir en niveaux progressifs : facile (cibles inertes), moyen (AAA légère), difficile (SHORAD réaliste). Activer un niveau doit aussi faire apparaître tout ce que contiennent les niveaux en dessous. Chaque niveau est une zone à part — sa propre trigger zone, son propre nom, ses propres groupes — et includes lui fait emprunter les éléments des autres :

combat_zones:
  - zone_name: "RANGE-EASY"          # cibles inertes (statiques)
    friendly_name: "Champ de tir — facile"
  - zone_name: "RANGE-MEDIUM"        # AAA légère
    friendly_name: "Champ de tir — moyen"
    includes: ["RANGE-EASY"]
  - zone_name: "RANGE-HARD"          # SHORAD
    friendly_name: "Champ de tir — difficile"
    includes: ["RANGE-MEDIUM"]       # donc aussi RANGE-EASY : l'inclusion est transitive

Ce que fait la construction :

  • L'inclusion est transitive : RANGE-HARD inclut RANGE-MEDIUM, qui inclut RANGE-EASY, donc activer RANGE-HARD fait apparaître les trois niveaux. L'ordre des zones dans la liste n'a pas d'importance.
  • Le niveau qui emprunte possède ce qu'il fait apparaître : le désactiver retire aussi les éléments empruntés, et sa réussite compte leurs unités. Un élément #command emprunté fonctionne aussi, et le groupe qu'il crée porte le nom du niveau actif.
  • Les erreurs arrêtent la construction : un nom qui n'est pas une zone de combat de la mission (ou qui est une operation), une zone qui s'inclut elle-même, ou un cycle (A inclut B qui inclut A). En jeu, chacune de ces erreurs ne serait qu'une ligne dans dcs.log et un niveau auquel il manquerait les niveaux inférieurs.

Un niveau à la fois. Deux niveaux actifs en même temps font chacun apparaître leur propre copie des éléments qu'ils partagent — deux fois les mêmes cibles, au même endroit. Pour changer de niveau, désactivez le précédent.

Pourquoi l'imbrication par préfixe de nom ne marche pas

La règle du préfixe laisse croire qu'on peut imbriquer des zones par leur nom : RANGE-HARD, RANGE-HARD-MEDIUM, RANGE-HARD-MEDIUM-EASY, et un groupe nommé d'après la plus profonde serait ramassé par les trois. Ce n'est pas le cas : au démarrage, une zone détruit les groupes qu'elle ramasse, et la zone suivante cherche des unités vivantes — elle ne trouve donc plus rien. Un groupe appartient en pratique à une seule zone ; c'est includes qui le partage.

En Lua, c'est VeafCombatZone:addZoneElementsFromZoneNamed(nomDeZone), appelé une fois toutes les zones créées ; includes génère exactement ces appels, clôture transitive comprise.

Champs de combat_zones[] — type operation

Champ Type Défaut Requis Description
type string — Oui Doit être operation
zone_name string — Oui Nom de la zone de trigger DCS
friendly_name string — Non Libellé dans le menu radio
briefing string — Non Texte de briefing
tasking_orders objet[] [] Non Liste de tâches ordonnées
tasking_orders[].zone_name string — Oui Nom de la zone de combat pour cette tâche
tasking_orders[].dependencies string[] [] Non Noms des zones devant se terminer en premier
active_at_start booléen false Non Active l'opération, donc toutes ses zones, au démarrage de la mission — pour une mission que personne ne lance depuis le menu F10

Quand sa dernière tâche est terminée, l'opération annonce à tous les joueurs « L'opération … est terminée ». L'activer active toutes ses zones d'un coup : dependencies ordonne les tâches, pas l'apparition des unités — un objectif qui ne doit apparaître qu'après un autre en est une chained_zones.

Des objets de la carte comme cibles — scenery_targets

Une zone se termine quand les unités et les statiques qu'elle a fait apparaître sont détruites. Un pont ou un bâtiment qui fait partie de la carte n'a été créé par personne : la zone ne le voit pas — sauf si on le lui donne par son identifiant :

combat_zones:
  - zone_name: "CZ-Pont"
    friendly_name: "Pont d'Al-Rastan"
    active_at_start: true
    scenery_targets: [156696667]   # l'identifiant DCS du pont

La zone ne se termine alors qu'une fois ces objets détruits et ses propres unités rouges éliminées (une zone qui ne contient que le pont se termine sur le pont seul). Les destructions sont enregistrées depuis le début de la mission : un objet détruit avant l'activation de la zone compte. C'est aussi pourquoi une telle zone ne se rejoue pas : réactivée, elle retrouve son pont détruit et se termine aussitôt. Le rapport F10 de la zone compte les objets de la carte qui restent à détruire, à part de ses unités ; une zone qui en inclut une autre attend aussi ses objets.

Les identifiants n'existent que dans DCS : veaf-tools dcs scenery-objects liste les objets autour d'un point avec leur identifiant, leur type et leur distance. Une valeur qui n'est pas un entier positif arrête la construction — ce serait une cible indestructible, et une zone qui ne se termine jamais.

Exemple minimal

modules:
  COMBATZONE:
    enabled: true
    combat_zones:
      - type: zone
        zone_name: "CZ-Alpha"
        friendly_name: "Alpha"

Zone jouée côté rouge

Par défaut, une zone de combat suppose que les joueurs sont bleus et que les unités à détruire sont rouges. Deux comportements en découlaient : la zone se terminait quand il ne restait plus d'unité rouge, et le rapport F10 annonçait les bleus comme « amis » et les rouges comme « ennemis ».

enemy_coalition: BLUE inverse les deux : la zone se termine quand ses unités bleues ont été détruites, et le rapport nomme les bleus « ennemis » et les rouges « amis ».

modules:
  COMBATZONE:
    enabled: true
    combat_zones:
      - type: zone
        zone_name: "CZ-Kobuleti"
        friendly_name: "Kobuleti"
        enemy_coalition: BLUE   # les joueurs sont rouges, les bleus sont les ennemis

Le décompte des unités reste identique — seul change le camp sur lequel porte la condition de fin. Une zone qui ne précise rien se comporte exactement comme avant.

Auparavant, une zone sans unité rouge nécessitait completable: false, ce qui n'en faisait pas une zone rouge : cela désactivait simplement la fin automatique, et le rapport continuait d'appeler les ennemis bleus « amis ». enemy_coalition remplace ce contournement.

Côté Lua, l'équivalent est VeafCombatZone:setEnemyCoalition(coalition.side.BLUE) ; le setter accepte aussi la chaîne "blue" / "red".

À qui le menu F10 est-il proposé ?

Le menu F10 d'une zone n'est pas seulement de la lecture : c'est par lui qu'on active la zone, qu'on demande son état, qu'on tire la fumée. Il est donc proposé au camp qui joue la zone, c'est-à-dire l'opposé de enemy_coalition :

enemy_coalition Menu F10 visible par
RED (défaut) les bleus
BLUE les rouges

Rien à écrire pour obtenir ce comportement. Pour y déroger, radio_menu_coalition :

      - type: zone
        zone_name: "CZ-Alpha"
        radio_menu_coalition: ALL   # les deux camps voient la zone et peuvent l'activer

ALL est utile pour un arbitre ou un Mission Master qui occupe un slot rouge et doit pouvoir déclencher une zone bleue. On peut aussi nommer un camp explicitement (RED / BLUE) quand il ne correspond pas à celui qui joue la zone.

Changement de comportement (6.11.8) : avant cette version, toutes les zones étaient proposées aux deux camps. Une mission dont tous les slots joueurs sont bleus ne verra aucune différence ; une mission avec des slots rouges qui doivent garder l'accès aux zones bleues doit ajouter radio_menu_coalition: ALL sur ces zones.

Le menu parent (COMBAT ZONES, et le sous-menu de radio_group_name) reste visible par tout le monde : un groupe radio peut contenir des zones des deux camps. Chaque camp voit donc l'entrée COMBAT ZONES sans les zones de l'autre.

Côté Lua : VeafCombatZone:setRadioMenuCoalition(coalition.side.RED) ou "all".


Fonctionnement

Placez toutes les unités qui doivent apparaître dans la zone directement dans l'éditeur de mission DCS, à l'intérieur de la trigger zone, et nommez leurs groupes en commençant par le nom de la zone — c'est la règle du préfixe, la seule qui décide de ce que la zone contient. Au démarrage de la mission, VEAF les retire toutes — la zone est vide. Quand un joueur active la zone via le menu F10, toutes les unités réapparaissent à des positions aléatoires dans le rayon de la zone. Quand toutes les unités ennemies sont détruites, la zone est marquée comme terminée (un callback optionnel se déclenche, les zones chaînées optionnelles s'activent).

Cette approche vous donne une conception entièrement visuelle dans l'éditeur tout en gardant la zone inactive au démarrage de la mission.

Mise en place dans l'éditeur de mission DCS

  1. Créez une trigger zone — définissez la zone de combat. Nommez-la, par exemple CZ-Alpha.
  2. Placez des groupes d'unités à l'intérieur de la zone, en préfixant leur nom du nom de la zone — CZ-Alpha-ARMOR, CZ-Alpha-AAA. Mettez-les dans n'importe quelle coalition — VEAF gère leur cycle de vie.
  3. Utilisez les tags de nom d'unité ou de groupe (voir ci-dessous) pour personnaliser le comportement d'apparition par groupe.
  4. Enregistrez la zone dans mission-script.lua :
VeafCombatZone:new()
  :setMissionEditorZoneName("CZ-Alpha")        -- nom de la trigger zone DCS
  :setFriendlyName("Alpha")                    -- libellé du menu radio
  :setBriefing("Strike Alpha — Colonne blindée")
  :initialize()

veafCombatZone.initialize() doit d'abord être appelé au niveau du module.


La règle du préfixe — ce que la zone ramasse

Un groupe appartient à la zone si, et seulement si, son nom commence par le nom de la trigger zone. La casse est ignorée, et rien d'autre n'est examiné. Se trouver dans le cercle est nécessaire, mais ne suffit pas.

Le nom qui compte est celui de la trigger zone DCS — zone_name en YAML, :setMissionEditorZoneName(...) en Lua. Ce n'est pas le friendly_name affiché dans le menu radio, qui n'est qu'un libellé.

Pour une zone nommée CZ-Alpha :

Nom du groupe dans l'éditeur Ramassé ? Pourquoi
CZ-Alpha-ARMOR oui commence par CZ-Alpha
CZ-Alpha-AAA oui commence par CZ-Alpha
cz-alpha-manpads oui la casse est ignorée
CZ-AlphaSAM oui c'est un préfixe, pas un segment : le tiret n'est pas exigé
ARMOR-1 non ne commence pas par CZ-Alpha, même posé au centre du cercle
Alpha-ARMOR non il manque CZ-

Un objet statique est son propre groupe : la règle s'applique alors à son nom.

Un groupe mal nommé est écarté — rien ne le montre en jeu

La zone s'active normalement et annonce sa réussite ; le groupe, lui, reste exactement où vous l'avez posé — jamais retiré au démarrage, jamais recréé à l'activation, jamais compté dans le rapport de zone. Une zone « qui ne fait rien apparaître » est presque toujours une zone dont les groupes ne portent pas son nom. Vérifiez le préfixe avant de chercher ailleurs.

Le log DCS les nomme. Une zone qui a trouvé des groupes dans son cercle et les a refusés écrit une ligne au démarrage, avec la liste des groupes et le préfixe qui leur manque :

COMBATZONE|I|Zone de combat [CZ-Alpha] : 2 groupe(s) présents dans la zone ont été ignorés :
ARMOR-1, SAM-NORTH. Pour faire partie de la zone, le nom d'un groupe doit commencer par le nom
de la zone [CZ-Alpha].

Une zone dont tous les groupes sont bien nommés n'écrit rien du tout.

Le corollaire vaut dans l'autre sens, et il est tout aussi silencieux : un groupe qui n'a rien à voir avec la zone, mais qui est posé dans le cercle et nommé CZ-Alpha-…, en fait partie — il sera donc détruit au démarrage de la mission avec les autres.

L'autre erreur silencieuse, un groupe nommé CZ-Alpha-… posé hors du cercle (aucune unité dedans), est signalée avant le jeu : veaf-tools validate et le build avertissent, en nommant le groupe et la zone. Un groupe qui porte le nom d'aucune zone n'est jamais signalé, où qu'il soit.


Groupes hors de combat

Un groupe n'est pas seulement vivant ou mort. Une batterie S-300 dont le radar de tir est détruit garde ses lanceurs, ses camions et son équipage — mais elle ne peut plus tirer.

Le rapport de zone le signale désormais :

HORS DE COMBAT (ne peuvent plus tirer) : CZ-Alpha-SA10

Un groupe entièrement détruit n'apparaît pas là : il est simplement absent des effectifs restants. Cette ligne ne concerne que les groupes encore debout et devenus inoffensifs.

Cela ne change pas la fin d'une zone

Pour l'instant, l'information est purement indicative : une zone se termine toujours quand toutes les unités ennemies sont détruites, lanceurs inutiles compris. Faire terminer la zone plus tôt est une décision de conception à part, pas encore prise.

Comment un groupe est jugé. Par défaut, un groupe est considéré hors de combat s'il est un site SAM (il lui reste un radar de veille ou un lanceur) et qu'il n'a plus aucun radar de tir. Un véhicule qui est à lui seul radar et lanceur — Tunguska, Tor, Osa — reste opérationnel tant qu'il vit. Un convoi, lui, n'a pas de radar : il reste une menace tant qu'il roule.

Pour les sites dont les attributs DCS ne suffisent pas à décrire la composition, une table de motifs (veaf.ImportantUnitsByGroupPattern, dans veaf.lua) déclare les ensembles d'unités indispensables et la vie minimale, en pourcentage, qu'il leur faut. Le S-300 y figure déjà.

Cibles statiques

Un objet statique de la zone compte comme une cible : la zone l'attend pour se terminer, et le panneau d'infos le compte parmi les ennemis restants, en « structure(s) » — en mode entraînement, avec son type. Mais un statique n'a pas de moteur et reste froid au pod de désignation ; les véhicules, eux, apparaissent moteur chaud (voir veafSpawn).

Tags de nom d'unité et de groupe

Les noms d'unités et de groupes dans l'éditeur de mission DCS peuvent porter des tags spéciaux qui contrôlent la façon dont VEAF les traite à l'activation de la zone. Les tags sont intégrés dans le nom et n'affectent pas DCS lui-même.

Tag Exemple Description
#spawnradius=N #spawnradius=200 Rayon de dispersion en mètres autour de la position enregistrée du groupe. Sans ce tag, voir #spawnradius
#spawnchance=N #spawnchance=50 Probabilité en pourcentage (0–100) que ce groupe apparaisse réellement. Voir #spawnchance
#spawncount=N #spawncount=2 Nombre d'éléments d'un même #spawngroup garantis à l'apparition. Voir #spawncount
#spawngroup="name" #spawngroup="SAM" Remplace le nom du groupe d'apparition (utile pour cibler un modèle nommé)
#spawndelay=N #spawndelay=120 Délai en secondes avant l'apparition de ce groupe après l'activation de la zone
#command="cmd" #command="-spawn sa-11" Exécute une commande VEAF au lieu de faire apparaître ce groupe ; l'unité sert de déclencheur et est détruite
#alarm=N #alarm=2 État d'alerte donné à ce groupe : 0 AUTO, 1 VERT, 2 ROUGE. Sans ce tag, l'état dépend de la nature du groupe — voir #alarm

#spawnradius — la dispersion par défaut

Sans tag, un groupe apparaît dispersé de 50 m autour de sa position enregistrée, et un objet statique apparaît exactement sur la sienne. La dispersion existe pour qu'un groupe ne réapparaisse pas deux fois au même mètre près ; un statique, lui, est souvent posé à un endroit précis (un parking, un quai) où le déplacer n'aurait pas de sens.

Ce que vous écrivez Ce que le groupe reçoit
rien 50 m pour un groupe, 0 m pour un statique
#spawnradius=200 200 m
#spawnradius=0 aucune dispersion — c'est ainsi qu'on la désactive

Une unité #command= n'est jamais dispersée, avec ou sans défaut : la commande s'exécute à sa position, donc la déplacer déplacerait ce qu'elle fait apparaître. Un #spawnradius= écrit explicitement s'y applique tout de même.

Ce comportement change les missions existantes

De mars 2023 à la 6.15.14 le défaut de 50 m était inatteignable : la constante existait, le code censé l'appliquer ne s'exécutait jamais, et tous les groupes d'une zone de combat apparaissaient exactement sur leur position enregistrée. Une mission construite pendant ces trois ans verra donc ses groupes bouger d'une cinquantaine de mètres. Si un placement était volontairement précis, écrivez #spawnradius=0.

Le premier point de passage suit le groupe

Un groupe dispersé n'apparaît plus sur son premier point de passage, et c'est de là que le groupe part. Le premier point est donc déplacé avec lui, de la même distance et dans la même direction.

Le reste du tracé ne bouge pas. Vous avez posé vos points de passage sur des routes, des ponts, des cols ; les décaler de cinquante mètres au hasard les mettrait à côté, et le tracé serait différent à chaque activation. Un groupe part donc de l'endroit où il apparaît et rejoint la route que vous avez dessinée.

Avant la 6.15.20, le premier point restait à la position d'éditeur : un convoi dispersé revenait d'abord le chercher, en parcourant une patte que personne n'avait dessinée. Visible surtout depuis la 6.15.15, qui a rendu la dispersion de 50 m effective.

Un groupe à cheval sur le bord de la zone

Un groupe est pris en charge par la zone dès qu'une seule de ses unités se trouve dans le cercle : il est alors détruit et recréé en entier, les unités restées dehors comprises. C'est voulu, et ça évite d'avoir à cadrer une zone au mètre près autour d'un convoi.

Ce qui posait problème, c'est que la zone s'ancrait sur la première unité qu'elle voyait, donc sur la deuxième du groupe quand la première était hors du cercle. Le groupe entier apparaissait alors décalé de l'écart entre ces deux unités — la longueur d'un camion pour un convoi — sans dispersion demandée et même avec #spawnradius=0.

Depuis la 6.15.21, l'ancrage est toujours la première unité du groupe, qu'elle soit dans le cercle ou non. Un groupe à cheval sur le bord apparaît donc à l'endroit où vous l'avez dessiné. Si vous aviez compensé ce décalage à la main en déplaçant vos unités, retirez la compensation.

Une valeur au hasard dans un intervalle

Les quatre tags qui portent un nombre — #spawnradius, #spawnchance, #spawncount, #spawndelay — acceptent un intervalle au lieu d'une valeur fixe, avec la même écriture que dans les commandes de marqueur :

CZ-Alpha-CONVOY #spawnradius=100-300 #spawndelay=30-90

La valeur est tirée une fois par mission, à la lecture des noms au démarrage. Toutes les activations de la zone utilisent donc la même valeur : c'est un placement varié d'une partie à l'autre, pas un placement qui bouge à chaque réactivation.

Avant la 6.15.23, l'intervalle était tronqué en silence

#spawnradius=100-300 était lu comme 100, sans message : vous obteniez la borne basse en croyant avoir un intervalle. Si vous en avez écrit, ils prennent effet maintenant — et le rayon peut donc être plus grand qu'avant.

#alarm n'accepte pas d'intervalle

L'état d'alerte est une énumération (0 AUTO, 1 VERT, 2 ROUGE) : #alarm=0-2 n'est pas un état au hasard, c'est une faute de frappe. Le tag la refuse comme il refuse déjà une valeur hors bornes.

Où les tags sont lus

Les tags d'un groupe sont ceux portés par son propre nom et par les noms de toutes ses unités. Taguer un seul camion d'un convoi suffit donc, quel que soit le camion — inutile de taguer les quatre, et inutile de deviner lequel DCS traitera en premier.

Les sources sont lues dans un ordre fixe :

  1. le nom du groupe ;
  2. les noms des unités, par ordre alphabétique.

La première valeur trouvée pour un tag l'emporte. Si une source suivante annonce une valeur différente pour le même tag, elle est ignorée et le log l'écrit — deux camions du même convoi portant #alarm=0 et #alarm=2 ne tirent pas à pile ou face, l'un gagne et vous êtes prévenu. Répéter la même valeur sur plusieurs unités ne produit aucun message : c'est la façon ordinaire de faire.

#command fait exception

#command reste attaché à l'objet qui le porte : chaque unité qui en porte un devient un déclencheur distinct, ce qui permet à un groupe de transporter plusieurs commandes. Posé sur le nom du groupe, il fait de ce groupe un unique déclencheur, pas un par unité.

Avant la 6.15.14

Seuls les tags portés par l'unité que le moteur rencontrait la première comptaient, et ceux posés sur un nom de groupe étaient ignorés en silence. L'ordre de rencontre n'étant pas garanti, un tag posé sur un camion donné fonctionnait ou non sans raison visible.

#alarm — faire tenir sa position à un groupe

L'état d'alerte terrestre décide de deux choses à la fois, et les deux comptent : un groupe en ROUGE s'arrête et se déploie — radars allumés, prêt à tirer — tandis qu'en AUTO il roule et laisse DCS élever son alerte à la détection. Juste pour une batterie SAM dans un cas, juste pour un convoi dans l'autre, et jamais le même.

La zone choisit donc selon la nature du groupe, sans que vous ayez à le dire :

Le groupe État reçu Pourquoi
a une route à parcourir (plus d'un point de passage) AUTO pour qu'il parte : en ROUGE il ne bougerait jamais
reste sur place ROUGE pour qu'il se batte : en AUTO une batterie SAM garde ses radars éteints

#alarm=N reste maître et l'emporte dans les deux sens — pour clouer un convoi sur place (#alarm=2) comme pour laisser une défense discrète jusqu'au premier contact (#alarm=0) :

CZ-Alpha-SA6-BATTERY              ← ROUGE, sans rien écrire
CZ-Alpha-SUPPLY-CONVOY            ← AUTO, sans rien écrire
CZ-Alpha-SA6-AMBUSH #alarm=0      ← discrète volontairement

Une valeur illisible ou hors bornes (#alarm=7, #alarm=x) retombe sur ROUGE et l'écrit dans le log, plutôt que de faire échouer la zone.

Uniquement pour les groupes de mission

Le tag s'applique aux groupes que la zone fait apparaître elle-même. Sur une unité #command=, passez l'état d'alerte dans la commande (-spawn ..., alarm 2), l'apparition étant gérée par l'interpréteur de marqueurs VEAF.

Historique du comportement

Jusqu'à la 6.15.4 les zones faisaient apparaître tous leurs groupes en ROUGE, ce qui explique qu'un convoi placé dans une zone ne bougeait jamais (#290). La correction est passée par un défaut unique en AUTO, ce qui a réglé les convois et rendu muettes les défenses anti-aériennes des zones — une batterie en AUTO n'allume pas ses radars. D'où le choix par nature décrit ci-dessus. Si vous avez ajouté des #alarm=2 sur vos batteries entre-temps, ils restent valides et inutiles : le défaut fait maintenant la même chose.

#spawnchance et #spawncount — le hasard, ou le nombre

Ces deux tags répondent à deux questions différentes, et il faut choisir laquelle vous posez.

#spawnchance=N est une probabilité, tirée une fois par groupe à l'activation de la zone. #spawnchance=50 fait apparaître le groupe une fois sur deux ; #spawnchance=0 ne le fait jamais apparaître ; sans tag, il apparaît toujours (le défaut est 100).

#spawncount=N, associé à #spawngroup, est une garantie de nombre : « exactement N de ces groupes-là, à chaque fois ». La zone tire au sort qui, puis retire tant qu'il faut pour atteindre N — donc #spawnchance n'y décide plus que lesquels, plus combien.

CZ-Alpha-SA6-A #spawngroup="CZ-Alpha-SAM" #spawncount=2
CZ-Alpha-SA6-B #spawngroup="CZ-Alpha-SAM" #spawncount=2
CZ-Alpha-SA6-C #spawngroup="CZ-Alpha-SAM" #spawncount=2
CZ-Alpha-SA6-D #spawngroup="CZ-Alpha-SAM" #spawncount=2

Deux des quatre batteries, toujours deux, tirées au hasard à chaque activation.

Sans #spawngroup, chaque groupe est seul dans le sien : sa probabilité est tirée pour lui et pour rien d'autre. Un #spawngroup sans #spawncount continue de signifier « un seul de ceux-là », mais chaque candidat tire désormais sa chance au lieu que la place soit attribuée d'office.

Où écrire #spawncount, et ce qui se passe si deux membres se contredisent

Le nombre appartient au groupe d'apparition, pas à l'un de ses membres : l'écrire sur n'importe lequel des membres du #spawngroup suffit — l'exemple ci-dessus le répète sur les quatre par lisibilité, pas par obligation. Auparavant, seul le membre rencontré en premier comptait : un #spawncount écrit sur le deuxième était perdu sans un mot.

Si deux membres d'un même groupe d'apparition déclarent des nombres différents, le plus élevé l'emporte, et le log dit lequel a été retenu. La règle ne dépend pas de l'ordre des groupes dans l'éditeur — c'est précisément cet ordre qui causait l'ancien défaut — et un #spawncount est une garantie : la plus grande des deux promesses est celle qui tient les deux. Répéter le même nombre n'est pas une contradiction et ne produit aucun message.

Ceci change les missions existantes

Jusqu'à la 6.17.0 incluse, #spawnchance ne pouvait pas empêcher une apparition. La zone retirait jusqu'à dix fois et forçait le tirage au dernier essai, si bien qu'un groupe seul finissait toujours par apparaître : le tag ne changeait que le moment, jamais l'issue. Même #spawnchance=0 apparaissait. Les retirages et le tirage forcé sont désormais réservés à un #spawncount réellement écrit, où ils tiennent une promesse de nombre. Une mission en service qui utilise #spawnchance va donc faire apparaître moins de groupes qu'avant — c'est le comportement annoncé depuis toujours par cette page. Si un groupe doit apparaître à coup sûr, retirez son tag ou écrivez #spawnchance=100.

Exemple pratique — embuscade MANPADS

Vous voulez quatre positions de MANPADS dans une zone, mais seules deux environ devraient réellement être occupées. Placez quatre unités d'infanterie factices nommées :

CZ-Alpha-MANPAD-1 #spawnchance=50
CZ-Alpha-MANPAD-2 #spawnchance=50
CZ-Alpha-MANPAD-3 #spawnchance=50
CZ-Alpha-MANPAD-4 #spawnchance=50

Chaque position a 50 % de chances d'apparaître, indépendamment des autres — statistiquement, environ deux seront actives à chaque déclenchement de la zone. « Environ » est le mot juste : il arrivera qu'aucune n'apparaisse, et qu'elles apparaissent toutes les quatre. Si vous voulez exactement deux à chaque fois, c'est #spawncount qu'il faut, comme ci-dessus.

#command — apparition via la syntaxe de marqueur VEAF

Le tag #command transforme une unité en déclencheur à usage unique. À l'activation de la zone, VEAF exécute la commande à la position de l'unité et détruit l'unité. C'est l'équivalent du dépôt d'un marqueur de carte à cet endroit.

CZ-Alpha-SPAWN-SA11 #command="-spawn sa-11, side red"
CZ-Alpha-CONVOY-TRIGGER #command="-convoy from ZONE-DEPOT to ZONE-FRONT"

Cela permet de monter des apparitions complexes (batterie SA-11, convois avec routes IA) sans aucun code Lua.

L'unité déclencheur est un membre de la zone comme un autre : son nom doit donc lui aussi commencer par le nom de la zone. Une unité nommée SPAWN-SA11 posée dans CZ-Alpha n'est jamais lue, et sa commande n'est jamais exécutée.

Les commandes retardées sont rattachées à leur zone. Une commande peut porter un délai, de trois façons — -samsr!30 (délai d'alias), l'option delay d'un -spawn, ou une répétition. Dans ces cas, la commande rend la main avant d'avoir fait apparaître quoi que ce soit. Ce qui apparaît ensuite appartient bien à la zone : désactiver la zone détruit ces groupes comme les autres.

Avant la 6.15.9, ce n'était pas le cas : la zone lisait la liste de ce qu'elle avait créé trop tôt, donc un groupe retardé n'était enregistré nulle part et survivait à la désactivation de sa zone.

Si la zone est désactivée pendant que le délai court, le groupe qui apparaît ensuite est détruit immédiatement — rien ne pouvant annuler une apparition déjà planifiée, c'est le résultat que la désactivation aurait produit.

À noter : #spawndelay n'a jamais eu ce problème, parce qu'il retarde l'apparition de l'élément de zone lui-même, qui s'enregistre au passage.


Nom des groupes créés

Un groupe créé par une zone de combat n'a pas le nom que vous avez donné dans l'éditeur. Il ressemble à ceci :

[r]-Hydra Unit#10230

Trois parties, dont deux sont là pour de bon :

Partie Rôle Réglable ?
[r] / [b] / [n] la coalition du groupe non
Hydra Unit un nom inventé, pas le vôtre oui, voir plus bas
#10230 un identifiant unique non — DCS exige des noms de groupe uniques

Le nom inventé est délibéré : sans lui, un joueur lit le contenu d'une zone sur la carte F10 avant d'y aller. C'est le réglage veaf.HideNamesFromSpawnedGroups, actif par défaut.

Pour voir les vrais noms — en construction ou en débogage d'une mission :

mission:
  hide_names_from_spawned_groups: false

Les noms deviennent alors <nom de la zone> [r] <nom réel>#<id>. Le tag de coalition et l'identifiant restent dans les deux cas.

Le champ n'existe que depuis la 6.15.34. Avant, le réglage n'était atteignable que par module_settings: { veaf.HideNamesFromSpawnedGroups: false }, ce qui marche toujours.


Constantes du module

Constante Valeur par défaut Description
veafCombatZone.SecondsBetweenWatchdogChecks 60 Fréquence de vérification du watchdog de zone (s)
veafCombatZone.SecondsBetweenSmokeRequests 180 Délai entre marquages fumée (s)
veafCombatZone.SecondsBetweenFlareRequests 120 Délai entre marquages fusée (s)
veafCombatZone.RadioMenuName "COMBAT ZONES" Libellé du sous-menu F10
veafCombatZone.DefaultSpawnRadiusForUnits 50 Rayon de dispersion par défaut (m)
veafCombatZone.DefaultSpawnRadiusForStatics 0 Idem pour les objets statiques (m)
## Rayon de dispersion : les trois niveaux

À l'activation d'une zone, un groupe qui ne dit rien est dispersé autour de sa position. C'est souhaitable pour un convoi, mais pas pour une batterie posée dans les emplacements bétonnés que la carte fournit : quelques dizaines de mètres suffisent à mettre un lanceur sur le merlon au lieu d'être dedans.

Trois niveaux, du plus précis au plus général — le premier trouvé gagne :

Niveau Où l'écrire Portée
le groupe #spawnradius=0 dans le nom du groupe ou d'une de ses unités ce groupe
la zone default_spawn_radius sur une entrée de combat_zones tous les groupes non tagués de cette zone
la mission default_spawn_radius sous combat_zone_settings toutes les zones

0 veut dire « exactement là où je les ai placés » — pas « presque ». Le placement est strictement identique à celui de l'éditeur de mission.

Les objets statiques ont leur propre réglage, default_spawn_radius_statics, parce que leur défaut intégré vaut déjà 0 : un réglage unique pour les deux se mettrait à disperser des statiques aujourd'hui immobiles.

COMBATZONE:
  combat_zone_settings:
    default_spawn_radius: 50          # le défaut habituel, pour toute la mission
  combat_zones:
    - zone_name: CMBT_PALMYRA
      default_spawn_radius: 0         # ici, tout est placé au mètre : ne rien bouger
    - zone_name: CMBT_TIYAS           # celle-ci garde les 50 m



Définir une zone

Dans le cas le plus courant, les éléments sont peuplés automatiquement à partir des unités placées dans la trigger zone DCS via :addZoneElementsFromZoneNamed(...) :

local strikeZone = VeafCombatZone:new()
  :setMissionEditorZoneName("CZ-Strike-Alpha")     -- nom de la trigger zone DCS
  :setFriendlyName("Strike Alpha")                 -- libellé du menu radio
  :setBriefing("Détruire tous les véhicules. Prévoir de l'AAA et des MANPADS.")
  :addZoneElementsFromZoneNamed("CZ-Strike-Alpha")
  :initialize()

On peut aussi construire et attacher un élément manuellement avec :addZoneElement(...) :

local element = VeafCombatZoneElement:new()
  :setName("CZ-Strike-Alpha-ARMOR")
  :setDcsGroup(true)
  :setSpawnGroup("CZ-Strike-Alpha-ARMOR")    -- nom du groupe DCS à faire apparaître
  :setSpawnRadius(100)

strikeZone:addZoneElement(element)

Méthodes du builder VeafCombatZone

Méthode Description
:setMissionEditorZoneName(name) Trigger zone DCS qui définit la zone de spawn
:setFriendlyName(name) Libellé affiché dans le menu radio
:setBriefing(text) Texte complet du briefing
:setOnCompletedHook(fn) Callback quand tous les ennemis sont détruits
:addZoneElement(element) Ajouter un élément à la zone
:addZoneElementsFromZoneNamed(zoneName) Peupler les éléments depuis les unités d'une trigger zone
:addSpawnedGroup(groupOrName) Déclarer un groupe déjà apparu comme appartenant à la zone
:setActive(bool) Activer la zone au démarrage
:setTraining(bool) Mode entraînement
:setCompletable(bool) La zone peut être marquée comme terminée
:enableUserActivation() / :disableUserActivation() Autoriser/interdire l'activation par les joueurs
:setRadioGroupName(name) Regrouper cette zone (et toutes celles portant le même nom) sous un sous-menu radio commun
:setRadioMenuPrefix(text) Préfixe affiché devant le nom de la zone dans le menu

Méthodes du builder VeafCombatZoneElement

Méthode Description
:setName(name) Nom de l'élément
:setPosition(pos) Position de l'élément
:setDcsGroup(bool) L'élément référence un groupe DCS
:setDcsStatic(bool) L'élément référence un objet statique DCS
:setSpawnGroup(name) Nom du groupe DCS à faire apparaître
:setVeafCommand(cmd) Commande VEAF à exécuter au lieu d'un spawn
:setRoute(route) Route IA de l'élément
:setCoalition(side) Coalition de l'élément
:setSpawnRadius(m) Rayon de dispersion autour du centre de la zone
:setSpawnChance(pct) Probabilité d'apparition (0–100)
:setSpawnCount(n) Nombre d'exemplaires à faire apparaître
:setSpawnDelay(s) Délai avant apparition (secondes)

  • Activer — faire apparaître les groupes d'unités de la zone
  • Désactiver — faire disparaître les unités, réinitialiser la zone
  • Infos — statut, nombre d'unités restantes, briefing
  • Fumée — marquer la zone avec de la fumée (délai applicable)
  • Fusée éclairante — marquer la zone avec des fusées

Sécurité : par défaut, les commandes d'activation/désactivation sont sécurisées : le groupe agit au niveau de son occupant le moins gradé (voir veafSecurity). Seul le mode entraînement supprime cette restriction. Les demandes d'infos, fumée et fusée sont toujours accessibles à tous.

Options du menu radio

Méthode Description
:disableRadioMenu() Désactiver complètement le menu radio pour cette zone
:setRadioMenuPrefix(text) Préfixe affiché devant le nom de la zone dans le menu
:setRadioGroupName(name) Regrouper cette zone (et toutes celles portant le même nom) sous un sous-menu radio commun
:setEnableSmokeAndFlare(bool) Activer/désactiver les demandes de fumée et fusée (défaut : true)
:setShowUnitsList(bool) Inclure la liste des unités restantes dans le message info (défaut : true)
:setShowZonePositionInfo(bool) Inclure les coordonnées et la météo dans le message info (défaut : true)

Nettoyage des carcasses

Par défaut, à la désactivation d'une zone, les carcasses et cadavres sont automatiquement supprimés. Pour les conserver :

:disableJunkCleanup()

Opérations (zones groupées)

Plusieurs zones peuvent être regroupées dans une Opération qui se complète quand toutes les zones filles sont terminées :

local operation = VeafCombatOperation:new()
  :setMissionEditorZoneName("OP-THUNDER")
  :setFriendlyName("Operation Thunder")
  :setBriefing("Détruire les deux colonnes blindées avant qu'elles atteignent Senaki.")

operation:addTaskingOrder(alphaZone)                 -- première tâche
operation:addTaskingOrder(bravoZone, { "OP-THUNDER-ALPHA" })  -- débloquée après Alpha
operation:initialize()

VeafCombatOperation = VeafCombatZone:new() — l'opération étend VeafCombatZone. Les tâches sont ajoutées avec :addTaskingOrder(zone, requiredComplete), où zone est une VeafCombatZone et requiredComplete la liste optionnelle des noms de zones devant être terminées avant que celle-ci soit activée. L'opération apparaît dans le menu radio comme une seule entrée.

Son menu propose les infos, le briefing de chaque tâche active et, comme celui d'une zone, la commande Activer la zone (ou Désactiver la zone quand elle tourne — le libellé est celui des zones). Cette commande est sécurisée, sauf en mode entraînement, et absente quand l'activation par les joueurs est coupée (disableUserActivation()). Désactiver l'opération désactive aussi ses zones, et retire donc leurs unités ; la réactiver ne fait pas réapparaître une zone qui tourne déjà. Une opération peut aussi démarrer seule (active_at_start: true) ou par la commande de marqueur -zonestart.

Par défaut l'opération se range directement sous le menu des zones de combat. Le sous-menu Opérations n'existe que si operation_menu_name est renseigné dans combat_zone_settings.


Chaînage de zones

Une zone peut activer automatiquement une ou plusieurs zones suivantes lorsqu'elle est terminée. Cela permet de construire des progressions de campagne dynamiques sans scripting manuel :

VeafCombatZone:new()
  :setMissionEditorZoneName("CZ-Alpha")
  :setFriendlyName("Strike Alpha")
  :addChainedCombatZone("Strike Bravo")     -- se déclenche quand Alpha est terminée
  :addChainedCombatZone("Strike Charlie")   -- l'une est choisie au hasard
  :setChainedCombatZonesDelay(60)           -- attendre 60 s avant le chaînage
  :initialize()

Quand plusieurs zones chaînées sont définies, une seule est tirée au hasard — utile pour des scénarios à embranchements ou pour éviter la prévisibilité.

Méthode Description
:addChainedCombatZone(name) Ajouter une zone à déclencher après la complétion
:setChainedCombatZonesDelay(s) Secondes à attendre avant le chaînage (défaut : 0)

Mode entraînement

Mettre une zone en mode entraînement change deux choses :

  • Pas de sécurité : n'importe quel joueur peut activer ou désactiver la zone via le menu radio (normalement ces commandes sont réservées au niveau effectif du groupe — voir veafSecurity).
  • État détaillé : le message d'info de la zone liste les unités restantes et leurs positions approximatives (via fumée ou relèvements), donnant aux pilotes une vue claire de ce qu'il reste.
VeafCombatZone:new()
  :setMissionEditorZoneName("ZONE-TRAINING-A")
  :setFriendlyName("Training-A")
  :setTraining(true)
  :initialize()

Le mode entraînement est idéal pour des scénarios d'entraînement BFM / CAS où les pilotes ont besoin de connaître les positions des unités.


Suivre une zone en direct dans le journal

Quand une zone ne se comporte pas comme prévu (le panneau d'infos ne montre pas ce qui est sur le terrain, la zone ne se termine pas), activez les diagnostics dans mission.yaml :

module_settings:
  veaf.Diagnostics: true

Chaque zone écrit alors dans dcs.log, au niveau info et quel que soit le niveau de log des modules, des lignes marquées DIAG| : la demande d'activation et qui l'a faite, chaque élément apparu (son groupe, ses unités vivantes et leurs types) ou raté, le contenu exact du panneau d'infos groupe par groupe, chaque passage de la surveillance de fin de zone avec sa décision, et la désactivation. Filtrez le journal sur DIAG| pour ne lire que cela.

C'est bavard : réservez-le à une session que vous surveillez, puis remettez false.

Voir aussi