- Weuhzor
- Accueil
- Warcraft III
- Menus css
- Javascript

Le problème dans l'éditeur de war3 c'est qu'il est impossible de contrôler aisément l'ordre des déclencheurs, pour l'écriture du script de la map lors de la sauvegarde. Cela devient un réel problème lorsque l'on doit récupérer du code créé par un autre mappeur, tout se place dans le script personnalisé de la map le plus souvent, ce qui n'est pas pratique du tout, ça crée un beau bordel au final.
Les librairies vous permettront donc de ranger votre code dans n'importe quel ordre !, sachant qu'au moment de la sauvegarde de votre map tout sera mis dans le bon ordre.
Syntaxe
library <nom_de_la_librairie> // Votre code habituel endlibrary
Exemple
library A function FunctionA takes nothing returns nothing endfunction endlibrary library B function FunctionB takes nothing returns nothing endfunction endlibrary
Avec cet exemple vous pouvez être certain que les fonctions FunctionA et FunctionB seront au début du script de votre map. Donc toutes les autres fonctions pourront faire appel à ces 2 fonctions. Dans l'exemple les 2 fonctions garderont le même ordre, un élément du langage permet d'influer sur cet ordre.
Avec l'attribut needs il est possible d'ordonner soi-même les fonctions dans le script qui est généré à la sauvegarde. Pour cela on utilise un système de dépendance, une librairie a besoin des fonctions d'une ou plusieurs autres librairies pour fonctionner.
Dans l'éditeur on écrira:
library A needs B function FunctionA takes nothing returns nothing call FunctionB() endfunction endlibrary library B function FunctionB takes nothing returns nothing endfunction endlibrary
Et à après la sauvegarde on obtiendra :
function FunctionB takes nothing returns nothing endfunction function FunctionA takes nothing returns nothing call FunctionB() endfunction
Sans l'attribut needs cela n'aurait pu fonctionner, car la fonction FunctionA a besoin de la fonction FunctionB.
Pour faire dépendre une librairie de plusieurs autres librairies il suffit de séparer les requis par une virgule.
library A needs B, C function FunctionA takes nothing returns nothing call FunctionB() call FunctionC() endfunction endlibrary library B function FunctionB takes nothing returns nothing endfunction endlibrary library C function FunctionC takes nothing returns nothing endfunction endlibrary
Après la sauvegarde:
function FunctionB takes nothing returns nothing endfunction function FunctionC takes nothing returns nothing endfunction function FunctionA takes nothing returns nothing call FunctionB() call FunctionC() endfunctionN'oubliez pas que:
Il est possible d'ajouter un contrôle d'accès sur les variables globales, les fonctions, les structure et plus encore (cela sera expliqué au fur et à mesure), on appelle ça l'encapsulation des données.
Ce contrôle permet de limiter l'accès à certaines données afin d'avoir un code facilement portable. Il existe 2 types d'accès pour le vJass: public et privé. Ces accès se limitent aux blocs qui les contiennent (librairie, structure, scope).
L'accès public ce définit par le mot clé "public" devant la donnée à protéger. Un accès public ne sert pas à grand chose, car par défaut tous les accès sont public.
library A globals public real x = 0.0 endglobals public function FuncA takes nothing returns nothing set x = 18.0 // Autorisé endfunction endlibrary function FuncB takes nothing returns nothing set x = 24.0 // Autorisé call FuncA() // Autorisé endfunction
Dans cet exemple rien ne change, c'est tout à fait classique. Cela montre que l'on a le droit d'accéder à la variable x en dehors de la librairie A
L'accès privé se définit par le mot clé "private" devant la donnée à protéger, les éléments privés sont uniquement accessibles par les blocs qui les contiennent.
Cela permet également d'avoir un même nom de variable globale dans plusieurs bloc, dans différentes librairies par exemple.
library A globals public real x = 0.0 private real y= 0.0 endglobals public function FuncA takes nothing returns nothing set x = 18.0 // Autorisé set y = 12.5 // Autorisé endfunction private function FuncC takes nothing returns nothing set x = 34.0 // Autorisé set y = 84.5 // Autorisé endfunction endlibrary function FuncB takes nothing returns nothing set x = 24.0 // Autorisé set y = 106.047 // Interdit call FuncA() // Autorisé call FuncC() // Interdit endfunction
Explication: La fonction FuncC et la variable globale y ne peuvent être utilisées QUE dans la librairie A, en dehors de ce bloc, une erreur surviendra à la sauvegarde.
Il est possible de considérer n'importe quel élément comme un objet: un stylo, un ordinateur, une personne, une arme, un sort. Et également des concepts plus abstrait tel qu'un point (location pour l'éditeur).
Les structures permettront de créer soi-même ses propres types.
Syntaxe
struct <type_structure> // corps de la structure endstruct
Le corps de la structure peut se diviser en 2 parties: la partie déclarative et la partie contenant les fonctions propres à la structure que l'on nommera des méthodes.
Exemple
struct point real x real y = 0.0 // L'assignation d'une valeur dans la partie déclarative est facultative, cela correspond à une valeur par défaut. endstruct
Après sa déclaration une structure peut être utilisée comme n'importe quel autre type de variable. Elles doivent être créées puis détruites car il ne peut exister plus de 8190 instances d'une même structure. Cela laisse une grande marge de main d'oeuvre mais il faut être prudent dans le cas d'une structure utilisée souvent, s'il existe de trop nombreuses instances inutilisées et non détruites cela peut avoir un impact sur une partie de plusieurs heures. Cependant ne vous inquiétez pas, personnellement il ne m'est jamais arrivé de dépasser cette limite, faut vraiment le vouloir ^^.
La création se fera à l'aide du mot-clé "create()" précédé du type de la structure de cette manière: <type_structure>.create()
La destruction se fera avec le mot-clé "destroy(<instance_de_struct>)" précédé du type de la structure de cette manière: <type_structure>.destroy(<instance_de_struct>) ou alors l'instance de la structure suivi du mot clé "destroy()" <instance_de_struct>.destroy()
function testPoint takes nothing returns nothing local point p = point.create() set p.x = 18.0 set p.y = 34.0 call BJDebugMsg( "x:"+R2S(p.x)+" ; y:"+R2S(p.y) ) // Affichera "x:18.0 ; y:34.0" call point.destroy(p) ou call p.destroy() // Libère une instance endfunction
Autre exemple :
function afficherPoint takes point p returns nothing call BJDebugMsg( "x:"+R2S(p.x)+" ; y:"+R2S(p.y) ) endfunction function creerPoint takes real x, real y returns point local point p = p.create() set p.x = x set p.y = y return p endfunction function testPoint2 takes nothing returns nothing local point p = creerPoint( 10.0, -354.14) call afficherPoint(p) // Affichera "x:10.0 ; y:-354.14" call p.destroy() endfunction
Note pour les puristes: il est inutile de nullifier les instances de structure car en réalité ce sont des entiers.
Une structure peut elle même être composée de structure(s), pour une instance de structure, la valeur 0 signifie que la structure n'a pas encore été créée. Une instance de structure qui n'est pas créée est inutilisable.
struct pairPoint point p1 = 0 // il est interdit d'utiliser point.create() pour la valeur par défaut. point p2 = 0 endstruct function testPair takes nothing returns nothing local pairPoint pP = pairPoint.create() set pP.p1 = point.create() set pP.p1.x = 34.18 set pP.p1.y = -23.56 set pP.p2 = creerPoint( 91.35, 59.1 ) call afficherPoint( pP.p1) // Affichera "x:34.18; y:-23.56" call afficherPoint( pP.p2) // Affichera "x: 91.35; y:59.1 " call pP.p1.destroy() call pP.p2.destroy() call pP.destroy() endfunction
Il est également possible d'avoir des globales avec un type de structure.
globals point point_global = 0 // Autorisé point point_x = point.create() // Interdit endglobals
Les méthodes sont des fonctions directement intégrés à la structure, la syntaxe est la même que pour les fonctions sauf que l'on remplace "function" par "method" et "endfunction" par "endmethod". Les méthodes doivent être déclarées dans le corps de la structure, comme pour les attributs.
Nous allons reprendre notre structure de point et l'éteindre.
struct point real x = 0.0 real y = 0.0 method afficherPoint takes nothing returns nothing call BJDebugMsg( "x:"+R2S(this.x)+" ; y:"+R2S(this.y) ) endmethod method placerPoint takes real x, real y returns nothing set this.x = x set this.y = y endmethod endstruct function testPointMethod takes nothing returns nothing local point p = point.create() call p.placerPoint( 24.001, 59.02) call p.afficherPoint() // Vous savez ce que ça va afficher maintenant ! call p.destroy() endfunction
Pour l'appel des méthodes le principe est le même que pour accéder aux attributs de la structure: <instance>.<méthode>(<paramètres_de_la_méthode>)
Vous l'aurez compris (ou pas) le mot-clé "this" permet de pointer sur l'instance elle-même. Ce mot-clé ne peut être employé qu'à l'intérieur des méthodes de la structures.
"this" est optionnel: Vous pouvez utiliser un simple '.' à la place de celui-ci. (exemple: set .x = 18.0). Cependant ça marche 1 fois sur 2 donc à utiliser avec modération, ou pas du tout.
Les structures bénéficient de l'encapsulation des données (accès privé et accès public)
Les attributs de la structures et les méthodes peuvent bénéficier d'un droit d'accès. Ainsi un attribut privé sera modifiable uniquement au sein des méthodes de la structure, tout comme une méthode privée qui ne pourra être exécuté qu'au sein des méthodes de la structure. Les éléments public seront accessible de n'importe où à l'intérieur et à l'extérieur de la structure.
struct point private real x = 0.0 private real y = 0.0 private boolean init = false // le point a-t-il été initialisé ? method afficherPoint takes nothing returns nothing call BJDebugMsg( "x:"+R2S(this.x)+" ; y:"+R2S(this.y) ) endmethod method placerPoint takes real x, real y returns nothing set this.x = x set this.y = y if this.init == false then set this.init = true endif endmethod method IsInit takes nothing returns boolean return this.init endmethod endstruct
Dans cet exemple un troisième attribut de type booléen a été ajouté: "init". Cette variable indique si le point a été initialisé ou si les valeurs x et y n'ont pas été modifiés, la variable est privé car l'utilisateur (développeur) ne dois pas avoir le droit de modifier cette valeur, cependant il bénéficie d'un droit de lecture sur la valeur grâce à la méthode "IsInit" qui renvoie la valeur de l'attribut "init". Les attributs 'x' et 'y' sont également privés, elles ne peuvent être lus, ni modifiés, cependant la méthode placerPoint permet de modifier leurs valeurs.
Pour expliquer simplement l'héritage prenez par exemple l'être humain. A partir de cet être humain nous pouvons avoir deux extensions possible: l'homme et la femme. Les deux extensions ont beaucoup d'attributs en commun (relatif à l'être humain) et réalisent les mêmes actions: boire, manger, réfléchir, pisser xD, mais ne le font pas de la même manière. Les actions possibles sont regroupées au sein de l'être humain, l'homme et la femme apporteront la définition de ces actions.
Mais à quoi cela peut-il bien servir ?
Vous intégrez dans votre map deux types de soldat, les êtres humains et les morts vivants. Vous souhaitez que les deux soient considérés comme des unités et qu'ils aient des fonctions communes, mais qui réagissent différemment selon que l'unité soit un mort vivant ou un être humain. Les deux régénèrent leur points de vie, mais pas de la manière, les humains en permanence et les mort-vivants uniquement sur de la flétrissure.
L'interface forme la base de votre type (le soldat), c'est dans l'interface que nous allons déclarer les attributs communs et les actions communes mais sans les définir !, leur définition seront dans les structures qui héritent de l'interface (structure être humain et structure vampire).
Syntaxe au travers de l'exemple
interface soldat real regen = 0.0 unit u = null method Init takes player p, real x, real y returns nothing // Initialisera la structure method RegenPv takes nothing returns nothing // On ne déclare que l'en-tête de la méthode endinterface struct humain extends soldat method Init takes player p, real x, real y returns nothing call BJDebugMsg( "Je suis un humain") set this.u = CreateUnit( p, 'hfoo', x, y, 0.0) // Création d'un footman set this.regen = 1.0 endmethod method RegenPv takes nothing returns nothing call SetUnitState( this.u, UNIT_STATE_LIFE, GetUnitState(this.u, UNIT_STATE_LIFE) + this.regen) endmethod endstruct struct mort_vivant extends soldat method Init takes player p, real x, real y returns nothing call BJDebugMsg( "Je suis un mort-vivant") set this.u = CreateUnit( p, 'ugho', x, y, 0.0) // Création d'une ghoule set this.regen = 2.5 endmethod method RegenPv takes nothing returns nothing if IsPointBlighted(GetUnitX(this.u), GetUnitY(this.u)) then // La régénération ne se fait que sur terre flétrit, mais elle est plus importante call SetUnitState( this.u, UNIT_STATE_LIFE, GetUnitState(this.u, UNIT_STATE_LIFE) + this.regen * 2) endif endmethod endstruct
Nos structures sont déclarées nous allons pouvoir les utiliser.
Exemple d'utilisation
function test takes nothing returns nothing local soldat array soldats local integer a = 0 set soldats[0] = humain.create() set soldats[1] = humain.create() set soldats[2] = mort_vivant.create() set soldats[3] = humain.create() set soldats[4] = mort_vivant.create() loop exitwhen a == 5 call soldats[a].Init() call soldats[a].RegenPv() set a = a + 1 endloop endfunction
A la sortie nous obtiendrons 3 fantassins et 2 ghoules avec en affichage:
Je suis un humain
Je suis un humain
Je suis un mort-vivant
Je suis un humain
Je suis un mort-vivant
Vous pouvez vous amuser à rajouter un type Elfe qui ne régénère que la nuit.