Tout permuter
Les scripts sont utilisés pour les événements dans les maps, les armes, et l'intelligence artificielle. Cette section ne couvre que les scripts utilisés par les maps de Doom 3.
(Les scripts de GUI [Graphic User Interface] sont une catégorie à part entière et sont couverts dans une autre page. )
Notes générales
- Les scripts d'entité ne sont pas rechargés automatiquement avec la commande
map
. On doit utiliser la commandereloadScript
et ensuite recharger la map. - La syntaxe du langage étant très près de celle du C++, les commentaires à ligne unique commencent avec le texte
//
; les commentaires multilignes commencent avec/*
et se terminent avec*/
. - Selon toute apparence, le fichier doom_events.script contient toutes les fonctions de script nécessitant des fonctions dans le code C++, par opposition aux fonctions déclarées et définies dans les fichiers .script.
- Les noms des fonctions de script sont sensibles à la casse.
- Certaines fonctions de script ont des propriétés équivalentes dans DarkRadiant. Par exemple, la fonction
bind()
, qui apparente une entité à une autre (et lui fera donc suivre les mouvements de l'autre entité) peut être utilisée dans DR en ajoutant la propriétébind
, avec comme valeur le nom de l'entité parente. - L'entité
$world
représente toutes les brush (les murs et les planchers créés dans Dark Radiant) d'un niveau. - Dans les scripts d'intelligence artificielle, la fonction
init()
d'une entité (qui agit comme un constructeur C++) est polymorphique ; par exemple, la fonctionmonster_zombie::Init()
(ai_monster_zombie.script) appelle la fonction du même nom qui appartient à l'objetmonster_base
(duquel l'objetmonster_zombie
est dérivé). - Pour accéder à un membre (x, y ou z) d'un vecteur (objet
vector
), on écrit le nom du vecteur suivi d'un trait souligné et la lettre qui correspond à la coordonnée :vector origine = $player1.getWorldOrigin();
sys.print( "Origine : ("+ origine_x +", "+ origine_y +", "+ origine_z +")" ); - Quand on déclare une nouvelle fonction dans un script, ne pas lui donner le même nom qu'une fonction déjà existante ; la nouvelle fonction ne sera pas reconnue par le jeu. Exemple : Pour une fonction qui utilise la fonction système
fadeOut
, lui donner un nom distinct :void monScript_fadeOut() // Fonctionnel, car les fonctions ont des noms différents
{
sys.fadeOut('0 0 0', 10);
}et non :
void fadeOut() // Non fonctionnel, car il y a un conflit entre les fonctions
{
sys.fadeOut('0 0 0', 10);
} -
La fonction waitAction() dans les scripts de personnages
La fonction de script
waitAction( string )
met le script en pause jusqu'à ce que la fonctionfinishAction( string )
soit appelée avec la même chaîne en paramètre.Par exemple, dans la fonction
monster_base::sight_enemy()
:animState( ANIMCHANNEL_TORSO, "Torso_Sight", 4 );
waitAction( "sight" );Explication détaillée de toutes les étapes nécessaires pour jouer l'animation :
La fonction
animState()
est reliée à une fonction C++, idActor::Event_AnimState(). Cette fonction appelle idActor::SetAnimState(), qui appelle idAnimState::SetState().Cette dernière trouve un pointeur vers la fonction de script
Torso_Sight
(représentée par un objet function_t* dans le C++) et exécute la fonction de scriptmonster_base::Torso_Sight()
. Enfin, celle-ci joue l'animation en question et avertit le script quand l'animation est terminée :finishAction( "sight" );
Fichiers importants
-
ai_monster_base.script
Le fichier de script de base pour tous les monstres. Certaines des méthodes de l'objet
monster_base
sont redéfinies par des sous-catégories de monstre.Propriété :
idle_sight_fov
Indique si le joueur doit être dans le champ de vision du monstre pour être repéré; si true, il peut être derrière le monstre sans l'alerter.
Cette propriété est initialisée avec la valeur true, mais prend la valeur false dans
monster_base::checkForEnemy()
. Conséquence : au départ, le monstre ne voit le joueur que si celui-ci est dans son champ de vision. Mais si le joueur se place tout juste derrière lui,idle_sight_fov
deviendra false et le monstre pourra ensuite voir le joueur n'importe où (sauf s'il est derrière un mur). -
ai_monster_zombie_base.script
Contient l'objet de base pour les zombis.
-
ai_monster_zombie.script
Contient le script qui est utilisé par plusieurs types de zombi comme
monster_zombie_fat
,monster_zombie_civilian
etmonster_zombie_chainsaw
, entre autres. -
doom_defs.script
Contient toutes les constantes qui sont utilisées dans les scripts, dont :
- les canaux d'animation (
ANIMCHANNEL_ALL
,ANIMCHANNEL_TORSO
,ANIMCHANNEL_LEGS
,ANIMCHANNEL_HEAD
,ANIMCHANNEL_EYELIDS
) - les directions de déplacement (
FORWARD
,LEFT
,RIGHT
,REL_LEFT
,REL_RIGHT
, etc.)
Il est possible de créer des macros dans ce fichier de la même façon qu'en C++. Par exemple, pour utiliser une fonction
_print()
qui agira commesys.println()
, ajouter cette ligne :#define _print( texte ) sys.println( texte )
- les canaux d'animation (
-
doom_events.script
Utile comme référence pour toutes les fonctions de script. Les commentaires expliquent quelles entités peuvent utiliser certaines fonctions.
Astuces
-
Fonctions utiles
Dans les exemples suivants,
$player1
est le nom de l'entité du joueur et$entite
est le nom d'une entité quelconque dans la scène (son type varie selon le contexte). Les fonctionssys
sont globales.$enfant.bind( $parent )
Apparenter une classe à une autre. Cette fonction peut aussi être utilisée comme propriété dans DarkRadiant (propriété : bind
; valeurentiteParent
)$entite.hide()
Enlever un objet de la scène. Cette méthode fera disparaître l'objet et enlèvera sa détection de collision $entite.playAnim ( animChannel, anim )
L'entité jouera l'animation voulue pour la partie du corps voulue. Par exemple : // Jouer l'animation
$player1.playAnim( ANIMCHANNEL_TORSO, "hard_land" );
// Attendre la fin de l'animation
sys.wait( $player1.animLength( ANIMCHANNEL_TORSO, "hard_land" ) );
// Rejouer l'animation d'inactivité
$player1.playAnim( ANIMCHANNEL_TORSO, "idle" );$entite.rotate( '90 0 0' )
Pour une entité func_rotating ou semblable : faire tourner un objet (90 degrés sur l'axe x à chaque seconde). Ne fonctionne pas avec l'entité du joueur ( player_doommarine ) $entite.rotateOnce( '90 0 0' )
Faire tourner un objet de façon permanente, à moins que decelTime
soit définie$player1.selectWeapon( "weapon_pistol" )
Équiper une arme. Les scripts pour les armes se trouvent sous pak000/script et pak008/script. Cette arme provient de pak000/script/weapon_pistol.script sys.firstPerson()
Caméra en première personne sys.print( "Voici un message.\n" )
Afficher un message dans la console sys.println( "Voici un message." )
Afficher un message avec un saut de ligne sys.setcvar( "nom_cvar", "valeur" )
Modifier une CVar sys.trigger( $entite )
« Active » une entité. Toutes les entités ne peuvent pas être activées ; certaines ne réagiront pas. Dans les autres cas, les résultats varient : les lumières peuvent être allumées et éteintes, et les moniteurs réagissent selon les guis qu'ils affichent. Note : lorsqu'on active une entité gui, toutes les entités dans la scène affichant le même gui seront activées. Il s'agit peut-être (mais pas certainement) d'un bogue.
-
Utiliser un script avec une map
- Créer un fichier .script, puis l'enregistrer dans le même dossier que la map, avec le même nomou
- Créer un fichier .script, puis l'enregistrer (avec un nom suivant le format :
map_nomDeLaMap
) dans le dossier base/script (la map sera dans base/maps) - Éditer pak000/script/doom_main.script pour ajouter le code suivant, avec le bon nom pour le script :
#include "script/map_nomDeLaMap.script"
- Créer un fichier .script, puis l'enregistrer (avec un nom suivant le format :
- Si une fonction doit être appelée au tout début du niveau, ouvrir la map dans DarkRadiant, ouvrir les propriétés de l'objet Worldspawn (sélectionner n'importe quelle brush statique qui compose le niveau et appuyer N), puis ajouter la propriété suivante (ne pas ajouter de parenthèses) :
call
:map_nomDeLaMap::maFonction
- Encapsuler tout le code du script dans l'espace de nom
map_nomDeLaMap
pour la sécurité (utiliser le mot-clénamespace
). Toutes les fonctions dans ce script seront créées dans cet espace de nom
- Créer un fichier .script, puis l'enregistrer dans le même dossier que la map, avec le même nomou
-
Ajouter une nouvelle fonction de script (événement) à une entité
Lorsqu'on appelle une fonction d'entité (comme
sys.print()
) dans un script, on fait en réalité référence à une fonction dans le code C++. Pour créer une nouvelle fonction dans le script, il faut donc la créer dans le code, puis l'enregistrer comme une fonction accessible par les scripts.Quelques notes avant de commencer :
- La classe utilisée pour cet exemple est idPlayer, la classe représentant l'entité du joueur.
- Le mot
chose
utilisé ci-bas représente ici le nom de l'événement (la fonction qui sera appelée). - Habituellement, lorsqu'on veut connaître les fonctions d'une entité, il suffit de regarder les événements enregistrés dans sa classe C++, c'est-à-dire les objets idEventDef qu'elle contient. Or, l'objet
sys
est un cas spécial : il faut passer par la classe idThread (neo/game/script/Script_Thread.cpp). C'est d'ailleurs cette classe qui se charge des événements pour idGameLocal ; par exemple, la fonction de scriptsetCamera()
appelle idThread::Event_SetCamera(), qui elle-même fait référence à idGameLocal::SetCamera().
- Créer dans le .h et le .cpp (ici, Player.h et Player.cpp) la fonction à laquelle l'événement fera référence. Dans ce cas, la fonction sera idPlayer::Event_Chose(). (La fonction n'a pas besoin de commencer par « Event_ », mais c'est la syntaxe habituelle pour des fonctions de script.)
- Dans Player.cpp :
- Créer l'événement en haut du fichier, avec les autres :
const idEventDef EV_Player_Chose( "chose" ); // Noter la casse de "chose"
On peut ajouter deux autres paramètres (const char *) optionnels : un pour spécifier le type des arguments que la fonction acceptera, et un autre pour le type d'objet qui sera retourné, s'il y a lieu. Quant aux valeurs que ces arguments peuvent prendre, le fichier neo/game/gamesys/Event.h en explique beaucoup :
"d"
: int"f"
: float"v"
: vecteur (idVec3)"s"
: string"e"
: entité (idEntity *)"E"
: entité nulle
On peut aussi faire des combinaisons pour les arguments acceptés :
"es"
, par exemple, signifie que la fonction prendra comme paramètres une entité et une string.Note importante : Dans le C++, la plupart des fonctions de script (sinon toutes) sont de type void. Pour les fonctions de script devant retourner une valeur, la classe idThread a cinq fonctions statiques : ReturnString(), ReturnFloat(), ReturnInt(), ReturnVector(), et ReturnEntity().
- Enregistrer l'événement et l'associer à la fonction C++ (sans point-virgule) :
EVENT( EV_Player_Chose, idPlayer::Event_Chose )
- Créer l'événement en haut du fichier, avec les autres :
- Déclarer la fonction dans pak008/script/doom_events.script :
scriptEvent void chose();
- Dans le script de la map qui sera utilisée, appeler la fonction :
$player1.chose();
Notes supplémentaires :
- Quand une fonction de script prend un nombre comme paramètre, je crois que le paramètre doit être de type float dans doom_events.script, peu importe le type de paramètre pour la fonction C++.
- Dans le cas d'une valeur booléenne, la valeur doit être un float partout, y compris dans le code C++. On vérifiera tout simplement si le nombre passé est autre chose que 0 ; si oui, la valeur sera traitée comme true. Voir la fonction de script bindToJoint() dans doom_defs.script pour un exemple.
Problèmes
Le nom maFonction
remplace ici une fonction quelconque.
-
Unknown value : "maFonction" (Erreur de console)
La fonction n'est pas reconnue ; s'il s'agit d'une fonction nouvellement créée, vérifier qu'elle a été ajoutée dans doom_events.script.