Le Langage MOHAA

retour sommaire

Auteur/Autor: Bjarne Grönnevik
Titre original : MOHAA scripting language
Traducteurs : Tropheus et [NER]Whisky T( )m

NDT : Je tiens tout d'abord à remercier Bjarne pour m'avoir accordé le droit de cette traduction. (Thank you Bjarne).
Je remercie aussi [NER]Whisky T( )m pour toute l'aide qu'il m'a apportée en tant en traduction qu'en explications.

Selon les voeux de l'auteur, la traduction sera la plus stricte possible et les exemples donnés par lui seront rigoureusement les mêmes. Seules quelques précisions vous seront données si l'occasion se présente. (en vert) Des exemples supplémentaires pourront aussi être fournis ultérieurement sur des liens séparés.
D'un point de vue technique, certains mots resteront en anglais car je n'en ai pas trouvé de traduction adéquate.
comme par exemple :Thread, methode...

Si par hasard, vous trouviez des erreurs dans le texte merci de me les faire savoir pour pouvoir les rectifier au plus vite.


Le langage utilisé dans MOHAA est très similaire de celui utilisé pour programmer en C++ ou en Java. Ce tutorial va essayer de démystifier ce langage à la fois pour les débutants mais aussi pour les plus chevronnés.

Sommaire.

* Le langage
* les opérateurs mathématiques de bases
* Les variables

* Créer des variables
* Créer des variables dans MOHradiant

* Les "opérateurs" de contrôle.

* if
* while
* for
*switch

* L'opérateur targetname
* Les "arrays"

* Créer un array
* Arrays multidimensionels
* Exemples

* Vecteurs
* Methodes

* Recevoir des information dans une methode.

* Les "Thread"

* Comment créer un "thread"
* Passer des informations dans un thread
* Attendre la fin d'un thread
* La commande "exec"

* Les scripts commençant automatiquement
* Les objets prédéfinis.
* Les objets "self"
* La priorité des opérateurs.
* Le casting

* automatique
* manuel

* Les "strings"
* Les références
* Les annexes.


Ce Tutorial se base sur trois choses :

* Le fichier Script Files.txt, fourni avec dans le dossier d'instalation de MOHradiant
* Ma propre expérience, en mapping, scripting et programmation.
* La grande connaissance de jv-map de .map (créateur de choses merveilleuses comme les BOTS jv-bots multiplayer)


* Le langage.

Le langage utilisé dans le scripting est défini dans le fichier Script Files.txt qui est fourni vec MOHradiant. Le problème (ou avantage?) est que sa définition est très fine et très difficile à comprendre pour quelqu'un qui n'aurait pas de bases solides en programmation. Un autre problème est le peu de description et le manque d'exemples. De plus il ne décrit que certaines parties du langage. Aussi, je vais essayer de combler les trous et d'expliquer ceci aussi clairement que je le peux.


* Les opérateurs mathématiques

La programmation et le langage utilisé pour le scripting sont de nature très mathématiques. Alors, commençons par les opérateurs mathématiques.

 exp + exp  Ajoute 2 valeurs : ex : 6+2 donne la valeur 8
 exp - exp  Soustrait 2 valeurs : ex : 6-2 donne la valeur 4
 exp * exp  Multiplie 2 valeurs : ex : 6*2 donne la valeur 12
 exp / exp  Divise 2 valeurs : ex : 6/2 donne la valeur 3
 exp % exp

 Modules 2 expressions (reste après une division)

ex 1 : 4%2 donne 0 car 4 divisé par 2 est une division sans reste.
ex 2 : 3%6 donne 3... 5%2 donne 1... 5%4 donne1.

 exp++  Incrément la valeur de 1. ex : 6++ donne le valeur 7
 exp--  Décrémente la valeur de 1. ex : 6-- donne la valeur 5
 exp | exp  ou
 exp ^ exp  ou exclusif
 exp & exp  et

Ces opérateurs sont très utilisés, et peuvent être insérés dans des opérations plus complèxes comme 4*2+5%2 par exemple. Mais on irait pas très loin en calculant seulement 5*12, il nous faut donc un endroit pour stocker ces valeurs : les variables.


* Les Variables

Une variable est un nom qui peut garder une valeur. N'importe quel objet peut avoir une variable. Il existe des variables qui sont crées par le jeu et d'autres que vous pouvez créer vous même.
Pour utiliser une variable prédéfinie, vous devez seulement connaitre son nom : Il existe une variable prédéfinie qui est utilisée par le "exploder system" (système qui gére les explosions) (exploder.scr) qui est appelée "level.target_to_destroy"... Bon..... Utilisons cet exemple : quel serait la valeur de level.target_to_destroy + 1 ? Et bien tout dépend de la valeur de "taget_to_destroy" au départ. Si elle est de 1, alors l'expression aura la valeur 2.

Maintenant que vous vous êtes familiarisé avec les variables, je vais vous parler d'un opérateur important le signe " = " . Cet opérateur assigne une valeur à une variable et ceci de cette façon.

 var = expr

Bon vous voulez définir le nombre de cible à détriure ? Pas de problème :

level.target_to_destroy = 2

Et c'est fait!

C'est vraiment très interressant lorsque vous avez plus de variables et moins de nombres à manipuler.

 Exemple :

On veut une limite de temps (roundlimit) pour ma map de 2 minutes par objectif, et un nombre d'objectifs aléatoire compris entre 2 et 5.

réponse :

level.target_to_destroy = randomint (3) +2
level.roundlimit = level.targt_to_destroy * 2

Vous commencez à trouver tout ceci cool n'est pas ? Et bien, ce n'est pas fini !

* Créer des variables

C'est très simple : nommez-la et elle existe! Mais vous devez la nommer dans un objet.
Comme :
(ici "indice" est l'objet)

local.indice = 0

Regardez aussi la section "objets prédéfinis" pour voir quels objets spéciaux sont disponibles !
Généralement on utilise les variables "local" et de temps en temps les variables "level".

(Les variables "local" ne peuvent être utilisées uniquemnt dans le "Thread" dans lequel elles ont été définies. Alors que les variables "level" peuvent être utilisées dans toutes les parties du script c'est à dire dans des "threads" différents.)

* Créer des variables dans MOHradiant.

Pour créer des variables dans des objets dans MOHradiant, vous sélectionnez simplement l'objet et entrez le nom et la valeur que vous voulez dans la fenêtre "entité" (touche "N"). Il y a des préfixes qui induisent l'utilisation des varibles par le langage.

$variable définie la "key" comme une variable de type texte.
#variable définie la "key" comme une variable de type numérique. (je pense)
variable fait que le jeu manipule la "key" comme une commande pour l'entité.

Peut-être ceci serait-il plus clair avec un exemple :
$variable et #variable devrait-être :
self.variable = "mavariable"
ou
self.variable = 4

variable est utilisée comme ceci :
self.variable"mavariable"

Comme 'variable' n'est pas une commande valide il en résulte une erreur ('failed execution of event 'variable'')

(merci à jv_map pour ses explications concernant la définition des variables standards)


* Les opérateurs de contrôle.

Il nous arrive d'avoir envie que certaines parties de notre script ne se déclenchent qu'à certaines conditions, ou que d'autres parties du script se répètent un certain nombre de fois. Et bien ceci peut se faire grâce aux 4 opérateurs :

 if SI une condition est remplie : execute la commande.
 while  PENDANT QUE cette condition est remplie : execute la commande.
 for  AUSSI LONGTEMPS QUE cette condition est remplie : excute la commande.
switch   CHOIX entre les commandes à exécuter en fonction de la condition.

Mais vous ne pourrez comprendre ces opérateurs que si vous comprenez comment définir une condition :

Tous les controleurs (sauf swicht) fonctionnent sur le mode vrai/faux (condition binaire). C'est réellement 1 & 0 : Il n'y a pas de "peut-être"! C'est oui ou non, vrai ou faux, bon ou mauvais, allié ou ennemi, mais rien entre les deux !!!

Mais, jetons un oeil sur les opérateurs binaires :

 

 expr == expr
 Egalité, si les 2 expressions sont égales : sortie 1, sinon 0

expr != expr
 Inégalité, si les 2 expressions sont inégales : sortie 1, sinon 0

 expr < expr
 Plus petit que, si la première expression est plus petite que la seconde : sortie 1, sinon 0

expr > expr
 Plus grand que, si la première expression est plus grande que la seconde : sortie 1, sinon 0

expr <= expr
 Plus petit ou égal, si la première expression est plus petite ou égale à la seconde : sortie 1, sinon 0

expr >= expr
  Plus grand ou égal, si la première expression est plus grande ou égale à la seconde : sortie 1, sinon 0

expr && expr
 ET logique, si les deux expressions sont vraies (1) : sortie 1, sinon 0

expr || expr
OU logique, si au moins une des deux expressions est vraie (1) : sortie 1, sinon 0

...OK! Nous sommes maintenant équipés pour manipuler les commandes conditionelles .

* Le IF

Il y a 2 types d'opérateurs IF : le IF et le IF ELSE

IF (si) l'expression entre parenthèses est vraie (de valeur 1), execute la commande entre accolades. "{" et "}".

 if (expr)
{
     commande
     ...
     commande
}

IF (si) l'expression entre parenthèses est vraie (de valeur 1), execute la commande entre les accolades suivant cette clause, ELSE (sinon) execute les commandes suivant la clause else.

if (expr)
{
     commande
     ...
     commande
}
else
{
     commande
     ...
     commande
}

 Exemple :

if (level.target_to_destroy < 1)
{
     teamwin allies
}
else
{
     iprintlnbold_noloc " Il reste des objectifs"
}


* Le WHILE

L'operateur while teste une condition, si elle est vraie (1) la commande est executée et la condition est testée à nouveau... Si elle reste vraie, la commande est une fois de plus executée... et ceci jusqu'à ce que la condition ne soit plus vraie.

 while (expr)
{
     commande
     ...
     commande
}

 Exemple :

local.time = 5
while (local.time>0)
{
iprintlnbold_noloc local.time + "secondes restantes !"
wait 1
local.time = local.time - 1
}


* Le FOR

L'opérateur FOR est très similaire à l'opérateur while, mais sa déclaration est plus complexe; rendant plus facile l'adaptation à certains cas :

 for (statement1 ; expr ; statement2)
{
     commande
     ...
     commande
}

Au début de l'execution de cette partie, le statement1 est executé. Au début de la boucle, l'expression est évaluée, et tant que cette expression est vraie, la commande entre accolades suivant la clause for est exécutée. A la fin de chaque cycle, la commande 2 (statement2) est exécutée.
Cela semble compliqué, mais avec l'exemple suivant, tout va s'éclairer. A noter que cet exemple est identique à celui utilisé pour expliquer while.

 Exemple :

for (local.time = 5; time >0; Local.time = local.time - 1)
{
     iprintlnbold_noloc local.time + "secondes restantes !"
     wait 1
}


* Le SWITCH

Cet élément de condition diffère des autres, car il n'utilise pas de valeur binaire (Il peut le faire, mais iln'est pas obligé). A la place, il converti n'importe quoi en une ligne de texte. Voici à quoi ça ressemble :

 switch (expr)
{
          label1:
                    commande
                    ...
                    commande
                    break
          label2:
                    commande
                    ...
                    commande
                    break
          case 0:
                    commande
                    ...
                    commande
                    break
          case 1:
                    commande
                    ...
                    commande
                    break
          default:
                    commande
                    ...
                    commande
                    break
}

Ce n'est pas aussi terrible que ça parait, mais il y quelques subtilités qu'il ne faut pas laisser passer. Voyons l'exemple suivant avant que je vous l'explique:

 switch (local.gameType)
{
          NIL:
                    local.gameTypeText = "ERROR : NOT INITIANIZED !"
                    break
          case 1:
                    local.gameTypeText = "free for all"
                    break
          case 2:
                    local.gameTypeText = "Team Death Match"
                    break
          case 3:
                    local.gameTypeText = "Round Base Death Match"
                    break
          case 4:
                    local.gameTypeText = "objective"
                    break
          default:
                    local.gameTypeText = "unknown or incorrect"
                    break
}

Voici comment ça marche :

En premier la valeur de l'argument est évaluée (local.gameType). Alors les cas sont recherchés un par un. Si local.gameType a la valeur 2... cas 1... non...cas 2...oui : et le programme execute la commande à partir de cet endroit.

Mais pourquoi autant de "break" ? Tout simplement car les commade sont executées à partir de l'endroit où se trouve le "case". Si on enlève les "break" et que la local.gameType a la valeur 3 ; alors le "case 3" sera activé, mais tout de suite écrasé par le "case 4" puis par le "default".

Alors, N'OUBLIEZ JAMAIS DE CLORE VOS "CASE" AVEC "BREAK".


* L'opérateur "targetname"

Vous avez probablement vu des mots commançant par $ dans les scripts.
La définition est la suivante : Le $ dans le script commande l'objet ayant ce targetname dans le jeu.

Voyons un exemple. Vous avez dans le jeu un model que vous voulez enlever, bouger... Comment pouvez vous y accéder par le script ? Tout simplement en lui donnant un nom comme ceci :
Il faut donner au couple Key/Value les valeurs suivantes : targetname/mon_objet
Après avoir fait ceci, vous pouvez contrôler votre objet à partir du script comme ceci.
$mon_objet hide ou tout autre commande. (Ici l'objet disparaitra)


* Les tableaux (arrays)

Les tableaux sont des collections de variables. Imaginez une variable et un tableau comme un ensemble de boites numérotées avec leur contenu à l'intérieur comme ceci :


Alors vous pouvez accédez à la variable aVaraible en écrivant simplement : aVariable.
Mais qu'en est-il du tableau ? Bon si vous voulez vous référez à l'array au complet, il suffit d'écrire : anArray
Mais si vous voulez voir ce qu'il y dedans voici ce que vous devez écrire.

 anarray[anIndice]

anIndice est le numéro de la "boite" dans laquelle vous voulez regarder. La première boite à l'indice 1, la seconde l'indice 2, la troisième l'indice 3 etc...

Alors :

anArray[2] à la valeur 3.7
et
anArray[3] = aVariable
a l'effet suivant :

* Créer un tableau.

- Un tableau constant

Vous le créez comme ceci :
local.n = hello::world::this::is::a::test::123

Et il peut être utilisé comme ceci :
println local.n[1] // affichera "hello"
println local.n[7] // affichera "123"
println local.n[8] // induira l'erreur script suivante : "const array index '8' out of range"

- Initialisation des données du tableau

Toutes les données sont réinitialisées à leur valeurs nulles (NIL). N'importe quelle donnée peut ensuite être modifiée.

- Le tableau targetname.

Il est crée par l'opérateur $ s'il y a plus d'une seule entité existante pour ce targetname.
Par exemple, $player est un tableau s'il y a plus d'un joueur dans le jeu.

- Tableaux multidimentionels

Peut-on avoir un tableau à plusieurs dimensions? Sans problème, ils sont appelés tableaux multidimentionels, et sont basiquement des tableaux qui
contiennent d'autres tableaux. Si un tableau standard (une dimension) peut être imaginé comme une ligne de boites, alors un tableau à 2 dimensions est
comme votre écran d'ordinateur, avec des lignes et des colonnes... Pour définir un point sur votre écran vous utilisez 2 coordonnées. Et bien ici,
c'est la mêm chose, vous devez seulement rajouter un index (indice) comme ceci :

AnotherArray[1][4] fera référence aux 4 premiers éléments du premier array.

Ajoutez une autre dimension et vous aurez des coordonnées en 3D (dans l'espace) comme ceci :

AnotherArray[1][4][2] fera référence au second élément du quatrième array du premier array.

Mais c'est assez compliqué comme ça... vous emploierez rarement ce genre de tableaux. Mais vous pouvez si vous voulez impressionner votre camarade scripter avec un baratin incompréhensible.

- Exemples de tableaux

print local.n[10] // affichera l'élément en position 10 du tableau local.n

local.n[1][3]=10 // donnera la valeur 10 à l'élément en position (1,3) du tableau local.n.

local.n = hello::word::this::is::a::test::123 // tableau constant
println local.n[1] // affichera "hello"
println local.n[7] // affichera "123"
println local.n[8] // induira l'erreur script suivante : "const array index '8' out of range"

local.n[variableOne][variableTwo][5]=23
local.a = local.n[variableOne]
local.b =local.a[variableTwo]
println local.b[5] // affichera 23

for(local.n = 1; local.n<= 10; local.n++)
{ // affichera l'élément du tableau game.stats qui se trouve à la position game.stats_name[local.n]
println game.stats_name[local.n]
}

local.a = (a::b)::c
println local.a[1][1] // affichera "a"
println local.a[1][2] // affichera "b"
println local.a[2] // affichera "c"


* Les vecteurs (Vectors)


Un Vecteur est la définition d'un point dans l'espace (ses coordonnées), comme la position d'un joueur sur une carte. Il est composé d'un tableau de 3 nombres (MOH est après tout un jeu en 3D)
On accède aux vecteurs comme à un tableau à 3 dimensions, avec les indices 0, 1 et 2.

Exemples de vecteurs

Un vecteur est créé de la façon suivante :
local.mon_vecteur = (10 -2 60.1)
Ensuite on peut accéder à ce vecteur (ou une de ses parties) comme ceci:
println local.mon_vecteur[2]
cette commande va imprimer 60.1 dans la console.

Exemple:
-------
$player.origin += (5 6 7) // va augmenter le vecteur de (5 6 7). C'est à dire 5 sur l'axe X, 6 sur l'axe Y, et 7 sur l'axe Z.

 Note: les vecteurs avec les valeurs suivantes (-1 2 3) doivent être codés de la manière suivante ( -1 2 3). Avec un espace entre la "(" et le "-".


* Les méthodes (methods)
Vous pouvez et devez séparer les parties réutilsable du code en unité distinctes.
Par exemple : si vous faites un ascenseur, le code sera plus facile à faire (et plus facile à comprendre pour les autres) si vous séparez en parties distinctes le code pour monterdu code pour descendre.

Une méthode ressemble à ceci :

ma_method:
                    for (local.indice = 5; local.indice < 20; local.indice++)
                    {
                    iprintlnbold_noloc " blablabla " + local.indice
                    wait 2
                    }
end

"ma_method:" cette ligne donne le nom de la "method". C'est grâce à ceci qu'elle pourra être activée d'un autre endroit.

Lorsqu'une "method" est appelée, elle est executée ligne par ligne jusqu'à la fin "end". Utilisez donc des "methods" pour des parties du code qui seront utilisées très souvent, ou juste pour partitonner votre code en parties plus petites ce qui est plus facile à comprendre.

Jetez un oeil sur la "thread section" pour voir comment sont activées les "method". Les "threads" et les "method" sont en relations très proches lorsque vous commencez un thread pour éxecuter une methode.

* Recevoir des informations dans une "method"

Cette partie est à cheval sur les parties "method" et "thread", aussi si quelque chose n'est pas clair je ne saurais que trop vous conseiller d'y jeter un oeil et de revenir ensuite par ici.

Exemple :

Vous voulez contrôler 2 (ou plusieurs) ascenseurs dans votre map. Vous n'allez pas dupliquer le "going_up" pour chaque ascenseur (pour 2 encore ça va, mais pour un système d'ascenseur imaginez un peu la taille et la complexité de votre code). Le fait de réutiliser le code minimise la taille du code et surtout le risque d'erreur. Aussi, il faut dire à la "method" : "Execute ce code comme d'habitude, mais fait le avec cet ascenseur et maintenant" (La prochaine fois ce sera un autre ascenseur, mais toujours le même code.)

Voici comment envoyer des paramètres à une "method" :

Au lieu de :

 going_up:
              // seulement un ascenseur
              $an_elevator moveto $an_elevator.top
              $an_elevator playsound elevator_run
              $an_elevator waitmove
end

vous allez écrire ceci :

 going_up local.an_elevator:
              // pour plusieurs ascenseurs
              local.an_elevator moveto local.an_elevator.top
              local.an_elevator playsound elevator_run
              local.an_elevator waitmove
end

De cette façon le code peut-être utilisé pour n'importe lequels des asenseurs dans la map. Vous n'êtes évidemment pas limité à un seul paramètre, ajoutez plus de variables pour avoir plus de paramètres... Si vous nêtes pas sûr du nombre de paramètres qui seront envoyés, envoyez un "array" de paramètres.


* Les "threads"

Plusieurs parties d'un script peuvent être executées en même temps. Ceci est possible grâce aux "threads".

Exemple : Vous voulez afficher toutes les minutes le nombres de minutes restantes.

première possibilité :

 for ( local.indice = 5; local.indice > 0 ; local.indice -- )
{
         iprintlnbold_noloc local.indice + " minutes restantes"
         wait 60
}

Ceci marchera si vous le mettez dans la "main method", mais si voulez "timer" autre chose (raid aérien par exemple), le code sera très complexe... Alors, on le met dans une "method" et on laisse un "thread" séparé exécuter le code. On aura donc :

 my_time_counter:
     for ( local.indice = 5; local.indice > 0 ; local.indice -- )
     {
         iprintlnbold_noloc local.indice + " minutes restantes"
         wait 60
     }

Et le code pour commencer le "thread" est :

 thread my_timer

ou

 waitthread my_timer

et il est placé dans la partie "main". (Il peut être placédans n'importe quelle "method", mais il est usuellement placé dans la partie "main".)

* Comment commencer un "thread"

Il y a 2 façons pour activer un "thread"

- Automatiquement

Les 2 scripts (ma_map.scr et ma_map_precache.scr) sont activés automatiquement s'ils existent. plus exectement, la "method" "main" du script et de tous les scripts precaches sont executés automatiquement par des "thread" automatiques. On n'a aucun comtrôle sur eux.

Aussi les scripts du dossier "anim" sont executés pour sortir les animation de comportement des personnages AI. Lequel script executé est déterminé par des éléments internes aux AI ou des scripts comme global/shoot.scr.

- Manuellement

Une commande complète a cette allure :

 <AN_OBJECT> thread <A_METHOD_LABEL>

<AN_OBJECT> défini un self cette commande optionnelle

< AN_object > place le "self.object" à < AN_object >. Cette commande est facultative, et si elle n'estpas mise le nouveau "thread" recevra le même "self.object" que le "thraed" qui l'a créé.

< A_method_label > ceci est la méthode qui doit être exécutée. Si la méthode est située dans le même dossier de manuscrit, ce sera juste le nom de la méthode, comme ceci:

 thread my_method_name

Si la méthode (bomb_thinker) est située dans un autre dossier (global/obj_dm.scr), il ressemblera à ceci:

 Thread global/obj_dm.scr::bomb_thinker

Il y a une variante à cedernier appel. Vous pouvez appeler "main method" (seulement la "main method") d'un script en nommant le fichier comme nom de "method", comme ceci :

 thread global/exploder.scr

... ceci est égal à l'écriture:

 thread global/exploder.scr::main

 

* Passer des informations dans un "thread".

Un prenant l'exemple de 'la montée de l'ascenseur", on peut passer un paramètre (ou plusieurs, mais ici un seul a été défini) dans la "method" d'un "thread" au moment de sa création comme ceci :

 thread going_up $some_boring_elevator

... maintenant la "method" "going_up" qui est éxécutée dans ce nouveau "thread" peut accéder à l'ascenseur que vous lui envoyez.

 

* Attendre la fin d'un "thread".

Si pour quelques raisons que se soit, vous ne voulez pas continuer d'executer le "thread" en cour tant que le nouveau soit terminé, remplacé le mot "thread" par "waitthread", comme ceci :

 waitthread my_method_name

De cette façon, l'execution du "thread" courant ne continuera pas tant le nouveau "thread" soit complet. Attention à ce que vous faites car tant que my_method_name ne sera terminée le thread sera bloqué.

 

* La commande exec.

Cette commande est très similaire d ethread. La différence réside dans le fait qu'il faille appeler le script avec son chemmin comme ceci :

 exec maps/obj/my_script_name.scr::my_method_name

... et elle est le plus souvent juste utilisée pour executer des script comme celà:

 exec global/DMprecache.scr

Il existe aussi une commande "waitexec", qui différe un peu de la commande "waitthread" car elle attend que tous les threads du soient terminés.

 Rappelez -vous que les groupes de threads sont un peu bizarres. Dans la plupart des cas, un nouveau thread est une partie d'un groupe séparé. Un thread appartenant à un groupe parent seulement si il est sur le même fichier et si le "self" n'est pas défini (egal au thread parent).

Exemple :

$myobject waitexec <myscript>::mythread

mythread:
thread myotherthreada
self thread myotherthreadb
end

myotherthreada:
wait 5
end

myotherthreadb:
wait 10
end

Dans cet exemple, "myotherthreada" appartient au même groupe de thead que "mythread" , mais "myotherthreadb" est son propre groupe de "thread" (car il a "self" devant l'appel). De cette façon, le "waitexec" attendra seulement que "myotherthreada" soit terminé.


* Scripts démarrant automatiquement


Ces scripts sont démarrés automatiquement lorsqu'une carte est chargée dans le jeu:


* maps/MA_CARTE.scr ( doît être dans le dossier maps/dm ou maps/obj)


Un script de niveau (level script) est associé à une carte, et est automatiquement chargé et démarré par le moteur du jeu lorsque celui-ci démarre cette carte (et pas pour un démarrage consécutif à une partie sauvegardée) .
Ce script est utilisé pour " triggerer " (sorry pas de traduction pour ce mot qui se rapporte purement et simplement aux triggers utilisés dans Radiant) tous les objets dynamiques contenu dans la carte, comme les portes, les élévateurs, les AI, etc…
Un script " maps/MA_CARTE.scr " correspond au bsp " maps/MA_CARTE.bsp ".
Ces scripts sont optionnels (pas nécessaires).


* maps/MA_CARTE_precache.scr ( doît être dans le dossier maps/dm ou maps/obj)
Un script "precache" de niveau peut-être associé à chaque carte, et est automatiquement chargé et démarré lorsque une carte démarre (même pour une partie sauvegardée). Ce script est utilisé pour mettre en cache préalablement (precaching) les ressources spécifiques d'une carte.
Un script " maps/MA_CARTE_precache.scr " correspond au bsp " maps/MA_CARTE.bsp ". Ces scripts sont optionnels (pas nécessaires).

 

* Les scripts qui sont dans le repertoire "anim"
…sont exécutés pour mener à bonne fin l'animation des personnages AI (Intelligence Artificielle) contenus dans une carte. Lequel script sera exécuté, est déterminé par l'état interne des AI ou des scripts comme l'est le global/shoot.scr.


* Les objets prédéfinis (Predefined objects)


Ces objets existent tout le temps, et peuvent vous être d'une grande aide lorsque vous scriptez.( Voir le document g_allclasses.html dans le dossier docs de MOHRadiant pour une définition complète de ces objets):


game
Il fait reference à l'unique objet game qui maintient son état entre et pendant chaque niveau, chaque carte, et donc tout le temps.
Seules les variables primitives (integers/floats/strings/vectors) peuvent persister (continuer d'exister) entre les niveaux.


level
Il fait reference à l'unique objet level qui maintient son état pendant toute la durée d'un niveau.


local
Fait référence au tread (partie de script) qui est exécuté pendant la commande en cours.


parm
Fait référence à l'unique objet parm qui est utilisé pour passer des paramètres aux nouveaux treads.
Notez que toute utilisation de cette variable peut-être " mieux " codée en utilisant les paramètres lors de la création des nouveaux treads.


self
Fait référence à l'objet pour lequel le tread est exécuté. Cet objet est le même pour tous les treads faisant partie d'un groupe de treads.


group
Fait référence à l'objet représentant le groupe de treads auquel le tread exécutant la commande en cours appartient. (dur dur hein ! Meuh non, faut juste le relire 3 ou 4 fois et puis ça fait TILT ;-) )


* L'objet "self" (self object)


Il existe unobjet spécial appelé "self".

Le "self object" a sa valeur à la création du group de "trhead". La suite est quelques situations :

* Script commençant automatiquement.

"Self" est sans valeur pour les "level script". Self est le personnage pour les scripts d'animation.

* Commande : thread label.

Puisque le nouveau "thread" a le même groupe que le "thread" original, le "self" dans le nouveau thread est égal à l'individu dans le "thread" original.

 

* Commande : object thread label.

"Self" dans le nouveau "thread" est la valeur d'objet.

 

* Thread événement (Event thread)

Si un "thread" est lancé en réponse à un événement d'objet, alors "self" est égal à cet objet.


* Priorité des opérateurs.

Tous les opérateurs sont executés dans un ordre spécial, certains ayant priorité sur d'autres.

Les opérateurs sont listés dans l'ordre de priorité, du plus plus fort au plus faible.

 * / %
 + -
 < > <= =>
 == !=
 &
 ^
 |
 &&
 ||

Ces règles de priorités gouvernent la façon dont sont évalué les "statements".

Exemple :

5 + 3 * 2 + 11

On compte d'abord 3 * 2 car * est prioritaire sur +. Ensuite le résulat (6) est additionné à 5.

Comment faire pour avoir 16 ? Et bien tout simplement en utilisant des parenthèses.

(5 + 3) * 2 = 16

(ça doit vous rappeler vos anciens cours de math tout ça !!!)


* Conversion de type (Casting)


Vous pouvez convertir les types des variables que vous utilisez dans les scripts. Et ceci, par votre ordre direct ou automatiquement (et parfois aussi lorsque vous ne vous y attendez pas).

* Conversion automatique (Automatic casting)
Si dans une expression, un paramètre doit obligatoirement être d'un certain type, une conversion automatique va avoir lieu.

* Conversion Manuelle (Manual casting)
En codant les mots-clés suivants en face d'une valeur, une conversion va avoir lieu :
int
float
string

Vous pouvez faire toutes les conversions que vous voulez entre des valeurs, mais avec precaution.
Exemples de conversions manuelles
println ( int 3.5 ) va imprimer 3 dans la console ( le reste est tronqué à cause de la conversion ).
println ( float "2" ) va imprimer 2.000 ( un float a une précision de 3 décimales).
println ( string 3.5 ) va imprimer 3.500 ( souvenez-vous que 3.5 est un float et a donc une précision de 3 décimales... donc le résultat ne sera pas 3.5 ).
println ( float "nimportekwa" ) va imprimer 0.000 ( la conversion vers un nombre va foirer, et donnera la valeur 0... ce qui sera converti en une valeur float, donc 0.000 ).


* Strings (chaînes de caractères)

Les chaînes de caractères (Strings). La chaîne de caractères est un type de variable qui diffère des autres types en de nombreux points. On peut en fait le considérer comme un tableau de caractères (lettres et chiffres donc).

Les caractères de la chaîne peuvent être accédées par l'opérateur de tableau "[]". L'indice du tableau commence à 0 (ET NON PAS A 1!!!)
Par exemple, "abc"[2] accède à la valeur "c".


* Références

Voici une liste de références matériel que vous trouverez très utile après avoir appris les rudiments du scripting.

 liens

 Description
 g_allclasses.html  Explique quelles commandes peuvent être appelées pour les objets communs de jeu, comme: Acteur, animation, entité, ScriptSlave, ScriptThread, Sentient, Trigger et world. Vous le trouverez avec le MOHRadiant original dans le répertoire Doc.
 MOH_Gamesclasses.html  Le même type de dossier que le g_allclasses.html, comme il est contenu dans l'expansion Speardhead, il contient les objets de jeu pour SH.
 Script Files.txt  Un document semblable à celui-ci. Contient moins d'exemples et est plus difficile à comprendre (voila pourquoi j'ai écrit ce document). Quelques concepts sont omis entièrement, mais il m'a enseigné le scripting de base. Vous le trouverez avec le MOHRadiant original dans le répertoire Doc.


* Annexes.

Voici les annexes qui vont avec ce document.

Annexe A (Commandes) : Ce document explique avec plus de détails les commandes usuelles décrites brièvement dans le document  g_allclasses.html.

Annexe B (Les classes) : Ce document explique ce que sont les classes, comment les utiliser, et ce qu'elles font.

Annexe C (architecture de script) : Ce document est une explication de l'architecture d'un script.