Différences entre les versions de « TESCS : Scripting »

De Wiwiki
Aller à la navigation Aller à la recherche
m (update du lien pour Script for dummies, sept ans après)
(45 versions intermédiaires par 4 utilisateurs non affichées)
Ligne 1 : Ligne 1 :
[[Catégorie:TESCS : Tutoriaux|Scripting]]
[[Catégorie:TESCS : Tutoriels|Scripting]]
__NOTOC__
[[Catégorie:TESCS : Scripting|*]]
 
{{Lien retour |
page=[[TESCS : Aide aux Moddeurs]]
}}


__TOC__
__TOC__


==Tutoriels de références==
==Tutoriels de références==


Avant d'aller plus loin, les documents suivants sont l'alpha et l'oméga pour tout scripteur :
Avant d'aller plus loin, les documents suivants sont l'alpha et l'oméga pour tout scripteur :
Ligne 12 : Ligne 15 :
'''en français :'''
'''en français :'''


*[http://morromods.wiwiland.net/spip.php?article287 Morrowind Scripting for Dummies 8ème édition]. Manuel essentiel et quasiment exhaustif qui commence par expliquer les bases avant de couvrir toutes les commandes, les difficultés et les demandes les plus courantes des scripteurs, y compris le scripting avancé, avec de nombreux exemples; recommandable du novice au scripteur chevronné.
*[https://ressources.wiwiland.net/spip.php?article1021 Morrowind Scripting for Dummies 8ème édition] (en abrégé, MSfD) Manuel essentiel et quasiment exhaustif qui commence par expliquer les bases avant de couvrir toutes les commandes, les difficultés et les demandes les plus courantes des scripteurs, y compris le scripting avancé, avec de nombreux exemples; recommandable du novice au scripteur chevronné.


*[http://membres.lycos.fr/imrryran/fonctions.htm Un début de traduction de la liste alphabétique des fonctions de scripting établie par UESP]
*[http://membres.lycos.fr/imrryran/fonctions.htm Un début de traduction de la liste alphabétique des fonctions de scripting établie par UESP]
*[[TESCS_:_Liste_des_commandes_de_script|Liste des commandes de script sur le Wiwiki]]




Ligne 21 : Ligne 26 :
*[http://planetelderscrolls.gamespy.com/View.php?view=Mods.Detail&id=6083 Morrowind Scripting for Dummies 9ème édition] : édition revue, augmentée et complétée par rapport à la précédente.
*[http://planetelderscrolls.gamespy.com/View.php?view=Mods.Detail&id=6083 Morrowind Scripting for Dummies 9ème édition] : édition revue, augmentée et complétée par rapport à la précédente.


*[http://www.uesp.net/wiki/Tes3Mod:Alphabetical_Function_List Liste des commandes sur UESP] la liste complète des commandes de scripting par ordre alphabétique, avec leur syntaxe, leur description et les subtilités d'utilisation. Le guide de référence.
*[http://www.uesp.net/wiki/Tes3Mod:Alphabetical_Function_List Liste des commandes sur UESP] la liste complète des commandes de scripting par ordre alphabétique, avec leur syntaxe, leur description et les subtilités d'utilisation. Le guide de référence, même si on y trouve occasionnellement des erreurs et des imprécisions (MSfD constitue souvent une source plus fiable).
 


==Concepts de base==
==Concepts de base==
Ligne 62 : Ligne 66 :




- le script local, attaché à un objet placé dans le jeu, c'est à dire à une référence. De cette façon, il permet de travailler directement avec cette référence en évitant les confusions entre id et référence.
- le script local, attaché à un objet. Chaque instance de cet objet placée dans le jeu possèdera son propre script local qui tournera de façon indépendante. De cette façon, un tel script permet de travailler directement avec cette référence en évitant les problèmes de confusions entre id et référence.


Un script local ne s'exécute que dans la cellule où est placée sa référence, en intérieur, plus les huit cellules adjacentes, en extérieur.
Un script local ne s'exécute que si le PJ se trouve dans la cellule où est placée sa référence, en intérieur, plus les huit cellules adjacentes, en extérieur.




Ligne 79 : Ligne 83 :
- il y a une limite au nombre de caractères du script (environ 32000) et une limite du nombre de lignes (autour de 1000, probablement 1024 lignes une fois le script compilé)
- il y a une limite au nombre de caractères du script (environ 32000) et une limite du nombre de lignes (autour de 1000, probablement 1024 lignes une fois le script compilé)


- il y a une limite de 255 commandes compilées dans une boucle if... endif
- il y a une limite de 255 commandes compilées dans une boucle if... endif (info Galsiah; concrètement, cela se traduit par un nombre de lignes limite dans une boucle)


- il y a une limite au nombre de conditions if elseif dans un script.
- il y a une limite au nombre de conditions if elseif dans un script.


- la 34e variable locale de même type déclarée dans un script n'est pas prise en compte.
- la 34e variable locale de même type déclarée dans un script n'est pas prise en compte.
- le bug des 72 heures et la recharge d'une sauvegarde affectent le scripting. Notamment, si les variables locales d'un script local ne sont jamais remises à zéro, en revanche les variables locales d'un script global sont remises à zéro après 72 heures si le script n'a pas été lancé au cours de la session de jeu courante. D'après MSfD 9, les scripts ciblés peuvent également perdre ainsi leur cible.


==Liste alphabétique détaillée et commentée des commandes de scripts==
==Liste alphabétique détaillée et commentée des commandes de scripts==
Pour la liste complète des fonctions, voir :
[[TESCS : Liste des commandes de script]]
La liste ci-après sera transférée progressivement dans la liste précédente.




Ligne 91 : Ligne 102 :


*''ref'' désignera une référence
*''ref'' désignera une référence
*''objet_id'' l'id d'un objet
*''<type>_id'' l'id d'un élément de type ''<type>'' (par exemple ''cell_id'' désigne l'id d'un élément de type cell, ''sort_id'' l'id d'un sort, etc.)
*''n'' un nombre de type short, éventuellement une variable
*''n'' un nombre de type short, éventuellement une variable
*''x y z'' un nombre de type float, éventuellement une variable
*''x y z'' un nombre de type float, éventuellement une variable




====AddItem====
====[[TESCS : AddItem|AddItem]]====




Ligne 102 : Ligne 113 :


Permet d'ajouter ''n'' copies d'un objet d'id ''objet_id'' à l'inventaire de la référence ''ref''.
Permet d'ajouter ''n'' copies d'un objet d'id ''objet_id'' à l'inventaire de la référence ''ref''.
D'après MSfD, Additem admet des variables en argument, mais seulement dans les résultats de dialogues et si la variable utilisée n'est pas modifiée dans le même résultat de dialogue.


Cette commande est la seule façon d'affecter une référence dans une cellule encore non visitée, au contraire des commandes de type ''Set<attribute>'', ''Mod<attribute>'', qui ne fonctionnent pas dans ce cas.
Cette commande est la seule façon d'affecter une référence dans une cellule encore non visitée, au contraire des commandes de type ''Set<attribute>'', ''Mod<attribute>'', qui ne fonctionnent pas dans ce cas.


 
====[[TESCS : AiTravel|AiTravel]]====
====AiTravel====




Ligne 127 : Ligne 138 :
Attention, lors de cette frame, le PNJ compagnon suiveur se trouve toujours dans la cell précédente. Ce n'est qu'ensuite qu'il est téléporté dans la nouvelle cell,d'où les bugs de PNJs qui subissent des dégats de chute lorsque les deux cells sont à des altitudes différentes et la difficulté de les corriger; dans Morrowind, bon nombre de cells intérieures ont été négligemment créées à l'altitude z = 16000, au lieu de respecter la cohérence entre l'intérieur et l'extérieur.
Attention, lors de cette frame, le PNJ compagnon suiveur se trouve toujours dans la cell précédente. Ce n'est qu'ensuite qu'il est téléporté dans la nouvelle cell,d'où les bugs de PNJs qui subissent des dégats de chute lorsque les deux cells sont à des altitudes différentes et la difficulté de les corriger; dans Morrowind, bon nombre de cells intérieures ont été négligemment créées à l'altitude z = 16000, au lieu de respecter la cohérence entre l'intérieur et l'extérieur.


D'après MsfD, CellChanged n'est pas viable à 100% pour détecter un changement de cell (par exemple avec les téléportations scriptées).
''Malheureusement, CellChanged n'est pas fiable à 100% pour détecter un changement de cell''. Il a été prouvé que CellChanged ne rend pas 1 dans les cas suivants :
 
<br>
- téléportation par rappel, intervention d'Almsivi ou intervention divine
<br>
- téléportation par la commande PositionCell/Position
<br>
- quand la porte qui ouvre sur la cell est scriptée et où le script appelle un OnActivate pour téléporter dans la cell en question (info forum officiel)


====CreateMaps====
====CreateMaps====
Ligne 161 : Ligne 177 :
Ces fonctions sont Tribunal requis.
Ces fonctions sont Tribunal requis.


Le script ''TribunalMain'' est buggué en ce sens qu'il cause des problèmes collatéraux, par exemple il rétablit la téléportation dans la chambre de l'Akhulakhan. Il cause aussi des problèmes dans les mods qui utilisent ces commandes. Il existe un script corrigé fonctionnel qui a été appliqué par exemple dans Kalendaar et par le PnoGoty.
Le script ''TribunalMain'', qui est un script global tournant en permanence et qui interdit la lévitation à Longsanglot et la lévitation et la téléportation dans la cité de Sotha Sil, est buggué en ce sens qu'il cause des problèmes collatéraux, par exemple il rétablit la téléportation dans la chambre de l'Akhulakhan. Il cause aussi des problèmes dans les mods qui utilisent ces commandes. Il existe un script corrigé fonctionnel qui a été appliqué par exemple dans Kalendaar et par le PnoGoty.


====Equip====
====Equip====
Ligne 176 : Ligne 192 :
       '''ref->Face x y'''
       '''ref->Face x y'''


''Face x y'' fait se tourner un PNJ dans la direction de coordonnées x y dans la cell courante.  
''Face x y'' fait se tourner un PNJ dans la direction de coordonnées x y dans la cell courante. Face semble admettre des variables en argument.


Il semble qu'essayer d'appeler cette commande continûment (par exemple, pour faire suivre du regard un objet se déplaçant), ou même à intervalles réguliers, fonctionne assez mal.
Il semble qu'essayer d'appeler cette commande continûment (par exemple, pour faire suivre du regard un objet se déplaçant), ou même à intervalles réguliers, fonctionne assez mal.


====FixMe====
====FixMe====
Ligne 192 : Ligne 207 :
====GameHour====
====GameHour====


Variable globale qui est de type float (c'est à dire que s'il est midi et quart, gamehour ne retournera pas 12 mais 12.25, il faut donc en tenir compte dans les scripts d'emploi du temps; ''if ( gamehour == 12 )'' est par exemple, un type de condition à proscrire absolument car jamais vrai en rpatique ).
Variable globale qui est de type float (c'est à dire que s'il est midi et quart, gamehour ne retournera pas 12 mais 12.25, il faut donc en tenir compte dans les scripts d'emploi du temps; ''if ( gamehour == 12 )'' est par exemple, un type de condition à proscrire absolument car jamais vraie en pratique ).
 
Un autre piège à éviter est l'incrémentation de cette variable pour simuler un écoulement de temps. En effet, si Gamehour dépasse 24 d'une quelconque quantité (que ce soit 24.001 ou 1234), Gamehour est réinitialisé à 0 (et la globale Day est incrémentée de 1). Ainsi un ''( set GameHour to Gamehour + 5 )'' ne produira qu'un écoulement de temps d'une heure s'il est déjà 23 heures.


Un autre piège à éviter est relatif à l'incrémentation de cette variable pour simuler un écoulement de temps. En effet, si Gamehour dépasse 24 d'une quelconque quantité (que ce soit 24.001 ou 1234), Gamehour est réinitialisé à 0 (et la globale Day est incrémentée de 1). Ainsi un ''set GameHour to ( Gamehour + 5 )'' ne produira qu'un écoulement de temps d'une heure s'il est déjà 23 heures.


====GetHealthGetRatio====
====GetHealthGetRatio====
Ligne 204 : Ligne 218 :


Attention, dans tout Morrowind, il est adopté la convention que 0/0 = 1 (ce qui permet d'ailleurs de beaux cheats basé sur le remplissage de la barre de magie par des réductions/atténuations d'intelligence jusqu'à atteindre 0, puis en revenant à la normale).  
Attention, dans tout Morrowind, il est adopté la convention que 0/0 = 1 (ce qui permet d'ailleurs de beaux cheats basé sur le remplissage de la barre de magie par des réductions/atténuations d'intelligence jusqu'à atteindre 0, puis en revenant à la normale).  
Si un PNJ voit sa santé actuelle ET sa santé de départ réduites à 0, par exemple par un SetHealth 0, GethealthGetRatio retournera donc par la suite 1. Il est donc extrêmement peu recommandable d'utiliser un GethealthGetRatio pour déterminer si un PNJ est mort ou non, et même, pour cette raison, il est préférable d'éviter cette commande, à moins de prévoir explicitement ce cas, par exemple en écrivant une boucle préalable court-circuitant le script si GetHealth retourne une valeur <= 0.
Si un PNJ voit sa santé actuelle ET sa santé de départ réduites à 0, par exemple par un SetHealth 0, GethealthGetRatio retournera donc par la suite 1. Il est donc extrêmement peu recommandable d'utiliser un GethealthGetRatio pour déterminer si un PNJ est mort ou non, et même, pour cette raison, il est préférable d'éviter cette commande, à moins de prévoir explicitement ce cas, par exemple en écrivant une boucle préalable court-circuitant le test si GetHealth retourne une valeur <= 0.
 


====GetPCCell====  
====GetPCCell====  
Ligne 213 : Ligne 226 :
Retourne 1 si le PJ se trouve dans une cell dont le nom commence par cell_id, et 0 sinon. Par exemple, si le PJ se trouve dans la cell « Vivec, temple », GetPCCell « Vivec » va donc retourner 1.
Retourne 1 si le PJ se trouve dans une cell dont le nom commence par cell_id, et 0 sinon. Par exemple, si le PJ se trouve dans la cell « Vivec, temple », GetPCCell « Vivec » va donc retourner 1.


Pour que le TESCS accepte de compiler la commande, il faut cependant que la cell cell_id existe vraiment. C'est pourquoi on trouve dans le tescs des cells quasi-vides et inaccessibles dans le jeu nommées « Longsanglot », « Sotha Sil », etc...
Pour que le TESCS accepte de compiler la commande, il faut cependant que la cell cell_id existe vraiment. C'est pourquoi on trouve dans les fichiers esm du jeu des cells quasi-vides et inaccessibles dans le jeu nommées « Longsanglot », « Sotha Sil », etc...
 


====GetPCVisionBonus====
====GetPCVisionBonus====
Ligne 220 : Ligne 232 :
Commande console fonctionnant également dans les scripts. Elle retourne une variable de type float comprise entre -1 (aveugle complet, écran noir) et 1 (vision nocturne maximale). Le PJ non affecté par aveuglement ou vision nocturne a un GetPCVisionBonus de 0. 100 points de vision nocturne correspond à GetPCVisionBonus = 1. Au-delà, les points ne sont plus décomptés, ce qui peut occasionner des bugs et un aveuglement temporaire du joueur en combinant plusieurs sorts (si vous lancez un sort donnant 50 points de vision nocturne pendant 30 secondes puis tout de suite après un sort donnant 100 points de vision nocturne pendant 60 secondes, vous êtes à GetPCVisionBonus = 1 les trente premières secondes, puis après la dissipation du premier sort qui vous ôte 50 points de vision, vous êtes à GetPCVisionBonus = 0.5 les trente secondes suivantes, en dépit du fait que vous soyez sous l'effet d'un sort de 100 points de vision nocturne)
Commande console fonctionnant également dans les scripts. Elle retourne une variable de type float comprise entre -1 (aveugle complet, écran noir) et 1 (vision nocturne maximale). Le PJ non affecté par aveuglement ou vision nocturne a un GetPCVisionBonus de 0. 100 points de vision nocturne correspond à GetPCVisionBonus = 1. Au-delà, les points ne sont plus décomptés, ce qui peut occasionner des bugs et un aveuglement temporaire du joueur en combinant plusieurs sorts (si vous lancez un sort donnant 50 points de vision nocturne pendant 30 secondes puis tout de suite après un sort donnant 100 points de vision nocturne pendant 60 secondes, vous êtes à GetPCVisionBonus = 1 les trente premières secondes, puis après la dissipation du premier sort qui vous ôte 50 points de vision, vous êtes à GetPCVisionBonus = 0.5 les trente secondes suivantes, en dépit du fait que vous soyez sous l'effet d'un sort de 100 points de vision nocturne)


====Position, PositionCell====
      '''ref->Position x y z zrot'''
      '''ref->PositionCell x y z zrot cell_id'''
''PositionCell'' place la référence concernée aux coordonnées x y z de la cell cell_id et avec la rotation autour de l'axe z d'angle zrot. Dans la commande ''Position'', il n'y a pas de mention de la cell, cette commande fonctionne partout en extérieur, et peut téléporter à l'intérieur de la cell intérieure courante sans mention de celle-ci. ''PositionCell'' fonctionne aussi bien en extérieur qu'en intérieur et permet de téléporter d'une cell intérieure à une cell extérieure et vice-versa.
Le paramètre zrot fonctionne aussi bien pour les PNJs que pour le PJ et les objets, mais il s'exprime en degrés pour les objets et le PJ, et en minutes d'arc pour les PNJs et les statics (1 degré = 60 minutes d'arc, donc 90°=5400 minutes).
''Position'' peut créér des bugs graphiques sur les compagnons ainsi téléportés. Il est donc recommandable d'utiliser ''PositionCell'' dans ce cas.
D'après MSfD, utiliser ''PositionCell'' dans la case result d'un dialogue fonctionne mal et peut causer des crashes. Il est recommandé de lancer un script de téléportation à la place.


==== Get/Mod/Set ====
==== Get/Mod/Set ====
Ligne 354 : Ligne 353 :


       Scale (the 3D scale of the object)
       Scale (the 3D scale of the object)
====[[TESCS : StopSound|StopSound]]====
      '''ref->StopSound sound_id'''
Arrête un son joué en boucle lancé par un PlayLoopSound3D ou PlayLoopSound3DVP. Ne fonctionne pas sur un son lancé seulement par un PlaySound.
==Coût comparé des différentes fonctions de script==
Les différents tests et fonctions de script ont été testés en utilisant le script suivant (cette méthologie n'est pas sans défaut, principalement à cause de l'utilisation de getSecondesPassed et de boucles while, mais a le mérite de pouvoir comparer entre elles les différentes fonctions de scripting du point de vue de leur coût d'exécution)
Begin test_cout_compare
long nombre_un
long compte
float timer
if ( timer < 30 )
while ( compte < n )
'''[instructions]'''
endwhile
set nombre_un to ( nombre_un + 1 )
set compte to 0
else
MessageBox "N1 = %g" nombre_un
stopscript Test_cout_compare
endif
set timer to ( timer + Getsecondspassed )
end
Les résultats expérimentaux suivants portent sur la valeur de n (nombre de boucles par frame) dans le script ci-dessus lorsqu'on place diverses fonctions dans le bloc [instructions]. Cette valeur de n est choisie telle que le script entraîne exactement une baisse de 10% du framerate dans une cell de test pratiquement vide. Il fait sens que la valeur de n en elle-même n'a pas de signification, celle-ci dépendant de paramètres externes (quantité de scripts et de mods ajoutés, configuration matérielle, etc.) : seules sont significatives les différences de performance entre les différentes instructions testées. La marge d'erreur sur ces valeurs est d'environ 1 à 2%.
{|border="1" ! Fonction de script !! Valeur seuil pour n
|-
|Boucle vide (pas d'instruction, cela représente donc le coût intrinsèque du script et de sa boucle while) || n = 495
|-
|set || 440
|-
|moveworld || 440
|-
| If ( GetDisabled ) || 310
|-
|if (test d'une variable si le comparant est un petit nombre) || 300
|-
| while || 300
|-
| If ( CellChanged ) || 295
|-
| If ( GetStandingActor ) || 295
|-
| If ( MenuMode ) || 290
|-
| If ( GetPCSneaking ) || 290
|-
| If ( GetPCSleep ) || 290
|-
| If ( SayDone ) || 290
|-
| If ( GetPos x ) || 290
|-
| If ( GetWaterLevel ) || 290
|-
| If ( GetCurrentWeather ) || 290 
|-
| If ( GetSpellReadied ) || 285
|-
| If ( GetWeaponDrawn ) || 285
|-
| If ( GetPCVisionBonus ) || 280
|-
| If ( GetSoundPlaying id ) || 280
|-
| If ( GetDeadCount id ) || 280
|-
| If ( OnDeath ) || 280
|-
| If ( OnActivate ) || 280
|-
| If ( ScriptRunning id ) || 275 
|-
| If ( GetItemCount id ) || 275
|-
| If ( GetScale ) || 275
|-
| If ( GetRace race_id ) || 275
|-
| If ( GetLos id ) || 270
|-
| If ( GetJournalIndex ) || 265
|-
| If ( GetPCCell ) || 260
|-
| If ( HasItemEquipped ) || 260
|-
| Une boucle if avec dix elseif, le premier test étant vérifié || 255
|-
| If ( GetDistance ) || 250
|-
| If ( GetSpellEffects ) || 250
|-
| If ( HasSoulGem ) || 250 
|-
| if (si le comparant est une valeur à six chiffres) || 250
|-
|rotateworld || 200
|-
|setscale || 105
|-
|removespell || < 3
|-
|MessageBox || < 1
|-
|removeitem || < 1
|-
|Journal ||< 1
|}
Quelques remarques et conséquences :
- if est une commande relativement lourde, beaucoup plus lourde en général que la fonction utilisée dans la condition de ce if; une boucle if testant une variable vaut environ cinq affectations par set de cette même variable.
- le test d'une inégalité a à peu près le même coût que le test d'une égalité
- une boucle if .../ elseif... /elseif/ ... /else / endif est un peu plus cher qu'un simple if
si le premier test est vrai; il est également très légèrement moins coûteux qu'une série équivalente de if... endif si tous les tests sont parcourus, mais le coût est très similaire. Les tests sont parcourus dans le sens d'écriture du script et ceux qui suivent la condition vérifiée ne sont pas exécutés (il est donc recommandable de placer les tests qui ont le plus de chances d'être vérifiés en tête de la pile de test)
- tester des float, des long ou des short a à peu près le même coût


==Exe externes==
==Exe externes==

Version du 29 mai 2018 à 04:55



Tutoriels de références

Avant d'aller plus loin, les documents suivants sont l'alpha et l'oméga pour tout scripteur :


en français :

  • Morrowind Scripting for Dummies 8ème édition (en abrégé, MSfD) Manuel essentiel et quasiment exhaustif qui commence par expliquer les bases avant de couvrir toutes les commandes, les difficultés et les demandes les plus courantes des scripteurs, y compris le scripting avancé, avec de nombreux exemples; recommandable du novice au scripteur chevronné.


en anglais :

  • Liste des commandes sur UESP la liste complète des commandes de scripting par ordre alphabétique, avec leur syntaxe, leur description et les subtilités d'utilisation. Le guide de référence, même si on y trouve occasionnellement des erreurs et des imprécisions (MSfD constitue souvent une source plus fiable).

Concepts de base

Objets, références, variables

Objets et références

Un objet est un élément défini dans le TESCS par un nom précis que l'on nommera id. Une référence est une instance d'un objet, placée dans le jeu. A une seule id peut donc correspondre plusieurs références placées dans le jeu, par exemple les gardes sont des copies d'un même PNJ, ils ont même id et donc mêmes caractéristiques.

Les commandes de script s'appliquent souvent à des éléments du jeu. Ces éléments sont placés dans le jeu, ce sont donc des références. Mais comme les références n'ont pas de nom en propre, la syntaxe du scripting utilise leurs id pour les nommer, ce qui occasionne bien des problèmes et des confusions.

La plupart du temps, ces commandes de script ne fonctionnent que s'il n'y a pas d'ambiguïté entre id et référence, c'est à dire qu'à une id correspond une unique référence placée dans le jeu. Par exemple, player est l'id du joueur, dont il n'existe qu'une référence associée placée dans le jeu. Player est donc valable pour désigner une référence.


Variables

Une variable est un espace de stockage pour un résultat. Dans Morrowind, une variable est toujours un nombre réel (en pratique, un nombre décimal compris dans une certaine fourchette) ou un nombre entier, Morrowind ne supportant que trois types de variables.

Ces types sont :

- short : un entier compris entre -32768 et 32767

- long : un entier compris entre -2147483648 et 2147483647

- float : un flottant avec au maximum 7 décimales, compris entre -3,4.10^38 et +3,4.10^38


Une variable peut être locale (c'est à dire interne à un script, qu'il soit global ou local) ou globale (utilisable et accessible partout).

Toute variable, locale ou globale, doit être déclarée; dans le TESCS (gameplay->globals) pour les globales et au début du script concerné pour les locales.

Certaines fonctions acceptent des variables en paramètres, d'autres non et n'acceptent que des valeurs numériques. L'extension Tribunal puis MWSE ont augmenté le nombre de commandes de script acceptant des variables.

Les différents types de script

Il existe trois types de scripts :


- le script local, attaché à un objet. Chaque instance de cet objet placée dans le jeu possèdera son propre script local qui tournera de façon indépendante. De cette façon, un tel script permet de travailler directement avec cette référence en évitant les problèmes de confusions entre id et référence.

Un script local ne s'exécute que si le PJ se trouve dans la cellule où est placée sa référence, en intérieur, plus les huit cellules adjacentes, en extérieur.


- le script global est en général lancé par une commande startscript. Une seule instance de ce script s'exécute à la fois et il fonctionne en permanence jusqu'à ce qu'il soit arrêté par une commande stopscript.


- le script ciblé (targeted script) est un hybride entre script local et script global. C'est un script que l'on lance sur une référence à l'aide d'une commande startscript. Il est donc lié à une référence, mais il fonctionne en permanence sans restriction de cellule. Il est possible de lancer plusieurs scripts ciblés sur une même référence, y compris simultanément et même si celle-ci possède déjà un script attaché à son id dans le TESCS, mais il semblerait que lancer simultanément un même script sur différentes références puisse causer des problèmes.

Limitations du scripting

- la profondeur maximale de boucles if imbriquées est de 10

- il y a une limite au nombre de caractères du script (environ 32000) et une limite du nombre de lignes (autour de 1000, probablement 1024 lignes une fois le script compilé)

- il y a une limite de 255 commandes compilées dans une boucle if... endif (info Galsiah; concrètement, cela se traduit par un nombre de lignes limite dans une boucle)

- il y a une limite au nombre de conditions if elseif dans un script.

- la 34e variable locale de même type déclarée dans un script n'est pas prise en compte.

- le bug des 72 heures et la recharge d'une sauvegarde affectent le scripting. Notamment, si les variables locales d'un script local ne sont jamais remises à zéro, en revanche les variables locales d'un script global sont remises à zéro après 72 heures si le script n'a pas été lancé au cours de la session de jeu courante. D'après MSfD 9, les scripts ciblés peuvent également perdre ainsi leur cible.

Liste alphabétique détaillée et commentée des commandes de scripts

Pour la liste complète des fonctions, voir : TESCS : Liste des commandes de script

La liste ci-après sera transférée progressivement dans la liste précédente.


Dans toute la suite :

  • ref désignera une référence
  • <type>_id l'id d'un élément de type <type> (par exemple cell_id désigne l'id d'un élément de type cell, sort_id l'id d'un sort, etc.)
  • n un nombre de type short, éventuellement une variable
  • x y z un nombre de type float, éventuellement une variable


AddItem

     ref->AddItem objet_id n

Permet d'ajouter n copies d'un objet d'id objet_id à l'inventaire de la référence ref. D'après MSfD, Additem admet des variables en argument, mais seulement dans les résultats de dialogues et si la variable utilisée n'est pas modifiée dans le même résultat de dialogue.

Cette commande est la seule façon d'affecter une référence dans une cellule encore non visitée, au contraire des commandes de type Set<attribute>, Mod<attribute>, qui ne fonctionnent pas dans ce cas.

AiTravel

     ref->AiTravel x y z


Permet à la référence ref de se rendre aux points de coordonnées x y z dans la cell courante. Par conséquent ceci fonctionne aussi bien en extérieur qu'en intérieur puisque l'id de la cellule n'est pas mentionné.

AiTravel n'accepte pas les variables en argument, même avec Tribunal; cet écueil n'est surmontable qu'avec MWSE (voir la commande xAiTravel)

La présence ou non d'un pathgrid a un certain effet sur cette commande. Lorsqu'un pathgrid existe et permet à la référence en question de se rendre au point x y z, alors s'il n'y pas d'obstacle entre le PNJ et le point d'arrivée, le PNJ s'y rend en ligne droite en empruntant le chemin le plus court; sinon le PNJ se rend au point désiré en empruntant les lignes du pathgrid. S'il n'y a pas de pathgrid, la référence essaie toujours de se rendre au point x y z en ligne droite. S'il y a un obstacle entre le point de départ et le point d'arrivée, la référence aura alors tendance à warper, c'est à dire à disparaître lorsqu'elle rencontre l'obstacle pour réapparaître immédiatement au point x y z.

S'il n'y a pas de difficulté majeure en intérieur, en extérieur le franchissement de bordure d'une cellule pose par contre de gros problèmes. Si l'on veut éviter à la référence de warper, il faut conduire par un AiTravel la référence jusqu'au début de la cellule voisine – mais pas plus loin - puis appeler un autre AiTravel. Cette procédure est délicate et nécessite des tests in game soigneux.

CellChanged

Retourne 1 à la frame où le PJ entre dans une nouvelle cell, 0 le reste du temps.

Attention, lors de cette frame, le PNJ compagnon suiveur se trouve toujours dans la cell précédente. Ce n'est qu'ensuite qu'il est téléporté dans la nouvelle cell,d'où les bugs de PNJs qui subissent des dégats de chute lorsque les deux cells sont à des altitudes différentes et la difficulté de les corriger; dans Morrowind, bon nombre de cells intérieures ont été négligemment créées à l'altitude z = 16000, au lieu de respecter la cohérence entre l'intérieur et l'extérieur.

Malheureusement, CellChanged n'est pas fiable à 100% pour détecter un changement de cell. Il a été prouvé que CellChanged ne rend pas 1 dans les cas suivants :
- téléportation par rappel, intervention d'Almsivi ou intervention divine
- téléportation par la commande PositionCell/Position
- quand la porte qui ouvre sur la cell est scriptée et où le script appelle un OnActivate pour téléporter dans la cell en question (info forum officiel)

CreateMaps

     CreateMaps "Morrowind.esm"

Commande console permettant permet de créer une carte des cells extérieurs du jeu, prenant en compte tous les esm et esp chargés.

Pour pouvoir utiliser cette commande, il convient de changer la ligne Create Maps Enable=0 en : Create Maps Enable=2 dans la section [General] de Morrowind.ini, et de créer un répertoire Maps dans le répertoire Morrowind; puis de l'appeler en cours de jeu à la console par CreateMaps "Morrowind.esm". Le processus charge successivement toutes les cells du jeu et peut s'avérer très long, souvent plus d'une heure. Le résultat est produit sous forme d'images au format bmp (une image par cell, de format 256x256, pesant chacune 193 ko). Morrowind compte environ un millier de cells extérieures.

DisableTeleporting

DisableLevitation

DisableTeleporting désactive la téléportation dans la cell courante (dans toutes les cells extérieures si appelé en extérieur). Cela s'applique aux interventions d'almsivi, intervention divine, marque et rappel, mais pas aux commandes scriptées telles que positioncell, ou aux objets scriptés de cette façon tels que la bague absconse de Barilzar.

DisableLevitation interdit toute lévitation selon les mêmes conditions.

Ces fonctions sont Tribunal requis.


EnableTeleporting

EnableLevitation

Permet d'autoriser à nouveau la lévitation/la téléportation après que celle-ci a été désactivée par un DisableLevitation/Disableteleporting.

Ces fonctions sont Tribunal requis.

Le script TribunalMain, qui est un script global tournant en permanence et qui interdit la lévitation à Longsanglot et la lévitation et la téléportation dans la cité de Sotha Sil, est buggué en ce sens qu'il cause des problèmes collatéraux, par exemple il rétablit la téléportation dans la chambre de l'Akhulakhan. Il cause aussi des problèmes dans les mods qui utilisent ces commandes. Il existe un script corrigé fonctionnel qui a été appliqué par exemple dans Kalendaar et par le PnoGoty.

Equip

ref->Equip objet_id

Permet de faire équiper un objet à la référence ref, le PJ, un PNJ ou une créature. Commande bugguée avec Morrowind seul, provoquant le message d'erreur habituel« compile and run error ». Réparée avec Tribunal et versions ultérieures. Equip est donc Tribunal requis de facto et rend tout mod l'employant Tribunal requis pour fonctionner correctement.


Face

     ref->Face x y

Face x y fait se tourner un PNJ dans la direction de coordonnées x y dans la cell courante. Face semble admettre des variables en argument.

Il semble qu'essayer d'appeler cette commande continûment (par exemple, pour faire suivre du regard un objet se déplaçant), ou même à intervalles réguliers, fonctionne assez mal.

FixMe

Commande console permettant de déplacer un PNJ.

FixMe peut être utilisé dans les scripts.

FixMe est une commande indispensable si l'on fait des teleports par setpos en extérieur. En effet, si l'on enregistre la position initiale du PJ par des getpos x/y/z, qu'on le téléporte dans une autre cell, puis qu'on le ramène au point de départ par setpos, la cell initiale n'est plus chargée en mémoire, ce qui cause un beau trou dans le paysage et un joli bug graphique. Ajouter un FixMe dans le script après le SetPos de retour permet de recharger la cell en question et donc d'éviter cet écueil.


GameHour

Variable globale qui est de type float (c'est à dire que s'il est midi et quart, gamehour ne retournera pas 12 mais 12.25, il faut donc en tenir compte dans les scripts d'emploi du temps; if ( gamehour == 12 ) est par exemple, un type de condition à proscrire absolument car jamais vraie en pratique ).

Un autre piège à éviter est relatif à l'incrémentation de cette variable pour simuler un écoulement de temps. En effet, si Gamehour dépasse 24 d'une quelconque quantité (que ce soit 24.001 ou 1234), Gamehour est réinitialisé à 0 (et la globale Day est incrémentée de 1). Ainsi un set GameHour to ( Gamehour + 5 ) ne produira qu'un écoulement de temps d'une heure s'il est déjà 23 heures.

GetHealthGetRatio

ref->GetHealthGetRatio

GetHeathGetRatio retourne un float compris entre 0 et 1 qui correspond au quotient santé actuelle/santé de départ de la référence ref.

Attention, dans tout Morrowind, il est adopté la convention que 0/0 = 1 (ce qui permet d'ailleurs de beaux cheats basé sur le remplissage de la barre de magie par des réductions/atténuations d'intelligence jusqu'à atteindre 0, puis en revenant à la normale). Si un PNJ voit sa santé actuelle ET sa santé de départ réduites à 0, par exemple par un SetHealth 0, GethealthGetRatio retournera donc par la suite 1. Il est donc extrêmement peu recommandable d'utiliser un GethealthGetRatio pour déterminer si un PNJ est mort ou non, et même, pour cette raison, il est préférable d'éviter cette commande, à moins de prévoir explicitement ce cas, par exemple en écrivant une boucle préalable court-circuitant le test si GetHealth retourne une valeur <= 0.

GetPCCell

GetPCCell cell_id

Retourne 1 si le PJ se trouve dans une cell dont le nom commence par cell_id, et 0 sinon. Par exemple, si le PJ se trouve dans la cell « Vivec, temple », GetPCCell « Vivec » va donc retourner 1.

Pour que le TESCS accepte de compiler la commande, il faut cependant que la cell cell_id existe vraiment. C'est pourquoi on trouve dans les fichiers esm du jeu des cells quasi-vides et inaccessibles dans le jeu nommées « Longsanglot », « Sotha Sil », etc...

GetPCVisionBonus

Commande console fonctionnant également dans les scripts. Elle retourne une variable de type float comprise entre -1 (aveugle complet, écran noir) et 1 (vision nocturne maximale). Le PJ non affecté par aveuglement ou vision nocturne a un GetPCVisionBonus de 0. 100 points de vision nocturne correspond à GetPCVisionBonus = 1. Au-delà, les points ne sont plus décomptés, ce qui peut occasionner des bugs et un aveuglement temporaire du joueur en combinant plusieurs sorts (si vous lancez un sort donnant 50 points de vision nocturne pendant 30 secondes puis tout de suite après un sort donnant 100 points de vision nocturne pendant 60 secondes, vous êtes à GetPCVisionBonus = 1 les trente premières secondes, puis après la dissipation du premier sort qui vous ôte 50 points de vision, vous êtes à GetPCVisionBonus = 0.5 les trente secondes suivantes, en dépit du fait que vous soyez sous l'effet d'un sort de 100 points de vision nocturne)


Get/Mod/Set

Caractéristiques


     Agility
     Endurance
     Intelligence
     Luck
     Personality
     Speed
     Strength
     Willpower


Compétences

Combat


     Armorer
     Athletics
     Axe
     Block
     BluntWeapon
     HandToHand
     HeavyArmor
     LightArmor
     LongBlade
     Marksman
     MediumArmor
     ShortBlade
     Spear
     Unarmored

Discrétion


     Mercantile
     Security
     Sneak
     Speechcraft


Magie


     Alchemy
     Alteration
     Conjuration
     Destruction
     Enchant
     Illusion
     Mysticism
     Restoration


Barres d'état


     Fatigue
     Health
     Magicka


Résistances


     ResistMagicka
     ResistFire
     ResistFrost
     ResistShock
     ResistDisease
     ResistBlight
     ResistCorprus
     ResistPoison
     ResistParalysis
     ResistNormalWeapons

Effets modificateurs actifs


     ArmorBonus
     AttackBonus
     DefendBonus
     Chameleon
     Invisible
     Flying
     SwimSpeed
     SuperJump
     WaterBreathing
     WaterWalking
     Blindness
     CastPenalty
     Paralysis
     Silence


Stats générales


     Level (only works with Set and Get)
     Reputation
     PCrimeLevel (PC Only)


IA


     Alarm (Changing this changes it for ALL references of the Actor)
     Fight (Changing this changes it for ALL references of the Actor)
     Flee (Changing this changes it for ALL references of the Actor)
     Hello (Changing this changes it for ALL references of the Actor)

Divers


     Scale (the 3D scale of the object)

StopSound

     ref->StopSound sound_id

Arrête un son joué en boucle lancé par un PlayLoopSound3D ou PlayLoopSound3DVP. Ne fonctionne pas sur un son lancé seulement par un PlaySound.

Coût comparé des différentes fonctions de script

Les différents tests et fonctions de script ont été testés en utilisant le script suivant (cette méthologie n'est pas sans défaut, principalement à cause de l'utilisation de getSecondesPassed et de boucles while, mais a le mérite de pouvoir comparer entre elles les différentes fonctions de scripting du point de vue de leur coût d'exécution)


Begin test_cout_compare
long nombre_un
long compte
float timer
if ( timer < 30 )
while ( compte < n )
[instructions]
endwhile
set nombre_un to ( nombre_un + 1 )	
set compte to 0
else
MessageBox "N1 = %g" nombre_un
stopscript Test_cout_compare
endif
set timer to ( timer + Getsecondspassed )
end

Les résultats expérimentaux suivants portent sur la valeur de n (nombre de boucles par frame) dans le script ci-dessus lorsqu'on place diverses fonctions dans le bloc [instructions]. Cette valeur de n est choisie telle que le script entraîne exactement une baisse de 10% du framerate dans une cell de test pratiquement vide. Il fait sens que la valeur de n en elle-même n'a pas de signification, celle-ci dépendant de paramètres externes (quantité de scripts et de mods ajoutés, configuration matérielle, etc.) : seules sont significatives les différences de performance entre les différentes instructions testées. La marge d'erreur sur ces valeurs est d'environ 1 à 2%.

Boucle vide (pas d'instruction, cela représente donc le coût intrinsèque du script et de sa boucle while) n = 495
set 440
moveworld 440
If ( GetDisabled ) 310
if (test d'une variable si le comparant est un petit nombre) 300
while 300
If ( CellChanged ) 295
If ( GetStandingActor ) 295
If ( MenuMode ) 290
If ( GetPCSneaking ) 290
If ( GetPCSleep ) 290
If ( SayDone ) 290
If ( GetPos x ) 290
If ( GetWaterLevel ) 290
If ( GetCurrentWeather ) 290
If ( GetSpellReadied ) 285
If ( GetWeaponDrawn ) 285
If ( GetPCVisionBonus ) 280
If ( GetSoundPlaying id ) 280
If ( GetDeadCount id ) 280
If ( OnDeath ) 280
If ( OnActivate ) 280
If ( ScriptRunning id ) 275
If ( GetItemCount id ) 275
If ( GetScale ) 275
If ( GetRace race_id ) 275
If ( GetLos id ) 270
If ( GetJournalIndex ) 265
If ( GetPCCell ) 260
If ( HasItemEquipped ) 260
Une boucle if avec dix elseif, le premier test étant vérifié 255
If ( GetDistance ) 250
If ( GetSpellEffects ) 250
If ( HasSoulGem ) 250
if (si le comparant est une valeur à six chiffres) 250
rotateworld 200
setscale 105
removespell < 3
MessageBox < 1
removeitem < 1
Journal < 1


Quelques remarques et conséquences :

- if est une commande relativement lourde, beaucoup plus lourde en général que la fonction utilisée dans la condition de ce if; une boucle if testant une variable vaut environ cinq affectations par set de cette même variable.

- le test d'une inégalité a à peu près le même coût que le test d'une égalité

- une boucle if .../ elseif... /elseif/ ... /else / endif est un peu plus cher qu'un simple if si le premier test est vrai; il est également très légèrement moins coûteux qu'une série équivalente de if... endif si tous les tests sont parcourus, mais le coût est très similaire. Les tests sont parcourus dans le sens d'écriture du script et ceux qui suivent la condition vérifiée ne sont pas exécutés (il est donc recommandable de placer les tests qui ont le plus de chances d'être vérifiés en tête de la pile de test)

- tester des float, des long ou des short a à peu près le même coût

Exe externes

Morrowind Enhanced (MWE)

Morrowind Scripting Extender (MWSE)

Page du projet libre MWSE

Télécharger MWSE (version 0.94)

Morrowind Graphic Extender (MGE)