Index du forum Vous n'êtes pas connecté Se connecter (désactivé) Inscription (désactivé)

Tuto SCAR

Ici seront abordés toutes les facettes de la programmation et du scriptage: avec ceci, vous pourrez fare n'importe quoi avec Dawn of War II! (sauf le café ^^)
Je vous demande d'abord de lire "la première partie du tuto RBF" avant de continuer. Si vous débutez en programmation, je vous conseille de suivre l'ordre du cours, sinon vous pouvez vous référez directement aux parties suivantes:

LUA, SCAR, ça se mange ?

Qu'est-ce que la programmation ?

L'esprit du scriptage

Nous allons ici parler un peu de la programmation et du scriptage, sans nous plonger dans les joies du codage.
Qu'est ce que la programmation ? Créer des logiciels et applications, tels que votre antivirus ou votre système d'exploitation, ainsi que des jeux.
Et le scriptage ? Déterminer une séquence d'execution d'ordres en fonction de divers paramètres. On pourrait assimiler cela à effets/déclencheurs - cause/effet. Le scriptage est une partie de la programmation.
Il faut bien faire la différence entre programmation et scriptage. Ici, ce que nous allons faire, c'est du scriptage, la programmation du jeu, c'est Relic qui l'a fait. En gros nous allons exploiter ce que Relic a fait pour en tirer le maximum (ce jeu a un potentiel ignoré, je vous le dis tout de suite).
Cela va aussi développer votre esprit d'analyse: on pense souvent que les informaticiens passent leur temps à écrire des trucs qu'ils connaissent par coeur (certes, il y aura quelques petits trucs à retenir mais pas plus que ça), or ceci est faux. La majorité du temps consacré à un script est dans la reflexion pour savoir comment décomposer son problème en structures de base simples (et cela peut se faire sans ordi !).

Retour en haut Retour en haut Retour en haut

Les languages

Pour communiquer avec un ordinateur, on doit utiliser un language spécial (en effet dites-lui "lance Dawn of War 2" en Français/Anglais/Espagnol/Allemand/Chinois/Russe/Geek je crois qu'il ne se passera rien ^^ ).
On va donc utiliser un idiome très spécial... Pour ne citer que les plus connus (la liste complète serait TRES longue):
Sites web:
- HTML
- CSS
- PHP
- MySQL
- Javascript

Applications:
- C/C++/C# (certainement le plus utilisé: quasiment toutes vos applications sont programmées en C, même Windows...)
- Python
- Ruby
- LUA
- Basic (dont le TI-Basic est un dérivé pour les connaisseurs ;) )

Rassurez-vous, votre ordinateur connait de base énormement de languages, au pire il suffit d'installer un programme pour qu'il sache le parler.
Mais une question se pose: lequel choisir ?
Relic a choisi le LUA, car il est plus rapide à exécuter, donc parfait pour les jeux, de plus votre ordinateur le connaît déjà (sinon comment Dawn of War 2 marcherait ?).
Nous allons donc apprendre ensemble le LUA ;)
Le SCAR, c'est une bibliothèque pour le LUA, mis inutile de vous affoler pour ce vocabulaire on verra ca plus tard.
Pour ceux qui connaissent les languges cités ci-dessus, je vous invite à lire la fiche de syntaxe (en cours de rédaction) qui résume tout sur le LUA.
Eh oui, j'apprend aux novices que si l'on connait un language, on peut TRES rapidement apprendre à programmer dans un autre language ! (pour la plupart, les différences sont très minimes...).

Retour en haut Retour en haut Retour en haut

Les outils

Même si votre ordinateur sait lire le LUA, il lui faut quelque chose pour savoir quoi faire...
Normalement, bloc-notes (eh oui !) ou un éditeur comme Notepad++ suffit. Seulement, vous ne pourrez pas tester vos script.
Je vous recommande donc "Lua For Windows", un programme complet incluant tout, même un debuggeur (vous verrez que vous en aurez besoin !). De plus, il colore votre code pour mieux s'y reperer.
Je considère dès à présent que vous travaillez avec ce logiciel. Pour l'installation, je vous fais confiance, il y a juste à suivre les instructions.
Un petit apercu de ce que cela donne:

(Ne vous inquietez pas, vous saurez comprendre tout ça et vous pourrez frimer avec !!!)
Pour cette première partie, nous n'allons pas utiliser Dawn of War 2, et vous ne verrez pas forcement le rapport avec ce que l'on fera. Je vous demande de me faire confiance car cette partie est ESSENTIELLE...
La première partie finie, vous vous direz: "et alors ?", mais dès le début de la deuxième vous comprendrez immédiatement le rapport avec le jeu.
Confiance, donc. Si on s'y mettait ?

Retour en haut Retour en haut Retour en haut

Création de votre premier script: "Hello World !"

Dans cette partie, nous allons faire un truc tout bête: afficher du texte à l'écran.
Ce sera l'occasion pour célébrer la création de votre premier script...

On crée un fichier...

Tout d'abord, il faut commencer par lancer le logiciel. Le seul problème, c'est que le pack inclus 5-6 logiciels... mais un seul les réuni tous. Lancez-donc "SciTE", c'est celui-là. Les autres, on ne les utilisera pas.
Je vous conseille dès maintenant de créer un dossier ou vous mettrez vos scripts en cours...
Quand vous lancer le logiciel, vous vous retrouvez devant une page vide.
On va commencer par créer un nouveau script. Faites "File > New" ou cliquez sur .
Puis on va l'enregistrer: faites "File > Save As" ou cliquez sur , je vous conseille de l'appeler "test.lua" (vous êtes obligés de mettre ".lua" à la fin si vous voulez que ca marche).
Nous pouvons commencer à scripter !

Retour en haut Retour en haut Retour en haut

...on teste...

Je vous demande maintenant d'inscrire ce code (je ne vous demande pas de le comprendre immédiatement):

print("Hello World !")

ATTENTION ! Le code est sensible à la casse ! Cela signifie que vous devez respecter les majuscules/minuscules, sinon il y aura des bugs !
Maintenant, il faut afficher la zone ou tout va se passer... cliquez sur !
On appelle ce cadre noir (ou blanc, ca dépend) la "console".
Il ne reste plus qu'à lancer ! Cliquez sur !
Vous obtenez normalement ceci (les couleurs peuvent varier):

>lua -e "io.stdout:setvbuf 'no'" "test.lua"
Hello World !
>Exit code: 0

Hourra !

Retour en haut Retour en haut Retour en haut

... et on explique !!!

Je vais vous expliqer les ligne une à une...
">lua -e "io.stdout:setvbuf 'no'" "test.lua" ": c'est le témoin qui indique que le script "test.lua" est lancé.
"Hello World !": ce qu'on voulait afficher !
">Exit code: 0": témoin qui indique que le code est terminé et s'est déroulé correctement.

Les petits malins qui auront tripoté ce bout de code auront remarqué que si on change ce qu'il y a entre les guillemets le texte affiché sera différent... et aussi que si on mettait deux fois ce code on affichait deux trucs...
Exemple:

print("Hello World !")
print("Bla")

>lua -e "io.stdout:setvbuf 'no'" "test.lua"
Hello World !
Bla
>Exit code: 0


"print" veut dire en Anglais: "imprimer" ou "afficher"... ça marche ? Vous avez fait votre premier script ! Vous êtes prêt ? On passe à la suite !

Retour en haut Retour en haut Retour en haut

Les commentaires

On va ici apprendre un truc très utile: les commentaires. Ce sont des notes apposées en fin de ligne, mais qui comptent "pour du beurre": votre ordinateur ne les lit pas.
Deux grandes raisons pour utiliser les commentaires:
- Cela vous permet de vous retrouver dans votre code quand il commence à faires plusieures centaines de lignes (ca arrive vite !);
- Si un ami veut lire votre script pour vous aider, ces commentaires l'aideront à comprendre votre façon de programmer.
Un script qui n'a pas de commentairs est un mauvais script.
Pour faire un commentaire c'est très simple:

-- ceci est un commentaire
print("Hello World !") -- ceci est un commentaire en fin de ligne
--[[ Ceci est
un commentaire
sur plusieurs
lignes ]]--

Vous avez vu ! Votre code change de couleur pour les commentaires !

Mais c'est comme toutes les bonnes choses: il ne faut pas en abuser. Cela ne sert à rien de commenter toutes les lignes.

Retour en haut Retour en haut Retour en haut

Les variables

Attention ! Chapitre TRÈS important !

Les nombres

Les variables sont l'une des choses les plus importantes dans tout programme, on ne peut pas faire sans.

Exemple concret (pour un jeu vidéo): on veut pouvoir savoir à tout moment le nombre de vies d'un joueur et tout d'abord regler le nombre de vies de départ... Comment fait-on ?
Tout simplement:

nombre_de_vies = 5

Hop, on s'empresse de décortiquer se code:

- "nombre_de_vies", c'est le nom de la variable. Le nom d'une variable ne doit pas contenir d'espace, et DOIT commencer par une lettre (pas de caractère spécial).
- "=": c'est le symbole qui permet de dire: "je veux que cette variable prenne cette valeur".
- La valeur que prend la variable. Ici c'est un nombre entier (c'est à dire qui n'a pas de chiffres après la virgule), mais on aurait très bien pu mettre 2,45 , pi, racine de deux... Tout les nombres que vous voulez en fait.

Dans le concret, le programme va demander à l'ordinateur si il peut stocker ce nombre dans une case de la mémoire vive (RAM), normalement il recoit l'autorisation, et ensuite imaginez votre petit 5 rangé dans un tiroir avec dessus une étiquette "nombre_de_vies".
C'est le principe des variables.

Vous remarquez que si vous executez ce code, rien ne sera affiché: c'est normal, on a juste mis "5" dans un tiroir et puis on s'en est allé...

Mais c'est affreux ! Ca prend de la place dans mon ordi ! Moi qui n'ai plus de place sur mon disque dur...

Rassurez vous ! Vous stockez ce nombre non pas dans votre disque dur, mais dans la mémoire vive, qui est spécialemet dédiée au stockage d'information à courte durée. Cette mémoire s'efface dès que vous redémarrez votre ordi. Et puis, il faut vraiment le faire exprès pour surcharger votre ordinateur avec des variables (le nombre que celui-ci peut stocker ne se compte pas...).

Au fait petite précision: une variable ne peut pas s'appeller "if", "else", "then", "do", "for", "until", "repeat", "while", "local", "end" et "function". À part ca, tout est permis !

Retour en haut Retour en haut Retour en haut

Afficher une variable

Maintenant, on veut connaitre notre nombre de vies. Pour cela il suffit juste d'écrire:

nombre_de_vies = 5
print(nombre_de_vies)

>lua -e "io.stdout:setvbuf 'no'" "test.lua"
5
>Exit code: 0

Et voilà ! On a affiché le nombre de vies ! (5). Pour ceux qui on bidouillé, si vous oubliez la ligne "nombre_de_vies", il s'affichera un joli "nil"...

Cela veut dire que la variable n'existe pas, elle n'a pas été trouvée, car vous ne l'avez pas créé ! En effet on considère qu'a la fin d'un programme, toutes les variables sont détruites (je vous avais bien dit que c'était du stockage à courte durée !).

Vous pouvez aussi observer que c'est le même "print" que tout à l'heure... Cette fois-ci pour afficher un nombre. Et il n'y a pas de guillemets ! Si vous en mettez, vous obtiendrez ceci:

>lua -e "io.stdout:setvbuf 'no'" "test.lua"
nombre_de_vies
>Exit code: 0

Explication au prochain chapitre...

Retour en haut Retour en haut Retour en haut

Modifier une variable

Et maintenant j'aimerais faire varier le nombre de vies...
Il y a deux méthodes: soit on redéfini la variable comme si elle n'existait pas, soit on l'utilise avec une relation.
Exemple:

nombre_de_vies = 5
print(nombre_de_vies)
print("Ban ! Un coup de pistolet bolter !")
nombre_de_vies = 4 -- première méthode
print(nombre_de_vies)
print("Un pack de soin !")
nombre_de_vies = nombre_de_vies + 2 -- deuxième méthode
print(nombre_de_vies)

>lua -e "io.stdout:setvbuf 'no'" "test.lua"
5
Ban ! Un coup de pistolet bolter !
4
Un pack de soin !
6
>Exit code: 0

Les opérateurs sont les suivants:
- "+": addition;
- "-": soustraction;
- "*": multiplication;
- "/": division;
- "%": modulo (division avec reste).

Retour en haut Retour en haut Retour en haut

Les chaines

Syntaxe

Nous allons ici voir un nouveau type de variable: les chaines de caractères, ou string en anglais.
Comme leur nom l'indique, ce sont des assemblages de caractères... un exemple vaut mieux qu'un long discours:

texte = "Hello World !" -- l'attribution se fait comme pour les nombres<br />
print(texte)

>lua -e "io.stdout:setvbuf 'no'" "test.lua"
Hello World !
>Exit code: 0


Maintenant vous comprenez: le "Hello World !" du début était en fait une chaine !!!
Il faut donc entourer notre texte de guillemets... mais cela marche aussi si on fait:

texte = "Hello World !" -- équivaut à:
texte2 = 'Hello World !' -- équivaut à:
texte3 = [[Hello World !]] -- Attention: le site colore très mal ces chaines, mais LUA for windows le fait très bien.
print(texte)
print(texte2)
print(texte3)

>lua -e "io.stdout:setvbuf 'no'" "test.lua"
Hello World !
Hello World !
Hello World !
>Exit code: 0


Pourquoi autant de facons de définir une chaine me direz vous ? D'après vous, que va-t-il se passer si l'on fait ca:

texte = "Je met des \"guillemets\" à l'interieur des guillemets"
texte2 = 'Oh l\'apostrophe en plein milieu de la chaine !'
print(texte)
print(texte2)


A coup sur, le script plante, car l'ordinateur s'arrête à la deuxième apostrophe/guillemets, il consigère ce qui suit comme anormal donc il plante. D'ailleurs, le logiciel colore automatiquement le texte d'une facon bizarre, il est donc facile dee trouver les erreurs.
Il y a deux moyens d'éviter cela: soit on met le texte entre guillemets si il y a une apostrophe, et inversement comme ceci:

texte = 'Je met des "guillemets" dans une la chaine avec apostrophes, pas de bug'
texte2 = "Oh l'apostrophe en plein milieu de la chaine avec guillemets, pas de bug !"
texte3 = [[Le texte entre crochets peut
être écrit sur plusieurs
lignes et permet de mettre des " et '
sans problèmes]]


L'autre moyen est d'avertir l'ordinateur que le sybole qui suit n'est pas la fin de la chaine: on appelle ca l'échappement du symbole, on le fait en mettant un antislash "\" devant.

texte = 'Les apostrophes sont échappées à l\'interieur de la chaine avec apostrophes, pas de bug'
texte2 = "Les \"guillemets\" sont échappés, pas de bug !"

Retour en haut Retour en haut Retour en haut

La concaténaquoi ?

Derrière le terme barbare de la concaténation se trouve une action très simple: c'est comme si l'on "additionnait" deux chaines. Exemple:

texte = "Je suis un".." bisounours"
texte2 = 'du monde' -- on peut mettre une variable directement comme ci-après:
texte3 = 'Les bisounours '.."ne sont pas"..[[les plus beaux]]..texte2 -- combiner les différents types de chaines marche
print(texte)
print(texte3)

>lua -e "io.stdout:setvbuf 'no'" "test.lua"
Je suis un bisounours
Les bisounours ne sont pas les plus beaux du monde
>Exit code: 0


C'est simple non ? En fait LUA considère chaque mot qui n'est pas une entre guillemets/apostrophes/crochets/commentaire comme une variables: l'exemple au-dessus avec la concaténation indirecte de texte2. Notez aussi qu'une variable nombre sera automatiquement transformée en variable chaine:

age = 7
texte = "J'ai"..age.."ans"
print(texte)

>lua -e "io.stdout:setvbuf 'no'" "test.lua"
J'ai 7 ans
>Exit code: 0


Retour en haut Retour en haut Retour en haut

Les fonctions

ATTENTION: chapitre essentiel !

Principe

Vous en avez peut-être assez de refaire dix fois la même suite d'actions ? Ca vous embête ? Vous voulez créer un outil puissant de calcul ? Ce chapitre est pour vous !
Nous allons créer des fonctions: si vous êtes en 3ème ou plus, vous savez déjà ce que c'est.
En math, on écrit par exemple: "f(x) = 3x + 5" (je prend un truc tout bête pour faire simple).
Et bien, en programmation, ca s'écrit quasiment pareil...

Allez, un apercu de ce que vous saurez faire à la fin du chapitre: cette fonction traduite en LUA:

function f(x)
    resultat = 3*x+5
    return resultat
end

Alors, qu'est-ce qu'on attend ?

Retour en haut Retour en haut Retour en haut

Créer sa propre fonction

Nous allons commencer avec le type de fonction le plus simple: celle qui exécute des instructions dans un ordre précis. On appelle ces fonctions "void" ("vide" en français).
Nous allons commencer par définir le bloc de base:

function test() -- on "délare" la fonction
end

"function" est le mot clé avant de déclarer chaque fonction (c'est pour ca qu'on ne peut pas l'utiliser en tant que variable).
"test" est le nom de la fonction: il a les mêmes restrictions que le nom des variable.
"()" indique que la fonction est "void".
"end" referme la fonction. Vous DEVEZ refermer chaque fonction...

Allez, on va faire une fonction qui va faire un truc totalement débile, mais qui va vous prouver l'utilité de celles-ci.
On écrit:

function journee()
    print("aube")
    print("jour")
    print("crépuscule")
    print("nuit")
end

Maintenant, inscrivez juste après:

journee()

Que s'affiche-t-il ?

>lua -e "io.stdout:setvbuf 'no'" "test.lua"
aube
jour
crépuscule
nuit
>Exit code: 0


Miracle ! Notre fonction à affiché le texte ! On appelle ca appeller une fonction. Voilà un petit schéma de ce qu'a fait l'ordinateur:


Maintenant, pour prouver l'utilité d'une telle chose, faites:

journee()
journee()
journee()
journee()
journee()
journee()
journee()

Ainsi, pour faire une semaine, il vous aurait fallu 28 lignes sans fonction, maintenant il vous en faut 13 (et vous verrez par la suit qu'on peut réduire ce nombre à 4)!
Bon, je vous l'accorde, l'exemple est un peu débile :p
Par contre, faites bien attention à une chose: déclarez toujours vos fonctions avant de les appeller ! Sinon le script plantera...

Retour en haut Retour en haut Retour en haut

Fonctions à arguments

Nous allons faire maintenant cette chose: créer une fonction qui présentera différents personnages. Nous devrons connaitre leur nom, leur age et leur ville.
Mais comment faire pour aller plus vite ? Avec les fonctions pardi !

Pour vous expliquer, le code:

function presentation(nom, age, ville)
    print("Je m'appelle "..nom..", j'ai "..age.." ans et j'habite "..ville)
end

presentation("Toto", "11", "Paris")
presentation("Bertrand", "42", "Marseilles")
presentation("Benjamin", "22", "Nimes")
presentation("Nicole", "81", "Rouen")

Le résultat:

>lua -e "io.stdout:setvbuf 'no'" "test.lua"
Je m'appelle Toto, j'ai 11 ans et j'habite Paris
Je m'appelle Bertrand, j'ai 42 ans et j'habite Marseilles
Je m'appelle Benjamin, j'ai 22 ans et j'habite Nimes
Je m'appelle Nicole, j'ai 81 ans et j'habite Rouen
>Exit code: 0


On décortique les rajouts: "nom", "age" et "ville" sont des argments de la fonction "presentation": il doivent être remplis pour que l'action soit exécutée. Dans le code de la fonction, ces arguments prennent la valeur rentrée lors de l'appel de la fonction, à chaque fois qu'elle est appellée.
Les arguments peuvent être de N'IMPORTE QUEL type: nombres, chaines, "nil"...

Retour en haut Retour en haut Retour en haut

Fonctions renvoyant une valeur

Revenons a notre fonction mathématique... je la réecris pour vous souvenir: "f(x) = 3x + 5".
Nous voulons maintenant que l'on puisse stocker le résultat d'une fonction DANS une variable... nous allons utiliser le mot-clé: "return". On dira alors que la fonction retourne le résultat.
Le code décripté:

function f(x) -- on déclare la fonction, avec "x" qui sera un argument permettant le calcul
    resultat = 3*x+5 -- on calcule le résultat
    return resultat -- on renvoie le résultat
end -- on n'oublie pas de fermer la fonction

Un exemple d'utilisation:

calcul = f(1) -- on met le résultat dans une variable
print(calcul) -- et on l'affiche
print(f(2)) -- on peut aussi passer directement sans stocker dans une variable (attention au nombre de parenthèses)
print(f(3))
print(f(4))

>lua -e "io.stdout:setvbuf 'no'" "test.lua"
8
11
14
17
>Exit code: 0


Et voilà le travail ! Un schéma pour résumer tout ce que l'on vient de voir:

Je crois que vous commencez à en connaitre un bout sur le scriptage...

Retour en haut Retour en haut Retour en haut

Fonctions essentielles

Vous l'aurez compris, print est une fonction, qui réclame un argument: le texte à afficher, sous forme de chaine. Mais c'est loin d'être la seule: en effet, un certain nombre d'entre elles sont prédéfinies.
Je vais vous dire comment utiliser la documentation LUA, puis je vous montrerais les plus importantes d'entre elles.

La documentation LUA (ou doc LUA) est la liste de toutes les fonctions prédéfinies, ce qu'elles font et comment les utiliser (en anglais).
Le lien vers la doc (les fonctions sont en bas de page).

Voilà comment se présente une fonction:

"Type de valeur retournée NOM_DE_LA_FONCTION(argumentobligatoire1, argumentobligatoire2 [,argumentfacultatif1] [, argumentfacultatif2)"
Elle est en générale suivie d'une description assez complète qui donne plus de détails.
Et oui ! Certaines fonctions peuvent avoir des arguments facultatifs, que l'on est pas obligé de préciser !
Pour les valeurs retournées, il y en a que vous ne connaissez pas encore, mais je vous dit celles que vous connaissez:
- Integer: entier. Pour faire vite, un nombre sans virgule (les nombres négatifs sont des entiers).
- Real: un réel. Pour tout ceux qui ne sont pas en Terminale ou plus, ce sont tout les nombres que vous connaissez (les nombres entiers, racine de 2, pi, etc...).
- String: une chaine.

Les fonctions essentielles:

- Real math.random ([m][, n])
Cette fonction retourne un nombre aléatoire entre 0 et 1, avec 14 décimales.
Si les arguments "m" et "n" sont précisés, cette fonction retourne un entier aléatoire entre "m" et "n".
Exemple d'utilisation: obtenier un nombre aléatoire entre 1 et 100:

nombre = math.random(1, 100)

- Void error (message [, level])
Void, je vous le rappelle, signifie que la fonction ne retourne rien.
Cette fonction stoppe net le script et affiche un message d'erreur si "message" est rempli, avec un numéro de ligne si "level" est rempli. Nous verrons plus tard qu'elle peut être utile pour identifier des bugs...

- String type (variable)
Retourne le type de variable sous forme de chaine.

Bien sur, il y en a plein d'autre, je vous laisse le soin de les découvrir par vous même...

Retour en haut Retour en haut Retour en haut

Structures conditionnelles

Avec cette partie, les scriptage va vraiment commencer à devenir intéressant. En effet, comment faire pour que si mon personnage à 5 vies, j'affiche "tu es en pleine forme !" ? Si il en a 1 et qu'il y a des ennemis autour, "fait attention !" ? C'est ce que nous allons voir.

Les booléens, un terme de geek ?

Il va falloir apprendre un nouveau type de variable: les booléens (prononcez "boulet-1"). C'est un système très simple: soit la valeur est "vrai" ("true" en anglais), soit "faux" ("false"). Dans l'ordi ca ne prend pas beaucoup de place: "1" pour vrai et "0" pour faux.
Pour déclarer un booléen, c'est très facile:

test = true -- vrai
test2 = false -- faux
print(test) -- il s'affiche "true"
print(test2) -- il s'affiche "false"


Il peu aussi se déclarer d'une facon un peu plus utile: on peut faire des comparaisons.
Exemple:

test = (1 < 100)
test2 = (86 > 500)
print(test) -- il s'affiche "true" car 1 est plus petit que 100
print(test2) -- il s'affiche "false" car 86 n'est pas plus grand que 500


On utilise des opérateurs logiques pour faire ces comparaisons. Ils sont 6 en tout:
- "<": "inférieur à";
- ">": "superieur à";
- "<=": "inférieur ou égal à";
- ">=": "superieur ou égal à";
- "~=": "différent de";
- "==": "égal à". ATTENTION: ne pas confondre ce double signe "égal" qui sert à la comparaison et le signe simple qui sert à assigner une valeur à une variable ! Ceci est souvent source de bugs...

On peut aussi comparer des chaines entre elles ! Si on compare deux variables de type différents, alors il s'affiche "nil"...

test = ("bla" == "pasbla")
test2 = (5 == "bla")
print(test) -- il s'affiche "false"
print(test2) -- il s'affiche "nil"

Retour en haut Retour en haut Retour en haut

Tests

Maintenant que vous savez ce qu'est un booléen, nous pouvons faire... un TEST !
On utilise alors ce qu'on appele un bloc conditionnel dont voici la syntaxe:

if <condition> then
    <instructions à exécuter>
elseif <autrecondition> then
    <instructions à exécuter>
elseif <autrecondition> then
    <instructions à exécuter>
else
    <instructions à exécuter>
end -- on ferme le bloc


Si on traduit en français ca fait "SI (IF) ceci est vrai (condition) ALORS (THEN) fait cela (instructions) SINON SI (ELSEIF) ceci est vrai ALORS fait cela SINON (ELSE) fait cela. (le point équivaut à END)".
On n'est pas obligé de de mettre des "eslseif" et un "else", mais mettre un "else" à la fin est fortement conseillé (ne serait-ce que pour afficher un message d'erreur).
Exemple:

if (nombre_de_vies >= 1) then
    print("Tu est en vie !")
elseif (nombre_de_vies == 0) then
    print("Game over !")
else
    error("Une erreur s'est produite !") -- on réutilise nos connaissances !
end

Dans cet exemple, l'erreur pourrait arriver si la variable vaut "-1" (et oui ! Si on ne fait pas attention, ca peut arriver !).
Vous pouvez imbriquer autant de blocs que vous voulez !

Retour en haut Retour en haut Retour en haut

Opérateurs et tables de vérité

Maintenant, on veut savoir si deux conditions sont réunies à la fois... on utilise alors des connecteurs logiques. Ils sont au nombre de 4:
- "and": "et". Si les deux conditions sont vraies, le test est vrai;
- "or": "ou". Si une des deux ou les deux conditions sont vraies, le test est vrai;
- "xor": "ou exclusif". Si UNE SEULE des deux conditions est vraie, le test est vrai, sinon, il est faux;
- "not": "inverse". Si les deux conditions sont opposées, le test est vrai.

Avec ces opérateurs, on peut alors définir ce que l'on appelle des tables de vérité, qui résume le résultat de tout ces tests:

test a test b and or xor not
true true true true false false
true false false true true true
false true false true true true
false false true false false false


Pour votre culture mathématique, je vous invite à lire cette page.
Exemple d'utilisation:

if ((nombre_de_vies >= 1) and (nombreennemisacote == 0)) then -- si on a des vies et qu'il n'y a pas d'ennemis à coté
    print("Tu est en vie !")
elseif ((nombre_de_vies == 0) not (envie == true) then -- si on a 0 vies et qu'on est pas... en vie ^^
    print("Game over !")
else
    error("Une erreur s'est produite !")
end

Maintenant un petit script pour vous pouver qu'avec ce que pous connaissez, vous pouvez d'ores et déjà faire des trucs vachement complexes !
La fonction suivante prend en argument deux variables, et les compare.

function Testeurdevariables(variable1, variable2)
    if (type(variable1) == type(variable2)) then
        print("Variables de même type")
        if (variable1 == variable2) then
            print("Variables égales")
            return true
        else
            print("Variables non égales")
            return false
        end
    else
        error("Variables pas de même type, impossible de comparer")
    end
end

Si on écrivait cette fonction dans la doc, ce serait:
"Boolean Testeurdevariables(variable1, variable2)"
Utilisez-là pour voir ! Faites des tests !

Retour en haut Retour en haut Retour en haut

Les tables

Il était une fois... une table

Nous allons ici aborder le dernier type important de variable: les tables. C'est un moyen de répertorier un tas d'informations sans se perdre dans les noms de variables.
Pour comprendre les tables, il faut imaginer un tableau à deux entrées, comme ceci par exemple:

1 2 3 4 5 ...
705 -3,1415 "Hello World !" true math.random(1, 100) false

Chaque case de ce tableau est une variable, à laquelle on associe un nombre (par exemple, 705 est associé à 1).
En LUA, définir une table est assez facile. Exemple avec le tableau ci-dessus.

unetable = {
    705,
    -3.1415,
    "Hello World !",
    true,
    math.random(1, 100),
    false
}


Donc, pour déclarer une table, on met les valeurs entre {} et on met des virgules entre chaque valeur (on peut sauter des lignes si on veut).

Mais où sont passés les nombres 1, 2, 3... ???

Il n'apparaissent pas. En fait, c'est l'ordre dans lequel on indique les variables qui est important, et donne le nombre associé.
Grâce à ce que l'on vient de faire, vous n'avez plus qu'un nom de variable à retenir au lieu de 6 !
Bon c'est vrai, ici le choix du contenu n'est pas très judicieux. On pourrait rassembler les variables de même type ou alors qui se rapportent à la même chose.

Allez, pour les courageux (pas les cous rageux hein...), plus difficile: si je vous disais que dans une table on peut stocker... des tables ?
Je vous entend déjà hurler :p
On assiste alors à la création d'une matrice (non, pas le film).
Exemple:

matrice = {
    {1, 2, 3},
    {true, false, false},
    {"Hello World !", "bla", "J'aime pas les tables LUA"}
}


Là, il faut imaginer que chaque table du tableau est un... tableau !
Vous vous dites que c'est trop compliqué pour ce que ca fait ? Nous allons voir ca tout de suite !

Retour en haut Retour en haut Retour en haut

Afficher/Modifier une table


Pour acceder à une valeur dans une table, on fait comme cela (pour afficher, modifier... comme une variable normale en fait):

unetable = {10, 25, 48}

print(unetable[1]) -- il s'affiche 10 car c' est la première valeur de la table
print(unetable[2]) -- il s'affiche 25
print(unetable[3]) -- il s'affiche 48

unetable[2] = unetable[2] + 1
print(unetable[2]) -- il s'affiche 26 car on a incrémenté la variable de 1

unetable[4] = 96 -- on rajoute une case dans la table qui s'agrandit automatiquement !


Dans le cas des matrices, c'est pareil:

matrice = {
    {1, 2, 3},
    {true, false, false},
    {"Hello World !", "bla", "J'aime pas les tables LUA"}
}

print(matrice[2][3]) -- on prend la 3ème valeur de la 2ème table, il s'affiche "false"

Vous vous dites toujours que cela ne sert à rien ? Quand on travaillera sur le jeu, ces tables seront très courantes. Je vous demande seulement de retenir que ca existe pour l'instant si vous avez du mal à apprendre.

Retour en haut Retour en haut Retour en haut

Fonctions sur les tables

Il existe certaines fonctions qui nous permettent de travailler sur les tables:

- void table.insert(table, [position,] valeur)

Si une position n'est pas indiqué, cette fonction insère la "valeur" à la fin de la "table". Sinon, cette valeur est insérée à la position, et l'ordinateur décale alors automatiquement la fin de la table.

Exemple:

matable = {"bla", true, 3}

table.insert(matable, 645) -- la table vaut maintenant {"bla", true, 3, 654}
table.insert(matable, 2, false) -- la table vaut maintenant {"bla", false, true, 3, 654}


- void table.remove (table [, pos])

Inverse de "table.insert". Supprime la position indiquée et redécale la table, ou alors supprime la dernière entrée si aucune position n'est indiquée.

- void table.maxn (table)

Retourne le nombre maximum dans la table. Si la table ne contient pas de nombre, la fonction retourne 0.
- void table.foreach(table, function) Cette fonction est assez simple: pour chaque entrée de la table, elle exécute une fonction. Exemple:

matable = {4, 110, 3}

table.foreach(matable, print) -- il s'affiche 4 - 110 - 3
 

Retour en haut Retour en haut Retour en haut

Les boucles

Un instrument pour les fainéants

Retenez cette chose: les informaticiens, geeks, et autres sont des gros fainéants !
C'est pourquoi ils ont inventé des outils pour se simplifier la vie: les boucles.
Dans le genre "évitage de copier-coller", c'est encore bien mieux que les fonctions.
Le principe: l'ordinateur répète une action jusqu'a ce qu'on lui dise de s'arrêter.
Il y a trois types de boucles, nous allons de la plus simple à la plus compliquée, mais de la moins à la plus efficace.

Retour en haut Retour en haut Retour en haut

Repeat

La première boucle se construit comme ceci:

repeat

    <instructions>

until <condition>


Un exemple commenté:


La particularité de ce type de boucle, c'est que les actions sont effectuées au moins une fois, car le test se trouve à la fin de la boucle.
ATTENTION ! Si vous n'incrémentez pas votre compteur, la condition sera toujours fausse ! L'ordinateur effectuera l'action à l'infini, ce qui entraîne... un crash du script (sinon la mémoire sera surchargée...).

Retour en haut Retour en haut Retour en haut

While


Ici, c'est quasiment la même boucle, mais le test est fait au début.

while <condition> do

    <instructions>

end


Le même exemple mais avec while:

a = 1

while (a == 10) do

    print("bla")

end

print("boucle finie")
 


Retour en haut Retour en haut Retour en haut

For

Ici, nous allons aborder la boucle la plus efficace: elle inclus un compteur intégré. C'est celle que nous utiliserons dans 95% des cas, donc retenez-là bien !
Elle s'écrit comme ceci:

for <variable-compteur> = <début du compteur>, <fin du compteur> do

    <instructions>

end


Exemple:

for n = 1, 5000 do

    print("bla")

end


Ce code débile va afficher "bla" 5000 fois ! Le compteur commence à 1 et fini à 5000, l'action est donc répétée 5000 fois ! Très efficace non ?
La variable-compteur s'appelle usuellement "n" ou "i", c'est plus rapide à écrire.
Et on peut utiliser ce compteur dans les instructions ! Exemple:

for n = 1, 100 do

    print("Je suis la "..n.." ligne.")

end


Essayez, c'est marrant !
Vous pouvez très bien commencer le compteur à 152 ou -27... l'important est que ce soit un entier. Alors, heureux de ne plus écrire 10 fois la même chose ?

Retour en haut Retour en haut Retour en haut

Fonctions II: le retour

Cette partie est un peu plus dure, ce n'est pas grave si vous ne comprenez pas, passez votre chemin...

Arguments obligatoires/faculatitfs


Certaines fonctions ont des arguments facultatifs, vous en avez déjà utilisée; error() par exemple.
Comment faire ses propres fonctions à arguments facultatifs ?
Vous le savez déjà...

...

Allons toujours pas ?

...

Je vous donne un indice: un argument qui n'est pas précisé compte pour nil.

...

Vous ne voyez vraiment pas ? Il faut utiliser un bloc conditionnel comme ceci:

function DireBonjour(nom, age, ville)

    if ville == nil then -- si "ville" n'est pas donné cet argument vaut nil (il n'existe pas)
        print("Bonjour, je suis "..nom.." et j'ai "..age.." ans.")
    else -- sinon, tout les arguments sont remplis
        print("Bonjour, je suis "..nom.." et j'ai "..age.." ans et j'habite "..ville)
    end

end


Ca va ce n'est pas trop dur ?

Retour en haut Retour en haut Retour en haut

Fonctions retournant plusieurs valeurs


Certaines fonctions plus compliquées peuvent retourner plusieurs valeurs...
C'est très simple:

function SommeDifference(a, b)

    somme = a + b
    difference = a - b
   
    return somme, difference

end


On stocke les variables comme ceci:

i, j = SommeDifference(5, 3)

print(i) -- vaut 8
print(j) -- vaut 2
 


On aurait très bien pu aussi ne stocker que i...

i = SommeDifference(5, 3)

print(i) -- vaut 8
print(j) -- vaut nil
 


Retour en haut Retour en haut Retour en haut

Nombre infini d'arguments

La ça se corse... comment faire si on veut mettre un nombre infini d'arguments ?
Par exemple, faire une fonction qui affiche toutes les variables qu'on lui envoie ?

Et bien, pour commencer, quand on défini la fonction il faut mettre:

function Affiche(...)

end
 


Les 3 petits points font savoir à l'ordinateur qu'il pourra recevoir un nombre variable d'arguments.
Quand on utilise la fonction, les arguments sont stockés dans une table appellée "arg" !
Une seule chose: il faut utiliser la fonction unpack() (dans certains cas seulement) sur cette table pour pouvoir accèder correctement aux données. Regardez si ca marche sans ou avec. D'où l'utilité de connaitre ce type de variable... Allez on réutilise nos connaissances !

function Affiche(...)
    -- la ca marche sans unpack(), mais sinon o aurait mis "arg = unpack(arg)"
    table.foreach(arg, print)
end

-- Exemple:
Affiche("bla", true, 275) -- il s'affiche bla - true - 275
Affiche() -- il ne s'affiche rien car il n'y a pas d'argument donc la table est vide
Affiche(2, 568) -- il s'affiche 2 - 568
 


Et mesdames et messieurs, cette fonction que nous venons d'écrire... existe déjà !!!
Vous la connaissez, c'est "print" !
J'ai "omis" de vous le préciser au début car ça aurait été trop compliqué mais maintenant vous pouvez !

Exemple:

print("bla", 2, true) -- ils'affiche bla - 2 - true !!!
 



Retour en haut Retour en haut Retour en haut

Autres

Cette partie est un peu fourre-tout, tout ce que je n'ai pas encore abordé et qui ne mérite pas une grande partie.

Localiser une variable

Il faut tout d'abord revoir la définition d'un bloc d'exécution.
Un bloc d'exécution commence TOUJOURS par un mot clé et fini TOUJOURS par un "end". Ces deux mots et ce qui se trouve entre forme un bloc d'exécution.

Vous les connaissez tous (if, while, repeat, for, functon), sauf un:

do
    <instructions>
end
 

Que fais ce bloc ? Il ne fait qu'exécuter les actions à l'interieur... Vous me direz: à quoi ça sert ?
Et bien c'est pour la chose qu'on va voir...

Pour éviter que la mémoire ne soit surchargée (bon et puis aussi pour montrer qu'on sait le faire car la mémoire ça se surcharge trèèèèès lentement...), et pour eviter que l'on remplace des variables déjà utilisées, on a inventé un mot clé: "local".
Ce mot-clé, placé devant une variable, signifie que la varable s'autodétruira à la fin de son bloc (stylé non ?).

Exemple:

local boolean = true -- on localise la variable

if boolean == true then -- /!\ Début d'un bloc /!\
    local a = 20
    print(boolean, a) -- il s'affiche true 20 car aucune des variables ne s'est autodétruite
end -- /!\ Fin d'un bloc /!\ : "a" est détruite car c'est la fin de son bloc, mais "boolean" reste car son bloc n'est pas terminé (il corresspnd à la fin du script...)

print(boolean, a) -- il s'affiche donc true nil (car la variable n'existe plus).
 


Ceci est très pratique dans le cas des boucles, car la variable est détruite à chaque fois que la boucle recommence.
Et c'est pour ça que le bloc que je vous ai montré au-dessus ne sert pas à rien.

Retour en haut Retour en haut Retour en haut

Mesurer la longueur d'une variable

Assez couramment, vous aurez besoin de mesurer la longueur d'une variable. Pour cela on utilise le symbole clé "#" devant cette variable pour en savoir la longueur.

boolean = false
integer = 527
string = "Je suis une chaine"
table = {true, 20, "Super !"}

a = # boolean -- produit une erreur, on ne mesure pas un booléen
b = # integer -- pareil
c = # string -- vaut 18 car il y a 18 caractères dans la chaine (l'espace compte)
d = # table -- vaut 3 car il y a 3 entrées dans la table

print(a, b, c, d)
 

Retour en haut Retour en haut Retour en haut

Les tables à clés

Une autre façon d'utiliser les tables: mettre des clés. Qu'est ce que cela veut dire ? Faire comme ceci par exemple:

manger = {
    orange = { -- on déclare les variables à l'interieur de la table comme n'importe quelle variable
        couleur = "orange",
        bon = true,
    },
    banane = {
        couleur = "jaune",
        bon = true,
    },
    fraise = {
        couleur = "rouge",
        bon = true,
    },
    topinambour = {
        couleur = "marron",
        bon = false, -- avez-vous déjà mangé des topinambours ? Non, et heureusement. ^^
    },
}
   
-- Pour accèder à une valeur dans la table il faut utiliser des points au lieu de crochets:

if manger.orange.bon then
    print(manger.fraise.couleur) -- il s'affiche "rouge"
end

-- on peut aussi mettre des nombres en clé !
autretable{
    1 = true,
    2 = "bla",
    3 = 523,
}

-- équivaut en fait à:
autretable = {true, "bla", 523}
 


C'est très pratique pour ranger des choses par catégories, et peut-être plus facile que l'autre manière.

Retour en haut Retour en haut Retour en haut

On fait quoi maintenant ?


Vous savez scripter en LUA ! Ce n'est pas magnifique ?
Je vous conseille de vous exercer et de bien maitriser le sujet avant de passer à la suite...
Regardez aussi un peu la liste des fonctions LUA, ça pourrait vous donner des idées.
Allez, vous devriez être capable de comprendre ce bout de code (on reprend les anciennes idées !)

-- Pour comparer deux variables
function TesteurDeVariables(variable1, variable2)

    if (type(variable1) == type(variable2)) then
        if (variable1 == variable2) then
            resultat = "Variables égales"
        else
            resultat = "Variables non égales"
        end
    else
        resultat = "Variables pas de même type, impossible de comparer"
    end

    return resultat

end

-- Compare les variables une à une
function ComparerToutesLesVariables(...)

    local tableau = {}
    local arg = unpack(arg) -- la ça marche pas sans
    local longueur = # arg
    print(longueur)

    for i = 1, # arg do

        for j = 1, # arg do

            if i ~= j then -- On ne compare que si les deux valeurs à tester ne sont pas deux fois la même

                local compare = {arg[i], arg[j], TesteurDeVariables(arg[i], arg[j])}
                table.insert(tableau, compare)
                print("Comparé:", arg[i], "Comparant:", arg[j], "Resultat:", TesteurDeVariables(arg[i], arg[j]))

            end

        end

    end

    return tableau -- on renvoie un tableau contenant les resultat si on veut s\en servir plus tard

end

matable = {"bla", 3, 1, 1}

ComparerToutesLesVariables(matable)
 


Il y a un truc qui ne va pas ? Regardez la partie corresspondante !

Retour en haut Retour en haut Retour en haut

II Le SCAR, ou comment scarifier le jeu de fond en comble

Alors ? Ca sert à quoi tout ca ?

Afficher la console

Et la vous me dites: mais ça sert à rien ton truc, on n'a jamais touché au jeu !
Et si je vous disais que les rbf sont en fait des gigantesques tables LUA, hmmm ? Ca vous met pas la puce à l'oreille ?
Bon ok vous êtes comme Thomas, il faut que vous voyez pour croire.
Bon. Lancez le jeu en mode développeur. Quoi ? Vous ne savez pas ?

Maintenant que c'est fait, dans le menu principal, appuyez sur la touche en rouge (selon votre type de clavier, un clavier azerty en vaut deux !):

Et que voyez-vous apparaitre ?

Oh une console !!!

INFO: vous pouvez la cacher/révéler en rappuyant sur la touche.

Ca ne vous dit toujours rien ?
Bon ok. Alors c'est parti. Je vais vous illuminer, vous qui êtes dans les ténèbres...

Retour en haut Retour en haut Retour en haut

Premier VRAI script

Bon, avec le SGA Reader allez chercher le fichier "annihilate.scar" se trouvant dans "simulation > scar > winconditions" (200_data.sga).

Ouvrez-le avec Scite (Notepad++ peut maintenant très bien faire l'affaire), voici ce que vous voyez:

import("WinCondition.scar")

function WinCondition_OnInit()
    WinCondition_StartupAnnihilate()
    WinCondition_RemoveAllVictoryPoints()
end
Scar_AddInit(WinCondition_OnInit)
 

Ca ne vous dis rien ? Cepandant, il y a des trucs que vous n'avez jamais vu.
Vous pouvez deviner à quoi servent les fonctions WinCondition_StartupAnnihilate() et WinCondition_RemoveAllVictoryPoints(), mais import() et Scar_AddInit() vous vous demandez à quoi ca sert...

Rajoutez cette ligne pour avoir ceci:

import("WinCondition.scar")

function WinCondition_OnInit()
    WinCondition_StartupAnnihilate()
    WinCondition_RemoveAllVictoryPoints()
    FOW_RevealAll()
end
Scar_AddInit(WinCondition_OnInit)
 

Maintenant, enregistrez le fichier au bon endroit et lancez le mode dev.
N'ESSAYEZ PAS DE LANCER CE SCRIPT SUR SCITE, VOUS OBTIENDREZ UNE ERREUR ! En effet, le logiciel manque de quelques trucs qu'a le jeu.

Maintenant en dev, lancez un partie d'annihiliation normale, et observez: le brouillard de guerre (Fog Of War : FOW) a disparu !!!

Retour en haut Retour en haut Retour en haut

Alors, heureux ?

Bon, ce n'est qu'un apercu, juste pour vous montrer qu'en fait le jeu est bourré de LUA. Des cinématiques de début de jeu aux parties acharnées en ligne, le LUA est omniprésent (Comme Chuck Norris :D).

Et ce n'est qu'un début ! Je ne vous avais pas mis en lien avec le jeu avant, vous auriez été trop troublé, là vous allez tout comprendre d'un coup !!!
Aller, on se lance une bonne fois pour toutes ?

INFO: en anglais, "scar" signifie "cicatrice"...

Retour en haut Retour en haut Retour en haut

Fonctions de base en SCAR

Nous allons maintenant analyser les lignes ci-dessus qui apportent leur lot de nouvelles fonctions.
A partir de maintenant, je vous demanderais de ne plus mettre .lua comme extension de fichier mais .scar !

import

- Void import(string)
Cette fonction est très utile: elle sert à importer toutes les fonctions et variables que contient un fichier.
Comme ça, vous n'êtes pas obligé de tout stocker dans le même fichier !

Exemple:
fichierprincipal.scar:

import("fichiersecondaire.scar")

Affiche500fois(texte)


fichiersecondaire.scar:

function Affiche500fois(variable)

    for n = 1, 500 do

        print(variable)

    end

end

texte = "Un bout de texte"


Cela équivaut à:

function Affiche500fois(variable)

    for n = 1, 500 do

        print(variable)

    end

end

texte = "Un bout de texte"

Affiche500fois(texte)


Par rapport au jeu, les fichiers importés sont placés dans "data\simulation\scar".
Donc vous devrez placer vos fichiers là-dedans si vous voulez les importer.

Vous pouvez même importer un fichier dans un sous-dossier, il suffit de mettre:

import("sousdossier/monfichier.scar")


Dans le code ci-dessus, le fichier "scarutil.scar" est le fichier principal. Par une succession d'import, toutes les fonctions SCAR de base sont importées, de quoi bien modifier le jeu.

Retour en haut Retour en haut Retour en haut

Scar_AddInit

- Void Scar_AddInit(function)
Voilà LA fonction principale. Quand vous travailliez sous Scite, le scipt se lancait automatiquement.
Ici, c'est different. On indique à l'ordinateur une fonction à exécuter au démarrage.
Dans le code ci-dessus, c'est la fonction "WinCondition_OnInit" qui est lancée au démarrage.

C'est aussi la première fois que l'on utilise une fonction comme argument !
Je vais vous dire un secret... je ne vous l'ai pas dit avant pour ne pas vous embrouiller, et surtout parce que cette façon de faire est BEAUCOUP moins courante, mais une fonction peut aussi se déclarer comme ceci:

nomdelafonction = function(argument1, argument2)

    <instructions>
   
end


Vous comprenez alors pourquoi on se rapproche des variables...

Retour en haut Retour en haut Retour en haut

print

Vous connaissez déjà cette fonction, mais je voulez préciser une chose: quand vous ferez un print dans le jeu, le texte s'afficher sur la console, et non sur l'écran. Les personnes qui ne seront pas en -dev ne pourrons donc pas voir le texte. Nous verrons plus tard comment afficher du texte à l'écran.

Retour en haut Retour en haut Retour en haut

Utilisation de la documentation

La liste des fonctions SCAR se trouve ici.
Elle contient la description de 1607 (!!!) fonctions de base.

Extrayez l'archive RAR, puis ouvrez le fichier "frameset.htm" avec votre navigateur internet.
Les fonctions sont regroupées en sous-dossiers à gauche, les fonctions se trouvant à droite.
Elles apparaissent dans l'ordre alphabetique, mais le plus simple est (si vous avez Mozilla Firefox par exemple) de faire Edition > Recherche, et de taper le nom de la fonction.

Malheureusement, la documentation est en anglais, mais elle n'est pas très dure à comprendre (toujours le même vocabulaire...).
Ne cherchez pas pour l'instant à comprendre toutes les fonctions, essayez (si vous avez le temps, de toutes façon on les reverra) de regarder un peu au pif pour voir l'étendue des possibilités offertes.

Une ligne est rajoutée par rapport à la doc LUA: la source. Elle indique le fichier ou est défini cette fonction.
Si c'est un fichier .scar ou .lua, vous le trouverez dans "data\simulation\scar".
La ligne est indiquée, cependant la doc n'a pas été mise à jour depuis Chaos Rising donc il se peut que les lignes aient changées, il vous faudra alors faire une recherche manuelle.
Si le fichier est .cpp ou autre, vous ne pourrez pas accèder à la source (elle est la propriété de Relic).
Remonter à la source peut aider pour le debuggage ou carrément modifier une fonction de base.

Retour en haut Retour en haut Retour en haut

Notions de base

Hop, nous allons voir des tas de nouveaux types de variables... au fait, maintenant, au lieu d'utiliser type(variable) qui donne le type des variables LUA, utilisez plutôt scartype(variable) - cherchez là dans la doc, elle est dans le sous-dossier "Various".
Elle retourne un entier, qui est en fait une suite de lettres en majuscules. Ne vous préoccupez pas d ca, si vous en avez besoin dans un test, faites comme si c'était une string sans guillemets.

if scartype(variable) == ST_TABLE then
    <instructions> 
end


Blueprints

Les blueprints (ou "chemins" en français), sont juste les emplacements des divers fichiers RBF du jeu...
En effet, quand on demandera au jeu d'utiliser un fichier RBF, on ne lui donnera pas le chemin directement, mais un nombre entier le représentant, aussi appelé PBG (PropertyBagGroupID).

LA fonction pour connaitre le PBG d'un fichier (cela marche avec des ebps, sbps, ability, upgrade, wargear, weapons... tout quoi):

sbps_force_commander = World_GetPropertyBagGroupID("sbps/pvp/race_marine/heroes/sm_force_commander")


Retour en haut Retour en haut Retour en haut

PlayerID

Les playerID sont des entiers représentants les joueurs.
Ce type de variable est très souvent utilisé...
Pour obtenir l'identifiant d'un joueur, il faut utiliser cette fonction:

-- exemple: ID du joueur 2:
player2 = World_GetPlayerAt(2)


Retour en haut Retour en haut Retour en haut

EntityID

Les entityID sont des entiers représentants des objets en jeu.
Objets, soldats, points de victoire, véhicules... tout ca, c'est des entités. En fait tout ce qui à un fichier ebps (je le rapelle EntityBluePrintS) est susceptible d'être une entity en jeu.
Pour connaitre l'ID d'une entity, un seul moyen: il faut faire du mapping. Dans le WorldBuilder, laissez votre curseur sur un objet ou autre, vous obtiendrez son ID. Pour l'utiliser en jeu, il suffira alors de faire:

-- exemple: mon objet à pour ID 50503 dans le WorldBuilder:
id_objet = Entity_FromWorldID(50503)


Retour en haut Retour en haut Retour en haut

EGroup/SGroup

Les EGroup sont des groupes de batiments, les SGroup d'escouades. On les utilise car c'est plus facile de modifier un groupe dans son ensemble que de le faire que par un objet par un objet...
La liste à un ordre, quand vous ajoutez une escouade/un batiment, celui-ci est placé à la fin.
Voici les fonctions de base, elles sont quasiment les mêmes pour EGroup/SGroup, donc je ne montre que pour les SGroup.

-- Création d'un SGroup
sgroup = SGroup_Create("Mon SGroup")

-- Ajout d'une escouade à la fin d'un SGroup (on verra les SquadID juste après)
SGroup_Add(sg, sqdID)

-- Créer un automatiquement un SGroup contenant toutes les escouades du joueur 1:
sg_touteslesescouades = Player_GetSquads(World_GetPlayerAt(1))


Retour en haut Retour en haut Retour en haut

SquadID

Les squadID sont des entiers (on se demande pourquoi ça ne change pas...) représentant les escouades en jeu. Chaque escouade à un identifiant. Pour le connaitre, il faut utiliser un SGroup contenant l'unité:

-- exemple: l'escouade se trouve à la 3ème position d'un SGroup:
sqd_unite = SGroup_GetSpawnedSquadAt(sg_unite, 3)


Tout cela vous parait peut-être un peu flou, mais on va passer à un exemple concret.

Retour en haut Retour en haut Retour en haut

Faisons joujou avec le WorldBuilder

Nécéssite de connaitre le WorldBuilder, maintenant on va pouvoir difficilement faire sans... si vous ne le connaissez pas, soit go sur le tuto mapping, soit vous suivez sans le support.

Posons un marker

Les markers sont des points placés sur une map, pour pouvoir retenir une position et l'utiliser par la suite.
Commencez par ouvrir le WB, créez une nouvelle map ayant pour nom "test", placez deux starting positions, désactivez les brouillards, puis créez un fossé au milieu (inutile d'être précis, c'est pour l'exemple).

Pour placer un marker, lancez le Render Marker Prox mode (F4).
Séléctionnez "region" dans la liste.

Faites un clic-droit dans le fossé, un marker apparait !
Changez le nom du marker en double-cliquant dessus pour qu’il corresponde à "spawn_marker_ally_1".

Placez les markers "spawn_marker_ally_2", "spawn_marker_ennemy_1", "spawn_marker_ennemy_2" pour obtenir grosso-modo ceci (les noms doivent être exacts !):

Une fois ces manips faites, faites "Save and Bake" (enregistrez dans le dossier "Pvp"), et vous pouvez fermer le WB, on n'en aura plus besoin !

Retour en haut Retour en haut Retour en haut

Exemple concret


Il faut savoir une chose: quand le jeu lance une map, il regarde si il y a un fichier du type "nomdelamap.scar"... si il y en a un, il l'exécute !
Créez donc dans "GameAssets\Data\Maps\Pvp" un fichier nommé "test.scar".
On va faire un script pour mettre en place une petite scenette !

Je rappelle la base: on crée une fonction, qui va être démarrée par Scar_AddInit (là on n'a pas besoin d'importer les fonctions car le jeu l'à déjà fait auparavent).

function OnInit()

end

Scar_AddInit(OnInit)


On va utiliser LA fonction pour faire apparaitre (spawner) un escouade:

- Void Util_CreateSquadsAtMarker(playerid, sgroup, blueprintID, location, numsquads[, loadout])

Hop hop, on décrit:
- playerid est l'ID du player qui va être le possesseur de l'escouade spawnée;
- sgroup est le... SGroup qui va contenir l'escouade;
- blueprintid: le PBG de l'unité;
- location: la position ou va spawner l'escouade. Il faut un markerID, nous allons voir comment l'obtenir;
- numsquads: le nombre d'escouades;
- loadout (optionnel): le nombre de gars dans l'escouade quand elle apparaitra.

On s'occupe d'abord des playerid, des SGroups et des PBG (n'hésitez pas à vous reporter à la doc).

function OnInit()

    -- les playerid
    player1 = World_GetPlayerAt(1)
    player2 = World_GetPlayerAt(2)

    -- les sgroups
    sg_player1 = SGroup_Create("Unités du joueur1")
    sg_player2 = SGroup_Create("Unités du joueur2")
   
    -- les pbg: on va utiliser des scouts et des hormagaunts
    sbps_scouts = World_GetPropertyBagGroupID("sbps/pvp/race_marine/troops/sm_scout_marine")
    sbps_hormagaunts = World_GetPropertyBagGroupID("sbps/pvp/race_tyranid/troops/tyr_hormagaunt")
   
    -- pour obtenir un markerID, il faut utiliser la fonction:  Marker_FromName("nomdumarker", "")
    marker1 = Marker_FromName("spawn_marker_ally_1", "")
    marker2 = Marker_FromName("spawn_marker_ally_2", "")
    marker3 = Marker_FromName("spawn_marker_ennemy_1", "")
    marker4 = Marker_FromName("spawn_marker_ennemy_2", "")
   
    -- on crée 2 escouades à chaque marker !
    Util_CreateSquadsAtMarker(player1, sg_player1, sbps_scouts, marker1, 2)
    Util_CreateSquadsAtMarker(player1, sg_player1, sbps_scouts, marker2, 2)
    Util_CreateSquadsAtMarker(player2, sg_player2, sbps_hormagaunts, marker3, 2)
    Util_CreateSquadsAtMarker(player2, sg_player2, sbps_hormagaunts, marker4, 2)
   
    -- Hop, une fonction de découverte de plus: Cmd_Attack(sgroupattaquant, sgroupattaqué)
    Cmd_Attack(sg_player1, sg_player2)
    Cmd_Attack(sg_player2, sg_player1)
   
end

Scar_AddInit(OnInit)


Et voilà le travail ! Lancez le jeu en dev et testez la map ! Vous verrez alors apparaitre 4 escouades de scout au milieu qui s'empresseront d'attaquer les 4 escouades d'hormagaunts du joueur 2...
J'espère que ça commence à devenir clair pour vous, car on rentre dans le vif du sujet !

Retour en haut Retour en haut Retour en haut

TD: recréer une condition de victoire: Anihiliation

Cahier des charges

Je n'ai presque plus rien à vous apprendre maintenant ! Je vais juste vous faire pratiquer et découvrir de nouvelles fonctions via des Travaux Dirigés.
Dans ce 1er TD, nous allons recréer la condition de victoire "Anihiliation". Attention ! Pas celle dans le jeu, mais bel et bien quand TOUTES les escouades sont mortes.

Il y a en fait deux types importants de fichiers scar:
- Les scripts de maps (ce que l'on vien de voir): il ne fonctionnent que sur la map concernée.
- Les conditions de victoire, qui fonctionnent à chaque fois qu'on les séléctionne.

Pour commencer, nous allons créer notre conditions de victoire.
Créez un fichier "TD1_local.lua" dans le dossier "Data\simulation\scar\winconditions".
Oh du .lua !
Et oui ! Le jeu ce sert de la fin "_local.lua" pour savoir à quoi sert le fichier ! Il faudra donc mettre cette extension !
C'est là que nous allons décrire la condition de victoire (wincondition) en général, sans scripter.
Mettez-y ce code:

Localization =
{
    title           = "TD1: Anihiliation",
    win_message     = "Vous avez anéanti l'ennemi !",
    lose_message    = "Vous vous êtes fait anéanti !",
    description     = "Anéantissez l'ennemi !"
}

-- the scenarios that this game type can be played on
ScenarioFilter =
{
    "default"
}

Pas besoin d'expliquer, c'est de l'anglais facile ;)
Ne touchez pas à la deuxième partie...

Maintenant créez un fichier "TD1.scar" au même endroit, c'est là dedans qu'on va scripter.

Nous allons découvrir une nouvelle notion: le LUA est monotâche. En effet, il execute le script dans l'ordre.
Cependant, avec le SCAR, il est possible de faire tourner en "tâche de fond" une action (une boucle si vous voulez) pendant que le script s'exécute.
Ces fonctions sont dans la catégorie "Rule" de la doc.

Par exemple, une fonction que l'on voudrait exécuter automatiquement toutes les 10 secondes:

-- variables de départ
player1 = World_GetPlayerAt(1)
sg_player1 = SGroup_Create("Unités du joueur1")
sbps_scouts = World_GetPropertyBagGroupID("sbps/pvp/race_marine/troops/sm_scout_marine")
marker1 = Marker_FromName("spawn_marker_ally_1", "")

function Spawn() -- la fonction que l'on veut actionner toutes les 10 secondes

    Util_CreateSquadsAtMarker(player1, sg_player1, sbps_scouts, marker1, 2)
   
end

Rule_AddInterval(Spawn, 10) -- la fonction de type Rule qui fait ce que l'on veut.

Il y a aussi la fonction Rule_Add qui cette fois-ci exécute la fonction à chaque image affichée à l'écran.
Inutile de dire qu'elle demande beaucoup plus de ressources à l'ordi que la première, donc il ne faut pas en abuser.

Les quelques fonctions que vous ne connaissez pas et dont vous aurez besoin:

Player_GetEntities(playerid) -- pareil que "player_GetSquads", mais retourne un EGroup.
EGroup_CountAlive(egroup) -- retourne le nombre de batiments vivants dans le EGroup
SGroup_TotalMembersCount(sgroup) -- pareil, mais pour les SGroup

Setup_SetPlayerTeam(playerid, n) -- met le joueur dans l'équipe numéro "n".
WinCondition_RemoveAllVictoryPoints() -- enlève tout les points de victoire
World_GetPlayerCount() -- Retourne le nombre de joueurs

World_SetTeamWin(playerid, "anihilate") -- fait gagner l'équipe du joueur concerné par le playerid
World_SetGameOver() -- met fin au jeu. ATTENTION ! Vous devez déclarer une équipe gagnante avant de mettre fin au jeu !
 

Essayez de chercher, si vous n'y arrivez vraiment pas, regardez les indices... mais pas les solutions !

Retour en haut Retour en haut Retour en haut

Indices

On ne cherche pas très loin ? On veut des indices ? En voici-en voilà !

1) Il faut mettre les joueurs dans la même équipe et enlever les points de victoire. Cela peut paraître évident, mais cela ne l'est pas en SCAR.

2) On va définir une fonction qui va être exécutée toutes les secondes par exemple. Cette fonction servira à determiner si quelqu'un a gagné.

3) N'oubliez pas qu'il faut refaire les Groups à chaque instant, les joueurs créent des escouades !

Retour en haut Retour en haut Retour en haut

Fait par: Kornelord, 2010, tous droits réservés. Hébergé par: Toile Libre Mentions légales
13232 connectés sur mon site depuis le 01/01/2010 !