"TEMPLATE" DE MISSION V2.1 © Pumpkin


Le but final est d'obtenir une mission-type, avec les "outils" les plus courants disponibles de suite, et ne nécessitant que peu de modifications. Les différents aspects d'une mission-type, comme les objectifs, les fins de missions sont également expliqués, ce qui fait que ce long texte peut aussi être une source d'informations pour les créateurs de missions "débutants".

La version 2.1 est modifiée selon :

Pour que le "template" soit pleinement fonctionnel, il faut un jeu patché en version 1.56 !

Le "template" pour ArmA 2 simple n'a quasiment pas été modifié. Ont été modifié :


Index :
(cliquez sur les labels pour accéder rapidement au sujet)

  1. RÉPERTOIRES & FICHIERS
  2. PRESENTATION
  3. DÉPART DE MISSION : MISE EN PLACE DES JOUEURS, MODULES ET MUNITIONS
  4. GESTION DES DIFFÉRENTES OBJECTIFS
  5. GESTION DES DIFFÉRENTES FINS
  6. LE FICHIER INIT.SQF
  7. LE FICHIER RESPAWN_HANDLER.SQF
  8. LE FICHIER STRINGTABLE.CSV
  9. LE FICHIER DESCRIPTION.EXT
  10. LA ZONE "INTRO"
  11. LA ZONE "GENERATION DE BATIMENTS SUR POSITION MARQUEUR"
  12. ZONE "SCRIPT DE PATROUILLE ALÉATOIRE SUR UNE PATROUILLE"
  13. ZONE "GENERATION DE PATROUILLE SUR UNE POSITION DE LOGIQUE DE JEU" (uniquement O.A. !)
  14. ZONE "GENERATION DE PATROUILLE SUR UNE POSITION DE "LOGIQUE DE JEU" + SCRIPT 'PATROLS.SQF'" (uniquement O.A. !)
  15. GENERATION DE GROUPE D'ATTAQUANTS (uniquement O.A. !)
  16. GENERATION DE GROUPE DE DEFENSEURS (uniquement O.A. !)

 


1. RÉPERTOIRES & FICHIERS :

Nota : les fichiers/répertoires suivi de (© Pumpkin) sont issus de mes travaux personnels.

Les scripts des répetoires lib\ et debug\ sont succeptibles de changer sans que l'archive du "template" soit modifiée. Les dernières versions sont éventuellement à récupérer sur ma page

 


2. GÉNÉRALITÉS :

a. présentation de la mission "template"

La mission est découpée en "zone", chaque zone est indiquée par 2 marqueurs rouges : un marqueur de texte et un marqueur englobant la zone. Le marqueur de texte décrit la fonction des éléments dans la zone. Par exemple, la zone "intro" :

Le marqueur "lancement_intro" indique que la zone contient l(es) élément(s) nécessaire(s) au lancement de l'intro (le déclencheur qui lancera l'intro en début de partie), et le marqueur "lancement_intro_zone" indique la zone entourant les éléments. Dans ce cas simple, la zone ne contient qu'un élément.
Ces deux marqueurs sont évidemment à effacer dans la mission finale.

b. déclencheurs de paramètrages

Il y a deux déclencheurs au beau milieu de la carte. L'un d'eux sert à récupérer la liste de toutes les unités enemis pour un script de débugage (voir l'entrée de menu "* head cam", si le mode de débugage est activé.)
A effacer également dans la mission finale.

L'autre sert à régler la compétence des IA, aussi bien les IA déjà présentes que celles générées (à condition de laisser le déclencheur en mode répétition). Suivant que vous ayez envie de vous la jouer "tir au pigeons" avec des "larves" comme ennemis ou que vous soyez plutôt du genre "suicidaire" et que vous vouliez vous mesurer à des "terminators"

Désactiver cette fonctionnalité en mettant "false" à la place de "true" dans le champ "condition". Les niveaux approximatifs sont :

c. compatibilité Multi-joueurs des déclencheurs

Dans le cas de mission MP, il y a des cas où un déclencheur ne doit s'exécuter que sur le serveur. Le cas le plus typique, c'est lorsque le créateur de mission veut joindre une IA à l'équipe des joueurs humains, comme dans le cas du déclencheur au-dessus de l'équipe des joueurs

si le déclencheur ci-dessous n'avait que "this" dans le champs condition et que la mission était joué sur serveur dédié avec 3 joueurs, malgré l'activation à "une fois", il se déclencherai 4X ! : 1 X sur le serveur dédié + 1 X sur la machine des 3 joueurs = 4. Or on ne veut qu'un unique déclenchement. Et comme la mission est joué sur un serveur dédié, c'est lui qui doit gérer l'ensemble du scénario.

On va utilisé, pour limiter le lancement au serveur, la propriété de l'unité "logique de jeu" nommé ici "logictestserveur", car un logique de jeu est toujours local au serveur, traduisez en gros, n'a d'existence que sur le serveur. Donc, si l'on ajoute "... and (local logictestserveur)" au champ "condition" des déclencheurs qui ne doivent se déclenché QUE sur serveur, la condition n'étant pas remplie sur les machines des joueurs, le déclencheur ne s'activera QUE sur le serveur. La méthode a également l'avantage de fonctionner sous l'éditeur, puisque la machine du créateur de mission est automatiquement serveur dans ce cas.

this and (local logictestserveur)
"this" pour une activation par BLUFOR et à la condition que l'unité "logictestserveur" existe sur la machine

[contact] join group (thislist select 0)
le civil nommé "contact" rejoindra le groupe du 1er "BLUFOR" qui activera le déclencheur.

On peut aussi déterminer un lancement sur les machines des joueurs et NON sur le serveur dédié, par la condition inverse, soit "... and (not (local logictestserveur))"

 

Autre exemple de bug classique : le lancement de script par déclencheur. Vous avez trouvé un script génial mais lorsque vous l'intégrez dans votre mission, ça capote, ça ne marche pas ou trés mal,... bref c'est la cata ! Et pourtant le script marche trés bien lorsque vous le testez sur l'éditeur. Ce sont généralement les symptômes d'un script non prévu pour le jeu en MP, et qui se lance plusieurs fois : il s'auto-parasite. Si le script en question est lancé via déclencheur, on peut utiliser la même technique.

Dans le template, j'ai utilisé mon script de "téléportation" (quelque chose d'un peu plus "élégant" qu'un simple "setpos"), que je lance par déclencheur, sans oublié d'ajouter le "...and (local logictestserveur)", qui garanti un lancement exclusif sur serveur. Avec le petit hélico, et en ayant pris soin de faire monter tout le monde à bord, je me suis dirigé vers ce déclencheur, derrière les hangars. Résultat : toutes les machines détectent la présence "BLUFOR" mais comme le "logique de jeu" n'a "d'existence" que sur le serveur, "(local logictestserveur)" ne sera vrai que sur le serveur, et donc lui seul s'occupera de "téléporter" ma fine équipe et l'hélicoptère, sans qu'un lancement sur une autre machine ne vienne parasité le script lancé par le serveur.

Ceux qui commence à se sentir une âme de scripteur peuvent aussi éventuellement mettre tout de suite en entête de script deux lignes qui font appel à mes fonctions :

_testclient = call compile preprocessFile "lib\Client_detection.sqf";
if (not _testclient) exitwith {};
Interdit le lancement sur serveur : le script fonctionnera uniquement sur une machine client (=de joueur) (et dans tous les cas en éditeur).

_testserver = call compile preprocessFile "lib\Server_detection.sqf";
if (not _testserver) exitwith {};
Interdit le lancement sur machine client (=de joueur) : le script fonctionnera uniquement sur serveur (et dans tous les cas en éditeur).

 


3. DÉPART DE MISSION : MISE EN PLACE DES JOUEURS, MODULES ET MUNITIONS

a. le "respawn" (ou résurrection du joueur)

Les deux types de "respawn" les plus utilisés sont le respawn "INSTANT" et le respawn "BASE". La définition du respawn est à mettre dans le fichier "description.ext"

fichier "description.ext"

...
respawn="INSTANT";
//respawn="BASE"; // nécessite un marqueur 'respawn_west' et/ou 'respawn_vehicle_west'
RespawnDelay = 10; // compte à rebour
RespawnDialog = true; // montre le dialogue
...

respawn="INSTANT" indique le type de respawn. Dans cet exemple, le respawn type "BASE" est désactivé (mis en remarque, en placant "//" devant la ligne). Le RespawnDelay représente le nombre de secondes avant résurrection et RespawnDialog permet d'activer/désactiver le petit dialogue avec score que l'on voit en attendant de revenir dans le jeu.

Dans le cas d'un respawn type "BASE", il faut un marqueur nommé impérativement "respawn_west", ou "respawn_east", "respawn_guerrila" ou encore "respawn_civilian", suivant le camp des joueurs. Un deuxième marqueur peut s'avérer indispensable si on veut faire réapparaitre les joueurs en véhicules, marqueur nommé "respawn_vehicle_west", "respawn_vehicle_east", "respawn_vehicle_guerrila", "respawn_vehicle_civilian". Pour plus d'infos sur le fichier "description.ext", se référer à la doc affiliée sur le wiki de bistudio.com.

b. les modules

Trois modules sont greffés sur les joueurs : sytème de 1ers soins et simulation de blessure alternative. Pour faire court, ils activent le système qui permet l'immobilisation du joueur blessé, trainer ou porter un blessé, l'assistance médicale d'urgence par un autre joueur,... bref, toute la gestion de joueur blessé. Tous les modules doivent être synchroniser à chacun des joueurs.

Note : le module défrichage a été supprimé, car, pour une raison inconnue, il semble qu'il génère un bug : les joueurs basculent en mode captif et sont ensuite ignorés des IA la seule preuve du bug est le fait que ce bug ait disparu après la suppression de ce module, et ce sur plusieurs missions "hors template". Le seule avantage de ce module est d'éventuellement alléger les lags en réseau, en supprimant le corps des joueurs morts, ce que fait le système de respawn du "template".

Un module supplémentaire, le module d'appui simple, permet l'utilisation de parachutage de ravitaillement et de renfort, d'appui aérien, d'artillerie et de mortier. Les variables dans le champ "initialisation" permettent d'activer/désactiver les différents type d'appui. Par défaut, tous les appuis sont activés. Pour les désactiver, il suffit de mettre "false" à la place de "true". (NOTA : ce module n'est disponible que sur Opération Arrowhead)

BIS_SSM_AmmoDrop_AVAILABLE_WEST = false;
BIS_SSM_UnitsDrop_AVAILABLE_WEST = false;
BIS_SSM_Airstrike_AVAILABLE_WEST = false;
BIS_SSM_Mortar_AVAILABLE_WEST = false;
BIS_SSM_Artillery_AVAILABLE_WEST = false;
BIS_SSM_CeaseFire_AVAILABLE_WEST = false;
missionNamespace setVariable ["BIS_SSM_Mortar_ENABLED_WEST", false]; missionNamespace setVariable ["BIS_SSM_UnitsDrop_ENABLED_WEST", false]; missionNamespace setVariable ["BIS_SSM_AmmoDrop_ENABLED_WEST", false]; The settings above should give you artilleriy support with a delay of 60 seconds and airstrike support having an A-10 showing up. Accessoirement, on peut modifier le délai du support artillerie
missionNamespace setVariable ["BIS_SSM_Artillery_DELAY", 60];
ou encore le type d'avion et la bombe utilisés pour l'appui aérien
missionNamespace setVariable ["BIS_SSM_Airstrike_VEHICLE_WEST","F35B"]; missionNamespace setVariable ["BIS_SSM_Airstrike_DISPERSION_WEST","CruiseMissile 1"];

Pour plus de précisions à propos des modules, consultez la page des module de bistudio

c. les armes et munitions

Comme il est relativement pénible de mettre à disposition toutes les armes, un script de remplissage universel est greffé sur la caisse. Ce script rempli la caisse avec toutes les armes disponibles, quelque soit les addons supplémentaires ajoutés, officiel (BAF, PMC,...) ou non officiel (ACE2, ADO, Felin/R3F, Hexagon,...), le nombre des armes disponible est fonction de la nature de l'arme (grand nombre d'arme primaire, nombre réduit de lance-roquette,...), même chose pour le nombre de munition.

nul = [this] execVM "lib\Rearm_Cargo.sqf"

 


4. GESTION DES DIFFÉRENTES OBJECTIFS

La gestion des objectifs est LE gros morceau de la mission car elle fait intervenir des déclencheurs, le script "briefing.sqf", le fichier "briefing.html" et les marqueurs de la zone "positions des objectifs pour le briefing"

    Contient :
  • un élément "logique de jeu"
  • le déclencheur (carré, le + haut) initialisant les "objectifs" (variables)
  • 4 déclencheurs (ronds, à gauche) servant à valider les objectifs
  • 3 déclencheurs (ronds, à droite) servant à basculer vers un autre objectif
    La zone "positions des objectifs pour le briefing" contient :
  • 4 marqueurs pour positionner les objectifs

a. le script "briefing.sqf"

C'est lui qui défini et permet l'affichage du briefing dans le jeu

script "briefing.sqf"

T1 = player createSimpleTask["nom_obj1"];
T1 setSimpleTaskDescription["Description de l'objectif 1. Les blablabla...", "Titre Objectif 1", "label du marqueur Objectif 1 sur carte"];
T1 setSimpleTaskDestination (getMarkerPos "objectif1");
player setCurrentTask T1;

L'objectif décrit ci-dessus est le premier objectif. Il faut indiquer aux joueurs que cet objectif est celui en court, ce qui est fait par la ligne de commande

player setCurrentTask T1;

pour créer un nouvel objectif, il faut :

Ce qui donne, pour l'objectif 2, dans la mission "template":

script "briefing.sqf"

T2 = player createSimpleTask["nom_obj2"];
T2 setSimpleTaskDescription["Description de l'objectif 2. Les blablabla...", "Titre Objectif 2", "label du marqueur Objectif 2 sur carte"];
T2 setSimpleTaskDestination (getMarkerPos "objectif2");

La seconde partie du fichier "briefing.sqf" permet d'ajouter des entrées (= des notes) supplémentaires grâce à la commande "createDiaryRecord". Le nombre d'entrées n'est pas limité, le choix des 5 entrées ci-dessous est purement arbitraire.

script "briefing.sqf"

...
player createDiaryRecord["Diary", ["Soutien","Soutien : à compléter. Les changements de lignes se font automatiquement."]];
player createDiaryRecord["Diary", ["Exécution","Exécution : à compléter. Les changements de lignes se font automatiquement."]];
player createDiaryRecord["Diary", ["Mission","Mission : à compléter. Les changements de lignes se font automatiquement."]];
player createDiaryRecord["Diary", ["Situation","Situation : à compléter. Les changements de lignes se font automatiquement."]];
player createDiaryRecord["Diary", ["Briefing","exemple de Briefing dans le journal..."]];
...

ces entrées sont visibles en jeu à l'affichage de la carte par "l'onglet Notes" (en ordre inverse)

 

b. les déclencheurs de gestion des différentes objectifs

- Le déclenchement des déclencheurs se fait grâce aux variables (nommées "objectif_T1", "objectif_T2",...) qui, lorsqu'elles basculent à "true" (= vrai), active les commandes du champs "sur activation". Pour rester "propre", il convient tout d'abord d'initialiser toutes les variables servant aux déclenchements des objectifs à "false" (= faux), ce qui est fait grâce au déclencheur carré, activé au début de la mission (cf. la condition "true")

 

- Lorsque "objectif_T1" passe à "true" (sous l'effet d'un autre déclencheur de votre cru, avec le champ "sur activation" contenant "objectif_T1 = true"), le déclencheur ci-dessous s'active :

Il commute le 1er objectif à l'état "succés" par la commande

T1 setTaskState "SUCCEEDED";

La ligne suivante est un appel à un script de BIS pour afficher le texte "Tâche accomplie..." que l'on voit au haut de l'écran, en jeu.

nul = [objNull,objNull,T1, "SUCCEEDED"] execVM "CA\Modules\MP\data\scriptCommands\taskHint.sqf"
Dans cette ligne, seuls importent les 2 arguments T1 et "SUCCEEDED", a faire varier selon l'objectif, bien entendu.

 

- Enfin, le dernier déclencheur, optionnel, permet de faire basculer automatiquement vers l'objectif suivant (en l'occurence l'objectif 2). A noter les délais mini, moyen et max de 1 seconde, qui laisse le temps au premier message "Tâche accomplie..." de s'afficher, puis d'afficher le suivant, "Tâche en cours..."

La 1ère commande rend l'objectif courant.

player setCurrenttask T2;
Il change également l'emplacement du curseur/marqueur jaune, significatif de l'objectif en cours, sur l'emplacement de l'objectif 2, défini dans le fichier "briefing.sqf"

l'appel suivant est identique au déclencheur précédent, mis à par que l'état en paramètre est "CURRENT" au lieu de "SUCCEEDED"

nul = [objNull,objNull,T2, "CURRENT"] execVM "CA\Modules\MP\data\scriptCommands\taskHint.sqf";

 

La gestion des autres objectifs, "T2" et "T3", est strictement équivalente. Seul l'objectif 4 n'a pas de déclencheur-bascule associé, il peut être considéré, par exemple, comme une objectif secondaire.

Sans l'avoir intégrer dans la mission-"template", pour info : il est possible de créer un nouvel objectif via un déclencheur. Il suffit d'insérer dans le champ "sur activation", un code ressemblant à ceci, trés proche du code se trouvant dans "briefing.sqf" :

T5 = player createSimpleTask["nom_obj5"];
T5 setSimpleTaskDescription["nouvel objectif secondaire 5", "Titre Objectif 5", "label du marqueur Objectif 5 sur carte"];
T5 setSimpleTaskDestination (getMarkerPos "objectif5");
nul = [objNull,objNull,T5, "CREATED"] execVM "CA\Modules\MP\data\scriptCommands\taskHint.sqf";
Il vous faudra également au moins créer un marqueur, nommé "objectif5" dans l'exemple ci-dessus

Je vous laisse le soin de comprendre, sur la base des explications déjà données. Notez cependant l'argument "CREATED" pour le script "taskHint.sqf". On a déjà vu les arguments "SUCCEEDED" et "CURRENT" dans les 2 déclencheurs ci-dessus; il y en a 5 au total pouvant être utiliser comme argument par ce script :

Dernière remarque : j'ai placé des aides dans le fichier "description.ext" afin de vous permettre de mieux comprendre son fonctionnement, ce que l'on peut y mettre et comment le placer, comme par exemple une image dans le briefing (voir <img image='imgs\coltanminesketch.paa'...), cliquer sur un pseudo-lien pour désigner un endroit précis (cf. <marker name='marqueur_obj_1'>ici</marker>), avec l'effet de dézoom-déplacement-zoom que l'on connait bien.

 


5. GESTION DES DIFFÉRENTES FINS

Les conditions des differentes fins étant extrèmement variées et dépendant du créateur de mission, il est impossible d'intégrer "quelque chose" de standard dans une mission "template" comme celle-ci. Cependant, pour donner une exemple et assuré le lien entre les objectifs et les différentes fins, j'ai laissé quelques déclencheurs "de test" dans la mission (qui seront à effacer dans votre mission finale). Le déclencheur de gauche simule un succés total de la mission. Il met à "true" les variables "objectif_T1", "objectif_T2", "objectif_T3" et "objectif_T4". Le déclencheur du bas simule un succès partiel en mettant à "true" les variables "objectif_T1" et "objectif_T4". Ces deux déclencheurs de test activeront également certains ou tous déclencheurs d'objectif vu au-dessus. Il faut imaginer que ce sont des objectifs géographiquement éloignés et/ou de natures différentes (assassinat réussi, franchissementde zone,...) et donc, chaque variable "objectif_Tx" sera commuté à "true" par des déclencheurs dont les conditions de déclenchement seront trés différentes. Passer par des variables de déclenchement telles "objectif_Tx" facilite la gestion des objectifs et des fins car chaque variable fait double emploi et interagit sur les objectifs en même temps que les fins.

- Le déclencheur du haut est celui qui test l'echec de la mission. Il s'active lorsque le joueur pénètre dans la zone (= activation "BLUFOR" et donc le "this") et qu'aucune des variables "objectif_T[1 à 3]" n'est passé à "true" (couplage des tous les "(not objectif_T[1 à 3])" par "and"). Pour le tester en prévisualisation, il suffit de courir vers le nord de la piste de l'aérodrome dès l'entrée dans la mission. A noter l'activation "BLUFOR" pour déclenchement uniquement en présence des joueurs,

- Le déclencheur du milieu teste la réussite partielle de la mission. Il s'active lorsque le joueur pénètre dans la zone et qu'une ou plusieurs des variables "objectif_T1 à 3" est passé à "true", mais pas toutes. La variable "objectif_T4" n'est pas testée car elle est liée à un objectif considéré comme secondaire. Comme précédemment, le "this" permet de tester l'entrée des joueurs dans la zone; (objectif_T1 or objectif_T2 or objectif_T3) donne "true" si au moins une variable est "true"; le liaison entre les 2 évènement est faite par le and". Pour le tester en prévisualisation, il faut courir vers le sud de la piste dans un premier temps, jusqu'a ce qu'un message "simulation de succés mitigé..." apparaisse, puis courir au nord de la piste.

- Le déclencheur du haut est celui qui test l'echec de la mission. Il s'active lorsque le joueur pénètre dans la zone et qu'aucune des variables "objectif_Tx" n'est passé à "true". Pour le tester en prévisualisation, il suffit de courir vers le but de la piste de l'aérodrome dès l'entrée dans la mission

Lorsque vous passez dans l'un de ces 3 déclencheurs, vous obtenez le dialogue de fin habituel, avec les commentaires de succés, de réussite mitigée ou d'échec.

Ces commentaires sont définis dans le fichier "briefing.html". Il n'est pas difficile de faire le rapport entre ce qui est inscrit dans le fichier et le contenu des dialogues ci-dessus, je ne n'étend donc pas trop sur ce sujet. Notez seulement le rapport qu'il y a entre la balise "<a name="Debriefing:End1">" et le déclencheur de type "Fin n°1" (= succès total de la mission), la balise "<a name="Debriefing:End2">" et le déclencheur de type "Fin n°2" (= réussite mitigée), et enfin, entre la balise "<a name="Debriefing:Loser">" et le déclencheur de type "perdre"

fichier "briefing.html"

<html>
<head>
<title>titre de la mission</title>
</head>
<body>

<br>
<h2><a name="Debriefing:End1">réussite totale</a></h2>
<br>
<p>Vous avez réussi la mission !</p>
<br>

<hr>
<br>
<h2><a name="Debriefing:End2">réussite mitigée</a></h2>
<br>
<p>Votre réussite est mitigée !</p>
<br>

<hr>
<br>
<h2><a name="Debriefing:Loser">Echec de la mission</a></h2>
<br>
<p>Vous avez échouez !</p>
<br>

</body>
</html>
</div>

 


6. LE FICHIER "INIT.SQF"

Ce fichier est systematiquement lancé sur chaque machine au départ de la mission. En conséquence, tous les scripts et commandes devant être lancé au moins une fois peuvent l'être à partir de ce fichier.

fichier "init.sqf"


// INIT.SQF TEMPLATE © Pumpkin V2.06
// ---------------------------

// --------------------------------------------------------------------------------------------------
// METTRE LES DOUBLES BARRE (//) DEVANT UNE LIGNE DE COMMANDE POUR LA DESACTIVER, NE PAS L'EFFACER !!

// Pour test Client
_testclient = call compile preprocessFile "lib\Client_detection.sqf";
// Pour test Serveur
_testserver = call compile preprocessFile "lib\Server_detection.sqf";

// Messages d'erreur
if (format["%1",_testclient]=="") then {
	sleep 1;
	hint "ERREUR INIT.SQF: script 'lib\Client_detection.sqf' absent";
	sleep 2;
};
if (format["%1",_testserver]=="") then {
	sleep 1;
	hint "ERREUR INIT.SQF: script 'lib\Server_detection.sqf' absent";
	sleep 2;
};


// ------------------------------------------------------------------------------------------------
// PLACER ICI LES SCRIPTS DEVANT S'EXECUTER SUR LE SERVEUR ET SUR LE CLIENT (=la machine du joueur)

 

// ------------------------------------------------------------------------------------------------ // PLACER ICI LES SCRIPTS NE DEVANT S'EXECUTER QUE SUR LE SERVEUR // ( À L'INTÉRIEUR DES {...}; !!) if (_testserver) then { // Init permettant de diffuser en MP le texte des chats, = objet nommé "PUMPKINBroadcaster" nul = execVM "lib\Init_Speach_Broadcast.sqf"; // Script de destruction de cadavres enemis //nul = [east] execVM "lib\Clean_bodies.sqf"; // Init pour fonctions de debuggage "maison" en menu // seulement si serveur non dédié if (not isDedicated) then { nul = execVM "debug\initdebug.sqf"; }; }; // ------------------------------------------------------------------------------------------------ // PLACER ICI LES SCRIPTS NE DEVANT S'EXECUTER QUE SUR LE CLIENT (=la machine du joueur) // ( À L'INTÉRIEUR DES {...}; !!) if (_testclient) then { // =================================== // TOUTES LA DOCUMENTATION DU BRIEFING // Met en place le briefing nul = execVM "briefing.sqf"; // Documentation(s) accessible(s) en jeu nul = execVM "medkit\medkit_doc.sqf"; nul = execVM "lib\docs\Repair_veh_doc.sqf"; // =================================== // Temporisation de sécurité pour mission MP : pour connexion rapide (LAN), il vaut mieux vérifier de // l'existence du joueur, et temporiser, sans quoi les scripts destinés au joueur se lance "dans le vide" waituntil{player iskindof "man"}; sleep 0.3; waituntil{alive player}; sleep 0.3; // Acces "Admin" en MP [] spawn { while {true} do { waituntil { serverCommandAvailable "#shutdown" }; nul = execVM "debug\initdebug.sqf"; waituntil { not (serverCommandAvailable "#shutdown") }; nul = execVM "debug\closedebug.sqf"; }; }; // Affichage de la version (pas utile pour un sou, juste pour mon comfort personnel afin de savoir où j'en suis ;D ) [] spawn { cuttext ["init V2.06", "PLAIN DOWN"]; sleep 1; cuttext ["", "PLAIN"]; }; // Mise en place du scruteur d'évènement "mort du joueur"; lancera automatiquement la // récupération des armes (et du contenu du sac à dos pour OA) dès la mort du joueur player addEventHandler ["killed",{execVM "respawn_handler.sqf"}]; // Autoriser (ou non) le système de scrutation du contenu du sac à dos (ajout des menus "Arme", ...). "Event_Inventory.sqf" // est lancé ici, dans le fichier "init.sqf" et dans le "respawn_handler.sqf". Il faut activer/désactiver les 2 lancements. nul = execVM "plr_respawn\Event_Inventory.sqf"; // Init Systeme de soins scripté perso medkitnum = 103; // modification du nombre de medkit disponible par mission nul = execVM "medkit\medkit.sqf"; // Activation du menu de récupération des medkits nul = player execVM "medkit\recover_medkits_menu.sqf"; // Init script de reparation de vehicule //nul = [2,["Car","Motorcycle","Air"]] execVM "lib\repair_veh.sqf"; //nul = [2,["Car","Motorcycle","Plane"]] execVM "lib\repair_veh.sqf"; //nul = [0.7] execVM "lib\repair_veh.sqf"; nul = [] execVM "lib\repair_veh.sqf"; };

  1. En jeu multi-joueurs, certains scripts doivent être lancé uniquement par le serveur, et d'autres uniquement par la machine-client (autrement dit la machine du joueur). Toute la partie ci-dessous ne sert qu'à mettre en place un système de test pour savoir sur quelle machine le script "init.sqf" a été lancé et exécuter les scripts dédiés serveur et dédiés clients, comme les commentaires l'indiquent. NE PAS Y TOUCHER, et ne pas oublier d'installer le répertoire "lib\" et ses scripts, s'il n'existe pas.
    	// Pour test Client
    	_testclient = call compile preprocessFile "lib\Client_detection.sqf";
    	// Pour test Serveur
    	_testserver = call compile preprocessFile "lib\Server_detection.sqf";
    
    	// Messages d'erreur
    	if (format["%1",_testclient]=="") then {
    		sleep 1;
    		hint "ERREUR INIT.SQF: script 'lib\Client_detection.sqf' absent";
    		sleep 2;
    	};
    	if (format["%1",_testserver]=="") then {
    		sleep 1;
    		hint "ERREUR INIT.SQF: script 'lib\Server_detection.sqf' absent";
    		sleep 2;
    	};

    Pour une mission solo, tout ceci n'aucune importance, mais pour une mission multi-joueur, il est impératif de faire le distinguo. Le fichier comporte donc 3 parties :

    1. La partie comportant les scripts communs au serveur/client OU pour mission solo. En l'occurence, il n'y a aucun script destiné à fonctionner à la fois sur le serveur et sur les clients.
      			// ------------------------------------------------------------------------------------------------
      			// PLACER ICI LES SCRIPTS DEVANT S'EXECUTER SUR LE SERVEUR ET SUR LE CLIENT (=la machine du joueur)
      			...
      			

    2. La partie comportant les scripts du serveur uniquement
      			// ------------------------------------------------------------------------------------------------
      			// PLACER ICI LES SCRIPTS NE DEVANT S'EXECUTER QUE SUR LE SERVEUR
      			if (_testserver) then {
      				...
      			};

      Dans cette partie, on va trouver :

      • Le lancement du script d'initialisation de mon propre système d'affichage de message en multi-joueurs, activé par défaut. J'utilise de plus en plus ce système de diffusion à tous les joueurs, de message de différents types (script de tir de barrage,...). S'il n'est pas utilisé, il ne ralentira rien, donc le désactiver ne fera rien gagner niveau performance, mais oublier de l'activer peut rendre "muettes" quelques uns de mes scripts. Donc, cher utilisateur du "template" et de mes scripts, laissez donc cette ligne active ;)
        nul = execVM "lib\Init_Speach_Broadcast.sqf";

      • Le lancement d'un script destiné à éliminer les "cadavres" des IA, sur les missions lourdes en IA, afin d'éviter un lag. Il est désactivé, car encore peu testé en condition réelle (si le coeur vous dit...), mais j'ai laissé l'appel à titre d'exemple
        // Script de destruction de cadavres enemis
        //nul = [east] execVM "lib\Clean_bodies.sqf";

      • Enfin, une petite astuce me permettant de ne pas avoir à activer/désactiver tout le temps mes outils de debug : Placer sous la section "lancement par le serveur", un test afin de savoir si c'est la machine-serveur n'est pas un serveur dédié et, dans ce cas, lancer mes outils de débugage. Ainsi, sous éditeur ou en serveur LAN, mes deux "environnements" de test, j'ai accès aux outils, alors qu'ils ne seront pas accessible en serveur dédié.
        					// Init pour fonctions de debuggage "maison" en menu
        					// seulement si serveur non dédié
        					if (not isDedicated) then {
        						nul = execVM "debug\initdebug.sqf";
        					};
    3. La partie comportant les scripts du client uniquement. D'une façon générale, tout script faisant référence au "player", ou mettant en place une entrée dans le menu du "player" doit être placé ici, car l'entité "player" n'existe pas pour un serveur dédié.
      			// ------------------------------------------------------------------------------------------------
      			// PLACER ICI LES SCRIPTS NE DEVANT S'EXECUTER QUE SUR LE CLIENT (=la machine du joueur)
      			if (_testserver) then {
      				...
      			};

  2. Le reste des explication ne concernent que les commandes et scripts exécutés sur les machines-clientes. Première chose à faire : lancer les scripts de documentation qui apparaitrons sur l'écran d'information. En premier lieu, le script qui doit être impérativement lancé est le "briefing.sqf"", vu dans le paragraphe sur le briefing. On va préférer le placer sur les machines clients, car, s'il peut être lancer également sur le serveur, personne ne le lira ;) . En cas de
    		nul = execVM "briefing.sqf";
  3. Ensuite, éventuellement, on peut lancer des scripts mettant en place des notes, journaux, et autres documentations consultable en jeu. Par exemple, ici, je lance la documentation explicant l'utilisation de mon système de trousse de secours, les medkits, ainsi que la façon de réparer un véhicule avec mon système de réparation "simpliste". Comme d'habitude, mettre // devant les lignes pour les inactiver (ou les effacer si votre décision de ne pas les utiliser est définitives... on est dans un pays libre après tout ;) )
    		// Documentation(s) accessible(s) en jeu
    		nul = execVM "medkit\medkit_doc.sqf";
    		nul = execVM "lib\docs\Repair_veh_doc.sqf";
  4. Les lignes suivantes sont une sécurité, assurant le lancement correct des commandes suivantes, qui nécessite impérativement "l'existence" de l'avatar du joueur. Elles n'aurait pas été vraiment nécessaires si je n'avais pas expérimenté des bugs de "non lancement" sur des parties "mixtes, en LAN et en WAN : l'initialisation pour les joueurs en WAN est "ralentie" du fait de leur connexion distante; par contre, en LAN, l'initialisation risque de se produire trop vite, empêchant notamment le respawn de fonctionner. Donc quelques petites lignes pour assurer le coup et attendre gentiment que le joueur humain sont "physiquement" présent :
    		// Temporisation de sécurité pour mission MP : pour connexion rapide (LAN), il vaut mieux vérifier de
    		// l'existence du joueur, et temporiser, sans quoi les scripts destinés au joueur se lance "dans le vide"
    		waituntil{player iskindof "man"};
    		sleep 0.3;
    		waituntil{alive player};
    		sleep 0.3;

  5. Ici, je lance en parallèle un petit bout de script qui permet l'accès à mes outils de debugage en jeu et en serveur dédié. Il suffit pour cela de se connecter en tant qu'admin sur le serveur dédié. Pour désactiver l'accès à mes outils, les sempiternels // devant les "nul =". Libre à vous de lancer d'autres scripts, mais ATTENTION, prévoyez de les stopper en cas de logout (= déconnection admin), sinon ils seront relancer à chaque fois que vous vous connecterez en admin !
    		// Acces "Admin" en MP
    		[] spawn {
    			while {true} do {
    				waituntil { serverCommandAvailable "#shutdown" };
    				nul = execVM "debug\initdebug.sqf";
    				waituntil { not (serverCommandAvailable "#shutdown") };
    				nul = execVM "debug\closedebug.sqf";
    			};
    		};
  6. Ces lignes sont quasiment inutiles ! Elles sont juste là pour afficher la version du fichier "init.sqf", pour mon confort personnel ;) Vous pouvez les supprimer si elles vous gênent.
    		[] spawn {
    			cuttext ["init V2.06", "PLAIN DOWN"];
    			sleep 1;
    			cuttext ["", "PLAIN"];
    		};

  7. La commande suivante est aussi importante que le lancement du "briefing.sqf" : Il met en place l'exécution d'un script qui s'exécutera à chaque mort du joueur. Dans cette mission-template, le script "respawn_handler.sqf" lance -TOUS- les scripts qui doivent être lancé après la mort du joueur (on verra le script en détail plus bas). Autre remarque trés importante : il ne doit y avoir qu'une ligne du type ...addEventHandler ["killed",{..., sinon il risque d'y avoir conflit et les scripts lancés par "respawn_handler.sqf" ne se lanceront peut-être pas. Dans la théorie, BIS indique qu'on peut ajouté des "gestionnaires d'évènement" pour un même évènement ("killed" en l'occurrence). Dans les faits, j'ai constaté quelques couacs qui me font préférer la méthode "1 seul eventhandler", qui lance un seul script qui, lui, va lancer tous les scripts "d'après mort".
    		player addEventHandler ["killed",{_this execVM "respawn_handler.sqf"}];

  8. Ce ligne lance le script de système de gestion des kits de premiers soins. Il se trouve, ainsi que ses scripts associés, dans le répertoire "medkit\". Il permet au joueur de se soigner sans avoir recours à un infirmier. Par défaut le nombre de kit de 1er secours est de 5, mais la valeur medkitnum = 3 (désactivée) permet de modifier le nombre de kits. Le chiffre de 5 a été choisi car, si le système de blessure alternative est actif (voir les modules), cela permet, après une blessure grave, de recouvrir totalement sa santé si on utilise tous les kits. Associé au système de 1ers secours et de blessures alternatif, il permet notamment à un petit groupe de joueur, ou à un sniper, isolé par définition, de ne pas être trop "embêter" par des blessures successives.
    		medkitnum = 10; // modification du nombre de medkit disponible par mission
    		nul = execVM "medkit\medkit.sqf";

  9. Partenaire du système de gestion des kits de premiers soins, le script permettant de récuperer la totalité du nombre de medkits en s'approchant d'un joueur-médecin
    		// Activation du menu de récupération des medkits
    		nul = player execVM "medkit\recover_medkits_menu.sqf";

  10. Suit le lancement d'un système d'alternance d'arme par menu. Ceux qui connaissent la célèbre mission "Domination" connaissent le "sac à dos virtuel scripté" de Xeno. C'est à peu près l'équivalent : si le joueur met une arme dans le sac à dos, un menu apparait, permettant d'alterner l'arme du sac à dos et celle du joueur; particulièrement intéressant pour utiliser un mitrailleuse lourde qui interdit normalement l'utilisation du sac à dos, idem pour les lances-roquettes. Ceci est le lancement initial. Le script s'arrête à la mort du joueur, mais comme cette ligne de lancement est présente dans le "respawn_handle.sqf", le script est relancé à chaque résurrection.
    		// Autoriser (ou non) le système de scrutation du contenu du sac à dos (ajout des menus "Arme", ...). "Event_Inventory.sqf"
    		// est lancé ici, dans le fichier "init.sqf" et dans le "respawn_handler.sqf". Il faut activer/désactiver les 2 lancements.
    		nul = execVM "plr_respawn\Event_Inventory.sqf";

  11. Le dernier script lancé permet à un joueur de réparer les véhicules (cf. la section de ma page sur ce script). J'ai laissé différents exemples de lancement en commentaire, pour faciliter les/vos tests.
    		// Init script de reparation de vehicule
    		//nul = [2,["Car","Motorcycle","Air"]] execVM "lib\repair_veh.sqf";
    		//nul = [2,["Car","Motorcycle","Plane"]] execVM "lib\repair_veh.sqf";
    		//nul = [0.7] execVM "lib\repair_veh.sqf";
    		nul = [] execVM "lib\repair_veh.sqf";

 


7. LE FICHIER "RESPAWN_HANDLER.SQF"

Comme indiqué ci-dessus, le script "respawn_handler.sqf" se lancera à chaque mort du joueur, une fois mis en place par la commande player addEventHandler ["killed",{_this execVM "respawn_handler.sqf"}]; dans le fichier init.sqf. Il faut distinguer deux phases : juste après la mort du joueur mais avant sa résurrection, et aprés sa résurrection. Certains script ne fonctionneront pas selon la phase à laquelle ils sont lancés.

fichier "respawn_handler.sqf"

// ---------- METTRE CI-DESSOUS LES SCRIPTS DEVANT S'EXECUTER AVANT LA REAPPARITION DU JOUEUR ----------


// recup. des armes dans la configuration juste avant la mort - A GARDER EN 1ERE POSITION IMPERATIVEMENT !
_isOperationArrowhead = "TK_CIV_Takistani_Base_EP1" isKindOf "civilian"; //test simple pour distinguer ArmA2 "normale" (jusqu'à V1.07) de Operation Arrowhead


// choix du script à lancer en fonction de la version (compatible sac à dos ou non compatible)
if ( _isOperationArrowhead ) then {
	nul = [ [50,50,300] ] execVM "plr_respawn\Respawn_player.sqf";
}
else {
	nul = execVM "plr_respawn\Respawn_player_A2.sqf";
};

// sécurité avant la poursuite des opérations sur le "nouveau" joueur : attendre que le joueur soit en vie
waituntil{alive player};
sleep 0.5;

// ---------- METTRE CI-DESSOUS LES SCRIPTS DEVANT S'EXECUTER APRES LA REAPPARITION DU JOUEUR ----------

if ( _isOperationArrowhead ) then {
	// ajout de la gestion du sac à dos (pour OA uniquement).
	nul = execVM "plr_respawn\Event_Inventory.sqf";
};

// // Activation du menu de récupération des medkits
nul = player execVM "medkit\recover_medkits_menu.sqf";

  1. Une petite astuce pour tester quel jeu est lancé...
    _isOperationArrowhead = "TK_CIV_Takistani_Base_EP1" isKindOf "civilian";
    ... pour pouvoir lancé le bon script de récupération des armes tel qu'avant la mort, le premier étant compatible OA+sac à dos ("plr_respawn\Recover_weapons.sqf"), le deuxième uniquement compatible Arma2 simple ("plr_respawn\Recover_weapons_A2.sqf", pour ceux qui n'aurait pas acheté l'extension...s'ils existent :) ). "Recover_weapons.sqf" rétabli non seulement les armes et munitions à l'identique, mais également le sac à dos et son contenu précédent (100 x merci à Madbull des R3F d'avoir trouvé le "St-Graal", autrement dit les 2 commandes qui permettent de scruter le contenu du sac à dos !). Le paramètre donné au script est un tableau permettant en pleine "mélasse", en cas de décès au beau milieu d'une foule d'ennemis furieux par exemple, d'être transporté dans une zone sûre, si cela est possible en fonction des paramètres donnés. En l'occurence, le script cherchera des ennemis dans un rayon de 30m, s'il en trouve, il élargira le cercle de recherche de 50m, et ainsi de suite, jusqu'à une distance maximale de 300m.
    	if ( _isOperationArrowhead ) then {
    		nul = [ [30,50,300] ] execVM "plr_respawn\Respawn_player.sqf";
    	}
    	else {
    		nul = execVM "plr_respawn\Respawn_player_A2.sqf";
    	};

  2. Suit l'attente de la résurrection, afin de lancer les autres scripts, qui doivent s'exécuter lorsque le joueur est vivant et réapparu.
    waituntil{alive player};
    sleep 0.5;

  3. Est tester ici quel type de jeu est lancé; si c'est OA, lancer la commande de gestion des menus/armes APRÈS la restoration des armes et du sac à dos.
    	if ( _isOperationArrowhead ) then {
    		// ajout de la gestion du sac à dos (pour OA uniquement).
    		nul = execVM "plr_respawn\Event_Inventory.sqf";
    	};

  4. Enfin, et si cette option n'a pas été désactivée dans le fichier "init.sqf", il faut rétablir le menu de recouvrement des medkits
    	// Activation du menu de récupération des medkits
    	nul = player execVM "medkit\recover_medkits_menu.sqf";

Pour donner un exemple supplémentaire de l'adaptation du script "étranger" à cette mission-template, je prendrais l'exemple de mon script de ravitaillement. Le fichier "init.sqf" de la mission d'exemple du ravitaillement est celui-ci :

nul = execVM "AirSupply\initsupplymenu.sqf";

player addeventhandler ["Killed", {execVM "AirSupply\initsupplymenu.sqf"}];
La 1ère ligne lance le script "AirSupply\initsupplymenu.sqf" qui installe les entrées liées au ravitaillement dans le menu du joueur. Comme il ne peut y avoir qu'un seul fichier "init.sqf", on prendra donc cette ligne pour la copier dans l'"init.sqf de la mission-template, ce qui donnera :

fichier "init.sqf"

...

// ------------------------------------------------------------------------------------------------
// PLACER ICI LES SCRIPTS NE DEVANT S'EXECUTER QUE SUR LE CLIENT (=la machine du joueur)
if (_testclient) then {

	nul = execVM "AirSupply\initsupplymenu.sqf";

	// Mise en place du scruteur d'évènement "mort du joueur"
	player addEventHandler ["killed",{_this execVM "respawn_handler.sqf"}];

	...

La 2ème ligne met en place le "scruteur d'évènement" pour que, après la mort du joueur, le menu se ré-installe automatiquement. Mais, comme je l'ai dis plus haut, deux lignes de type "player addeventhandler ["Killed", {..." sont INTERDITES SIMULTANÉMENT. On placera donc la ré-installation du menu dans le fichier DÉJÀ appelé par le "scruteur d'évènement", autrement dit le fichier "respawn_handler.sqf". Comme c'est une entrée de menu à ré-installer, on attendra la résurrection du joueur (logique ! on ne va pas la ré-installer sur le mort ! ;) ). On placera donc l'appel au script dans la seconde partie de "respawn_handler.sqf".

fichier "respawn_handler.sqf"

...

// ---------- METTRE CI-DESSOUS LES SCRIPTS DEVANT S'EXECUTER APRES LA REAPPARITION DU JOUEUR ----------

...

// ajout du menu "Check sac à dos" (pour OA uniquement)
if ( _isOperationArrowhead ) then {
	if (Pumpkin_CheckBackpack) then {
		menu_checkbackpack = player addAction ["Check sac à dos", "lib\Recover_weapons.sqf", true, 0, false, true, "", "true"];
	};
};

nul = execVM "AirSupply\initsupplymenu.sqf";

...

Moralité de l'exemple : Le copier-coller sauvage est une trés mauvaise idée lorsqu'on cherche à coupler les scripts récupérés ça et là !! :)

Petit ajout qui peut intéressé quelques joueur de team : j'ai voulu greffé tout mon système de script sur une mission Domination, pour voir s'il y avait imcompatibilité. Il s'avère que non, et l'intégration est très simple : renommer le fichier "init.sqf" d'origine et le lancer à la fin de mon propre fichier "init.sqf"

nul = execVM "init_dom.sqf";

 


8. LE FICHIER STRINGTABLE.CSV

Ce fichier permet la traduction automatique de texte, selon le pays du joueur. Sans s'étendre trop sur le sujet, j'ai mis ce fichier en place pour ceux qui en connaisse l'utilisation (ou ceux qui l'appendront :) ), et pour pouvoir au moins titrer la mission "internationalement". La 1ère ligne indique l'ordre des langues, la 2ème traduit le texte référencé par "STR_TTITRE_MISSION" dans les 8 langues. Je n'ai mis en place que l'anglais et le français. Cette traduction apparaitra sur l'écran de chargement (que l'on verra juste en dessous, dans le paragraphe "LE FICHIER DESCRIPTION.EXT") et lors de la petite intro.

LANGUAGE,"English","Czech","German","Polish","Russian","French","Spanish","Italian"

STR_TTITRE_MISSION, "Loadscreen Title of the Mission ", "!", "!", "!", "!", "Titre de la Mission de l'écran de chargement", "!", "!"

 


9. LE FICHIER DESCRIPTION.EXT

loadScreen = "imgs\loading.jpg";
OnLoadMission  = $STR_PKN_TITRE_CHARGMT;

respawn="INSTANT";
//respawn="BASE"; // nécessite un marqueur 'respawn_west' et/ou 'respawn_vehicle_west'
RespawnDelay = 10; // compte à rebour
RespawnDialog = true; // montre le dialogue

// définition des classes graphiques (textes,images,...)
class TitreIntro
{
	type = 0;
	idc = -1;
	x = 0.1;
	y = 0.4;
	w = 0.8;
	h = 0.4;
	colorText[ ] = {1, 0, 0, 1};
	colorBackground[ ] = {0, 0, 0, 0};
	style = 2+16+512;
	font = "TahomaB";
	//font = "LucidaConsoleB";
  	//font = "Zeppelin32";
  	//font = "EtelkaMonospaceProBold"
	text = "";
	lineSpacing = 1;
	sizeEx = 0.10;
};

class LogoPicture
{
	access = 0;
	type = 0;
	idc = -1;
	style = 48;
	colorBackground[ ] = {0, 0, 0, 0};
	colorText[ ] = {1, 1, 1, 1};
	font = "TahomaB";
	sizeEx = 0;
	lineSpacing = 0;
	text = "";
};


class RscTitles
{
	class TitreMission
	{
		idd = -1;
		movingEnable = 0;
		duration = 8;
		name = "TitreMission";
		class controls {

			class titretxt : TitreIntro
			{
				text = $STR_PKN_TITRE_MISSION;
			};
		};
	};

	class LogoMission
	{
		idd = -1;
		movingEnable = 0;
		duration = 8;
		name = "LogoMission";
		class controls {

			class logoimg : LogoPicture
			{
				type = 0;
				idc = -1;
				x = 0.2;
				y = 0;
				w = 0.55;
				h = 0.7;
				text="imgs\logo_mission2.paa";
			};
		};
	};
};

Ce fichier défini une telle foule de chose, que je ne vais pas tout expliquer, loin de là. Si vous êtes intéressé, voir la doc affiliée sur le wiki de BIS. Je vais me limité à ce que j'ai placé, et encore !

Je passe sur les définitions du respawn, déjà abordé. Ce qui suit, les "définition des classes graphiques" servent à définir les objets graphiques de l'intro, soit une image (le logo de l'intro) et un texte (le titre de la mission, reprit du fichier "description.ext"). Ces définitions sont trés lourdes (Et oui ! Tout ça juste pour pouvoir afficher un texte et une image !!), donc trés longues à expliquer. Je vais m'en tenir à l'essentiel, pour permettre une petite modification.

Petites remarques qui ont leur importance :

  1. Toute modification dans le fichier "description.ext" n'est visible que l'on recharge la mission.
  2. Une erreur, même minime, dans ce fichier provoque le crash d'Arma 2, lorsqu'on essaye de charger la mission ou si l'on essaye de sauver une mission avec une erreur dans le fichier "description.ext"
Donc méfiance lorsqu'on touche à ce fichier, ne le faites qu'après être sûr d'avoir une version saine de la mission sauvegardée quelque part.

 


10. LA ZONE "INTRO"

Tout ce qui a de plus simple : le déclencheur, avec condition "true", pour être lancé systématiquement en début de partie, exécute le script "intro.sqf"

fichier "intro.sqf"

sleep 2;
TitleRsc ["TitreMission","plain",1];
sleep 2;
cutRsc ["LogoMission","plain",1];

Simple aussi : attendre 2 secondes, afficher le titre par la commande "TitleRsc", attendre encore 2 secondes, afficher le logo par la commande "cutRsc". "TitreMission" et "LogoMission" sont les noms des deux ressources affichées et que l'on retrouve dans le fichier "description.ext". L'utilisation de "TitleRsc", puis de "cutRsc" permet la superposition du titre et de l'image; si on utilisé deux fois la même commande, l'image "chasserai", ferai disparaitre le titre.

 


11. LA ZONE "GENERATION DE BATIMENTS SUR POSITION MARQUEUR"

La possibilité de générer des groupements d'objets complexes sans avoir a placer tous les éléments les composants un par un dans l'éditeur est forcément intéressante, le procédé étant trés facile à mettre en oeuvre, moyennant un déclencheur et un marqueur.

Dans le champ "sur activation", on a une seule, mais trés longue ligne, qui va générer une composion d'objects (bâtiments, véhicules,...) à l'endroit du marqueur.

call { bati = [ [(getMarkerPos "position_nouveau_camp" select 0), (getMarkerPos "position_nouveau_camp" select 1), 0], 45, "Camp1_TK_EP1"] call (compile (preprocessFileLineNumbers "ca\modules\dyno\data\scripts\objectMapper.sqf")); }

On voit que les commandes sont entourés de parenthéses, précédé par "call". L'utilité de procéder ainsi, en exécutant les commandes entre "call{ }, permet plus facilement le copier-coller (pour ceux qui aiment le charabia : les variables entre { } sont locales au "call", la variable "bati", par exemple, n'a pas besoin d'être renommé.)

call {
	...
}

La ligne de commande ci-dessous est l'appel au script de BIS générant les compositions d'object et est, bien sûr, inaltérable.

bati = [ ... ] call (compile (preprocessFileLineNumbers "ca\modules\dyno\data\scripts\objectMapper.sqf"));

Beaucoup plus intéressant pour le créateur de mission, les valeurs entre "[ ]" sont les arguments à passer au script

[(getMarkerPos "position_nouveau_camp" select 0), (getMarkerPos "position_nouveau_camp" select 1), 0], 45, "Camp1_TK_EP1"
Pour pouvoir générer un autre batiment, il faut
  1. copier le déclencheur
  2. créer un marqueur
  3. remplacer le nom "position_nouveau_camp" du marqueur par celui nouvellement créé
  4. modifier, si besoin est, l'angle de rotation dans les paramètres
  5. choisir, le cas échéant, une nouvelle composition

NOTE POUR ARMA 2 SANS OA : Malheureusement, je ne sais pas si le script "objectMapper.sqf" existe encore sous Arma 2 version 1.07 sans OA. Aprés patch de OA, je me retrouve avec une version du script qui ne fonctionne plus sous Arma 2 version 1.07 sans OA. Pour palier à cet inconvénient, j'ai intégré une vieille version du script, que j'ai renommé "objectMapper_A2.sqf", qui, elle, fonctionne trés bien. Le code à intégré dans le déclencheur est :

call { bati = [ [(getMarkerPos "position_nouveau_camp" select 0), (getMarkerPos "position_nouveau_camp" select 1), 0], 45, "bunkerMedium04"] execVM "objectMapper_A2.sqf"; }
Et pensez aussi à n'utiliser que les compositions d'Arma 2 sans OA (des compositions comme "Camp1_TK_EP1" n'existe pas ! ;) )

 


12. ZONE "SCRIPT DE PATROUILLE ALÉATOIRE SUR UNE PATROUILLE"

Le déclenchement se fait par le champ "initialisation" du chef de groupe. "this" ou le nom du chef de groupe comme 1er paramètre, le nom du déclencheur comme 2ème paramètre. Pour plus de détails, voir l'entête du script ou ma page d'explication sur le script
nul = [patchief,nango_pat] execVM "patrols.sqf"
Le 1er paramètre, "patchief", est le nom du chef de groupe et aurait pu être remplacé par "this", car le script est greffé sur un homme du groupe. Mais cela montre que le script peut aussi se lancer par un déclencheur, avec comme 1er paramètre le nom de l'unité.

Il est possible que le script de patrouille aléatoire évolue (nouvelles fonctionnalités, débuggage,...). Un petit coup d'oeil à la page dédiée au script n'est pas inutile ;)

 


13. ZONE "GENERATION DE PATROUILLE SUR UNE POSITION DE LOGIQUE DE JEU"

(Attention ! spécifique OA !)

Remarques préalables importantes !

  • La génération de groupe par script (voir le champ "sur act." du déclencheur) ne peut fonctionner que si un "HQ I.A" existe sur la carte. La méthode la plus simple pour le créer est de placer au moins une unité de la FACTION ennemie n'importe où sur la carte sur la carte (Pour les experts, voir la commande "createCenter")
  • Il semble que, depuis le patch 1.55, les fonctions de BIS (BIS_fnc_spawnGroup, BIS_fnc_taskDefend,...) ne soient plus accessibles sans implanter le module "Fonctions" dans la mission. Et afin d'être pleinement compatible, il est préférable d'ajouter au champ "condition" : "... and (not isnil BIS_fnc_init)", afin de tester la fin de l'initialisation des fonctions de BIS.

Le but est de généré un groupe ennemi et de le faire patrouiller lorsque les joueurs entrent dans la zone du déclencheur, afin d'éviter de surcharger le jeu/ le serveur avec des unités inutiles (astuce mise en place dans la mission "Outgunned" de Dale). le "logique de jeu" nommé "position_pat_gen_01" sert à positionner la patrouille et la zone de patrouille. La génération du groupe et sa fonction de patrouille sont dû au "mini-script" dans le champ "sur activation" du déclencheur :

 call {
 grp = [(getPos position_pat_gen_01), EAST, (configFile >> "CfgGroups" >> "East" >> "BIS_TK_INS" >> "Infantry" >> "TK_INS_Patrol")] call BIS_fnc_spawnGroup;
 [grp, (getPos position_pat_gen_01), 150] call bis_fnc_taskPatrol;
 }

Execute le(s) commande(s) entre { }

call {
	...
}

grp = [(getPos position_pat_gen_01), EAST, (configFile >> "CfgGroups" >> "East" >> "BIS_TK_INS" >> "Infantry" >> "TK_INS_Patrol")] call BIS_fnc_spawnGroup;
Le groupe généré sera (égale à) "grp", apparaitra à la position (getPos position_pat_gen_01), appartiendra au camp EAST sera composé tel que défini par les classes (configFile >> "CfgGroups" >> "East" >> "BIS_TK_INS" >> "Infantry" >> "TK_INS_Patrol").

Pour déterminer ce qui doit suivre (configFile >> "CfgGroups" >>..., deux solutions :

Le tout entre [] composera les parametres pour la fonction BIS_fnc_spawnGroup, exécutée par le call. N'oubliez pas de faire la correspondance entre le 2ème paramètre "...,EAST,..." et la classe "...>> "East" >>..."

[grp, (getPos position_pat_gen_01), 150] call bis_fnc_taskPatrol;
Seconde ligne : le "grp" (1er paramètre), défini au-dessus, patrouillera autour de la position (getPos position_pat_gen_01), dans un rayon de 150 mètres. Cette fois, la fonction exécutée est bis_fnc_taskPatrol.

L'avantage de cette "méga"-ligne de commande, c'est qu'elle peut être "copiée-collée" à volonté (voir "copier-coller" tout le déclencheur). On a juste besoin de changer la position, et éventuellement le type de groupe que l'on veut insérer. Pour changer la position, on créera un autre "logique de jeu" et on remplacera position_pat_gen_01 par le nom du "logique de jeu". On peut aussi utiliser un marqueur mais, dans ce cas la commande (getPos position_pat_gen_01) deviendra (getMarkerPos "nom_du_marqueur"). Plus subtil, on peut utiliser une position pour la génération du groupe, et une autre position pour indiquer la zone de patrouille : le groupe apparaitra à un endroit, se dirigera vers le 2ème endroit et commencera à patrouiller.

Les attentifs auront peut-être remarqué les pointillés autour du "logique de jeu". Il a une rayon de placement de 500m, ce qui permet d'injecter un élément aléatoire de plus : la patrouille étant générée à l'endroit du "logique de jeu", celui-ci se placant aléatoirement, on ne sait pas où sera généré la patrouille.

 


14. ZONE "GENERATION DE PATROUILLE SUR UNE POSITION DE "LOGIQUE DE JEU" + SCRIPT 'PATROLS.SQF'"

(Attention ! spécifique OA !)

call {
grp = [(getPos position_pat_gen_01), EAST, (configFile >> "CfgGroups" >> "East" >> "BIS_TK_INS" >> "Infantry" >> "TK_INS_Patrol")] call BIS_fnc_spawnGroup;
nul = [leader grp, zargabad_pat] execVM "patrols.sqf";
}
C'est un cumul entre les deux types de patrouilles expliquée ci-dessus : générer un patrouille lorsque les joueurs entre dans une zone (= un déclencheur) et embrayer sur le script de patrouille aléatoire, afin de faire patrouiller le groupe dans la zone définie par un deuxième déclencheur. Cela se fait facilement en remplacant la ligne
[grp, (getPos position_pat_gen_01), 150] call bis_fnc_taskPatrol;
par la ligne
nul = [leader grp, zargabad_pat] execVM "patrols.sqf";
Autrement dit, utiliser le leader du groupe grp précédemment généré comme 1er argument, et le deuxième représente, comme expliqué précédemment, le déclencheur entourant la zone de patrouille. Cette méthode permet de multiplier les zones de patrouille potentielle et d'économiser un énorme temps-machine, puisque les patrouilles n'apparaissent qu'à l'approche des joueurs et que les scripts sont inactifs tant que la patrouille n'est pas générée.
On peut éventuellement utiliser le déclencheur générant la patrouille comme déclencheur indiquant la zone de patrouille, mais c'est une "petite" économie qui n'aurait pas beaucoup d'influence sur le jeu.

 


15. GENERATION DE GROUPE D'ATTAQUANTS

(Attention ! spécifique OA !)

Je ne rentre pas trop dans les détails, le système est quasiment identique à celui décrit au chapitre 13 : ZONE "GENERATION DE PATROUILLE SUR UNE POSITION DE LOGIQUE DE JEU". J'ai juste retardé l'apparition des attaquants à 30s après le début du jeu, afin de ne pas les avoir sur le dos de suite.

call {
grp = [(getPos attaquants01), EAST, (configFile >> "CfgGroups" >> "East" >> "RU" >> "Infantry" >> "RU_InfSquad")] call BIS_fnc_spawnGroup;
[grp, (getmarkerPos "attaque01")] call bis_fnc_taskAttack;
}

grp = [(getPos attaquants01), EAST, (configFile >> "CfgGroups" >> "East" >> "RU" >> "Infantry" >> "RU_InfSquad")] call BIS_fnc_spawnGroup;
Cette fois, c'est un groupe d'infanterie russe qui est généré : cf. EASTX2, classe "CfgGroups", sous-classes "East" (groupe du camp Est), puis "RU" (groupe RUsse), ensuite "Infantry" (groupe d'infanterie) et enfin "RU_InfSquad" (le nom du groupe). Ensuite vient la fonction de BIS qui provoque l'attaque de la part du groupe

[grp, (getmarkerPos "attaque01")] call bis_fnc_taskAttack;
le 1er paramètre est toujours le groupe généré, le 2ème est la position sur laquelle doit se porter l'attaque, en l'occurence la position du marqueur "attaque01" (cf. texte "attaque ennemie")

 


16. GENERATION DE GROUPE DE DEFENSEURS

(Attention ! spécifique OA !)

Système similaire à celui décrit aux chapitres 13 et 14.

call {
grp = [(getPos attaquants01), EAST, (configFile >> "CfgGroups" >> "East" >> "RU" >> "Infantry" >> "RU_InfSquad")] call BIS_fnc_spawnGroup;
[grp, (getMarkerPos "defense01")] call bis_fnc_taskDefend;
}

grp = [(getPos attaquants01), EAST, (configFile >> "CfgGroups" >> "East" >> "RU" >> "Infantry" >> "RU_InfSquad")] call BIS_fnc_spawnGroup;
Toujours le même groupe d'infanterie russe généré, comme au chapitre 15.

[grp, (getMarkerPos "defense01")] call bis_fnc_taskDefend;
le 1er paramètre est encore le groupe généré, le 2ème est la position que le groupe doit défendre. A noter que certain soldats du groupe resteront sur place, à attendre assis, tandis que les autres se rendront à l'emplacement à défendre, c'est-à-dire la position du marqueur "defense01" (cf. texte "défense ennemie"). Deuxième chose remarquable : il y a un "statique" à l'emplacement à défendre (un Kord mini-trépied); dès que les défenseurs arriveront sur place, un des soldats s'installera comme tireur dans le "statique".

 


(Plus qu'un petite place pour le mot de la fin :)

Bonne édition !