! ! Fichier d'exemple minimaliste avec INFORM 6 ! =========================================== ! ! Ce document est une aide pour les nouveaux programmeurs en jeux d'aventure ! textuels. Il ne pourra pas couvrir tous les aspects de la programmation mais ! permettra d'avoir une introduction aux principes généraux. ! ! Aides complémentaires : ! Site du traducteur des bibliothèques françaises : http://jlpo.free.fr/ ! Guide du débutant (en anglais) : http://www.firthworks.com/roger/IBG.html ! Guide complet (en anglais) : http://www.inform-fiction.org/manual/about_dm4.html ! Notre site et forum pour une aide sur mesure et en ligne : http://ifiction.free.fr/forumBB ! ! Et sinon, les commentaires en Inform sont précédés d'un point d'exclamation ! ! Nous vous conseillons d'utiliser un éditeur de texte qui supporte la coloration ! syntaxique, pour plus de lisibilité. ! Veuillez bien noter qu'il existe des syntaxes alternative parfois, et que certaines parties de codes ! ne sont pas forcément expliquées en détail ici. Si vous trouvez qu'il manque certaines explications, ! merci de nous en faire part sur le forum. !% ! Les options ci-dessous sont destinées au compilateur : !% ! (toutes sont désactivées dans cet exemple) !% ! +include_path=..\..\bibliotheques !% ! language_name=French ; !% ! -v5 ! version souhaitée : v5, v8, G, etc... !% ! -s ! give statistics !% ! -X ! infix (ne pas laisser dans la version publiée ; non disponible avec Glulx) !% ! -D ! debug (ne pas laisser dans la version publiée) !% ! -r ! imprime le contenu du jeu pour envoi vers un correcteur d'orthographe !% !+transcript_name=gametext.txt ! fichier d'arrivée !% !$MAX_SOURCE_FILES=512 !% !$MAX_INCLUSION_DEPTH=10 !% !$MAX_LABELS=50000 !% !$MAX_INDIV_PROP_TABLE_SIZE=50000 !% !$MAX_STATIC_DATA=20000 !-------------------------------------------- ! Constantes générales !-------------------------------------------- ! Les constantes sont des valeurs fixées une fois pour toute dans le jeu, et qui ! servent à définir des paramètres dans les mécanismes internes du jeu ! ou des définitions de l'univers (par exemple le système de fenêtrage, score ! maximum, nombre max d'objets portés...) Constant Story "Exemple titre"; ! titre de l'histoire Constant Headline "^sous titre^"; ! sous titre de l'histoire Release 1; ! version Constant MAX_CARRIED 6; ! nombre maximum d'objets transportés par le joueur. Constant MAX_SCORE 2; ! score maximum Constant OBJECT_SCORE 1; ! point gagné par objet spécial ramassé pour la première fois Constant ROOM_SCORE 1; ! point gagné par pièce spéciale découverte ! Les variables peuvent être internes à un objet, ou définies de façon globales. ! Lorsqu'elles sont valables pour tout le jeu, on les fait précéder de "Global" ! pour les initialiser. Global modereplay = 0; ! celle-ci sert pour des fonctiones de déboggage surtout. ! Les conditions avec #ifdef sont un peu spéciales. Elles servent lors de la compilation. ! Celle qui suit est pour faire la différence entre la version graphique (glulx) ou uniquement ! textuelle (zcode) du jeu. Vous n'avez pas besoin de vous préoccuper de cela pour le moment. #ifdef TARGET_ZCODE; ! "si le système cible est texte uniquement (zcode)... ! ne rien exécuter de plus" (la ligne est vide) #ifnot; ! TARGET_GLULX; ! "sinon, c'est à dire si c'est graphique (glulx)... Constant GG_MAPWIN_ROCK 210; ! initialiser ces constantes et ces variables pour le système de fenêtre..." Global gg_mapwin = 0; Global musicon = 1; Global gg_musicchan = 0; Constant theme; Constant fin; Constant outro; Global modeimages = 1; #endif; ! fin de la condition !-------------------------------------------- ! Directives de remplacement !-------------------------------------------- [DeathMessage; print "Vous avez perdu !";]; ! sert pour le message lorsque le joueur perd mais ne meurt pas. Replace RestartSub; ! sert pour le système de fenêtrage. Ne pas s'occuper de cela. !-------------------------------------------- ! Attributs !-------------------------------------------- !-------------------------------------------- Include "Parser"; ! inclusion de bibliothèques Inform obligatoires. Include "VerbLib"; ! inclusion de bibliothèques Inform obligatoires. Include "infglk"; ! inclusion de bibliothèques Inform obligatoires (pour la partie glulx) !-------------------------------------------- ! Les fonctions débutent par un crochet, suivi du nom de la fonction, suivi d'un point virgule. ! (De façon générales les différentes parties de code sont délimitées par un point virgule ;) ! Celle-ci est une fonction native d'Inform, que l'on peut personnaliser à sa guise [ PrintRank; ! fonction lors de l'affichage du score print ", vous donnant le rang "; if (score >= 1) "d'aventurier accompli."; if (score < 1) "de touriste."; "de touriste."; ]; ! nous venons de découvrir une condition : cela permet de tester SI (if) une variable est supérieure (>) ! ou inférieure (<) à une valeur. Si la première condition est remplie, un message s'affiche et le test ! s'arrête, sinon on teste la suivante et ainsi de suite. ! --------------------------------------------------------------------------- ! Ceci change le comportement du jeu quand on utilise le mot "tout" ! ou un mot ambigu ! --------------------------------------------------------------------------- [ChooseObjects obj code; if (code<2) { if (obj has scenery) return 2; rfalse; } if (action_to_be==##Eat && obj has edible) return 3; if (obj hasnt scenery) return 2; return 1; ]; !-------------------------------------------- ! Variables !-------------------------------------------- ! il n'y en a pas ici de spécifiques pour un jeu aussi court... ;) !-------------------------------------------- ! Routines ! ------------------------------------------- ! encore des fonctions, permettant divers effets visuels. [Attend; if (modereplay == 0) KeyCharPrimitive(); ]; ! par exemple pour attendre dans le jeu et continuer lorsque le joueur presse une touche, ! il suffit d'inclure la fonction " Attend(); " dans le code (cette fonction étant elle-même ! un appel à une autre fonction, mais ici on fait un petit test auparavant pour bloquer ! cette fonction dans certains contextes. [Pause; if (modereplay == 0) KeyDelay(40); ]; ! similaire à l'attente, sauf que là le jeu continue tout seul au bout de 4 secondes [Gras; ! style Gras #ifdef TARGET_ZCODE; #ifnot; ! TARGET_GLULX; glk_set_style(style_Emphasized); #endif; ! TARGET_ ]; ! fonction pour utiliser un style gras d'écriture [Italique; ! Italique #ifdef TARGET_ZCODE; #ifnot; ! TARGET_GLULX; glk_set_style(style_Note); #endif; ! TARGET_ ]; [Normal; ! style normal #ifdef TARGET_ZCODE; #ifnot; ! TARGET_GLULX; glk_set_style(style_Normal); #endif; ! TARGET_ ]; !-------------------------------------------- ! Modèles d'objets (déclarations de classes) !-------------------------------------------- ! les classes permettent de définir un modèle qui pourra resservir plus tard pour plusieurs objets. ! Nous reviendrons plus loin sur la structure type d'un objet. Class Personnage with life [; ! Answer,Ask,Order,Tell: ! print_ret "Utilisez plutôt -parler à-", (the) self, "." ; Kiss: print "Vous essayez d'embrasser ce personnage mais il n'a pas l'air d'accord !^" ; rtrue ; ], has animate; Class Maison with before [; Listen: print "Vous n'entendez rien de spécial dans cette maison.^" ; rtrue ; ], has light; !-------------------------------------------- ! Plan des lieux (pièces, portes...) !-------------------------------------------- ! Nous allons rencontrer ici notre premier objet. Un objet est de 2 types dans Inform : lieu ou chose. ! Les objets ont cette structure : ! Objet nom_objet "nom d'affichage de l'objet" ! with ! signifie que l'on commence les définitions, chaque définition est séparée par une virgule ! description "Description de l'objet lorsqu'on l'examine ou bien ce qu'on voit dans une pièce" ! n_to autre_piece, s_to encore_une_autre_piece ! lien vers autre pièce ! has light ; ! ce qui suit has s'appelle Attribute, cela définit des attributs génériques ! à l'objet, sous forme binaire (comme un interrupteur éteint ou allumé, seulement ! deux valeurs sont possibles), c'est à dire que l'objet peut être par exemple ! ouvert ou fermé, féminin ou non, donnant un score ou pas etc. ! Des attributs sont déjà prédéfinis, mais on peut en ajouter d'autres. ! Ils servent généralement lors des tests et conditions. Object salle1 "Salle 1" class Maison with description "Une pièce vide, avec une sortie au nord.", n_to salle2 ! ce lieu donne sur la salle n°2 has ; ! pas besoin de préciser que le lieu est allumé, car la classe dont ! il fait partie a déjà l'attribut "light" ! a noter que si votre pièce reste dans le noir, le joueur ! ne pourra rien faire dedans, sauf s'il a un objet qui fournit ! de la lumière. ! à noter qu'il est possible de définir une classe en remplaçant Object par le nom de la classe ! ou bien en écrivant à la suite class_ nom_de_la_classe Object salle2 "Salle 2" with description "Vous êtes à l'extérieur, pour entrer, c'est vers le sud.", s_to salle1 ! cela permet de retourner dans la salle n°1. Il faut définir dans les 2 sens ! has light scored ; ! Cette pièce n'a pas la classe aussi il faut préciser qu'elle a ! de la lumière. ! De plus lorsque l'on entre dedans cela donne un point !-------------------------------------------- ! NPCs (personnages non joueurs) !-------------------------------------------- Personnage Homme "homme" salle2 ! cet objet est de la classe personnage, ! et hérite de ses caractéristiques (essayez de l'embrasser pour voir ! ) with name 'homme' 'monsieur' 'bonhomme', ! noms de référence lors du jeu. Veuillez ! mettre le plus de synonymes possibles, essayez ! de prévoir comment le joueur voudra s'y référer. description "Un personnage sans relief. À vous de l'améliorer !", times_spoken_to 0, ! variable pour compter les sujets de conversation. Le nom de variable ! est ici complètement arbitraire. life [; Attack: print "Vous frappez l'homme, et il disparaît.^"; remove self; rtrue ; ! ce qui se passe lorsque l'on se réfère à l'objet en utilisant certains verbes (ici attaquer) Parler: self.times_spoken_to = self.times_spoken_to + 1; ! incrémentation de la variable locale. switch (self.times_spoken_to) { 1: print_ret "Vous lui parlez, et il vous rend un bref salut."; 2: print_ret "Vous lui parlez, mais il ne réagit pas."; 3: print "Rien à dire de plus^" ;rtrue ; default: return false;} ;], has ; ! ici la variable locale (uniquement valable pour l'objet) times_spoken_to sert à vérifier combien ! de fois on parle au personnage. Lorsque l'on manipule une variable locale (mais pas à l'initialisation), ! on ajoute le nom de l'objet à laquelle elle appartient avant, un point, puis le nom de la variable. ! Lorsqu'on manipule la variable dans l'objet auquel elle appartient, on peut utiliser à la place self. ! Ainsi dans l'exemple plus haut on pourrait dire self.times_spoken_to ou Homme.times_spoken_to ! Par contre si on manipulait la variable depuis un autre objet (par exemple si un second personnage ! devait être au courant du nombre de conversation avec le premier personnage, il faudrait ! utiliser impérativement Homme.times_spoken_to. ! L'intérêt d'utiliser self permet de réutiliser facilement une partie de code répétitive. !-------------------------------------------- ! Objets déplaçables !-------------------------------------------- Object Vase "vase" Salle2 ! salle2 placé ici signifie que l'objet vase se trouve dans l'objet salle2 with description "Un vase.", before [; Attack: remove self ; deadflag=3; print "Vous cassez le vase en mille morceaux. Ce vase précieux était inestimable !" ; rtrue ; Rub: print "Vous frottez le vase et un génie en sort, qui dépose une fleur à vos pieds. Le génie disparaît ensuite.^" ; move Fleur to location ; rtrue ; Receive: if (noun == Fleur) { deadflag=2; print "Félicitation, vous venez de décorer avec goût ce vase." ; rtrue ; } ; ], name 'vase', has open container scored; ! le vase est ouvert et a l'attribut container, ! donc on peut mettre quelque chose dedans. Object Fleur "fleur" ! cet objet ne se trouve nulle part ! with description "Une belle fleur odorante.", before [; Attack: remove self ; deadflag=3; print "Vous n'osez pas détruire une aussi belle plante" ; rtrue ; Smell: print "Cette fleur sent très bon.^" ; rtrue ; ! rtrue permet ici de ne pas afficher ! le message par défaut lorsque l'on ! respire quelque chose. Give: if (second == Homme && Fleur in Player) {print "L'homme sourit vaguement, prend la fleur, et la fait tomber sur le sol.^" ; move Fleur to location ; rtrue ; } ; ], name 'plante' 'fleur', has female ; ! les objets de marque féminine en français doivent avoir cet attribut pour afficher ! correctement les articles "la" / "une" !-------------------------------------------- ! Code spécifique !-------------------------------------------- ! ne pas s'occuper de cette partie... #ifdef TARGET_ZCODE; #ifnot; ! TARGET_GLULX; [ InitGlkWindow winrock; switch (winrock) { GG_MAINWIN_ROCK: } rfalse; ! leaving out this line will lead to a messy crash! ]; #endif; ! TARGET_ ! Plus bas c'est initialisation principale du jeu. Permet notamment de définir dans quelle salle commence l'histoire. [Initialise; location=salle1; lookmode = 2; ! mode long #ifdef TARGET_ZCODE; #ifnot; ! TARGET_GLULX; glk_window_clear(gg_mainwin); glk_image_draw(gg_mainwin,2,imagealign_InlineCenter, 1); ! Splash screen Musique_intro(); attend(); glk_window_clear(gg_mainwin); gg_mapwin = glk_window_open(gg_mainwin, (winmethod_Left+winmethod_Fixed),100, wintype_Graphics, GG_MAPWIN_ROCK); glk_image_draw(gg_mapwin,1, 0, 0); ! marge motif #endif; ! TARGET_ ]; !-------------------------------------------- Include "FrenchG"; ! partie à inclure obligatoirement pour le français !-------------------------------------------- !-------------------------------------------- ! Verbes ajoutés (extension de FrenchG.h) !-------------------------------------------- ! les verbes avec -Sub à la fin sont les définitions des verbes initialisés plus bas [MusicPlaySub topic; #ifdef TARGET_ZCODE; print "Pas de musique dans cette version."; #ifnot; ! TARGET_GLULX; gg_musicchan = glk_schannel_destroy(gg_musicchan); switch (topic) { theme : gg_musicchan = glk_schannel_create(0); glk_schannel_play_ext(gg_musicchan, 1, 1, 0); outro : gg_musicchan = glk_schannel_create(0); glk_schannel_play_ext(gg_musicchan, 3, 1, 0); } print_ret "morceaux disponibles : ~musique~ + theme, outro ..."; #endif; ! TARGET_ ]; [MusicOffSub; #ifdef TARGET_ZCODE; #ifnot; ! TARGET_GLULX; if (musicon == 1) musicon = 0; print "[la musique est désactivée]^"; rtrue ; !else musicon = 1; #endif; ]; [MusicOnSub; #ifdef TARGET_ZCODE; #ifnot; ! TARGET_GLULX; if (musicon == 0) musicon = 1; print "[la musique est activée]^"; rtrue ; #endif; ]; ! Attention juste en dessous ce n'est pas un verbe mais une fonction ! [Musique_intro; #ifdef TARGET_ZCODE; #ifnot; ! TARGET_GLULX; if (musicon == 1) { gg_musicchan = glk_schannel_destroy(gg_musicchan); gg_musicchan = glk_schannel_create(0); glk_schannel_play_ext(gg_musicchan, 100, 1, 0); ! musique intro } #endif; ]; [MusicStopSub; #ifdef TARGET_ZCODE; #ifnot; ! TARGET_GLULX; gg_musicchan = glk_schannel_destroy(gg_musicchan); #endif; ! TARGET_ ]; [ParlerSub; if (noun == player) print_ret "Cela ne vous étonne pas d'apprendre cela."; if (RunLife(noun,##Parler) ~= false) return; ! consult life[; Parler: ] print_ret " Vous ne savez pas quoi dire."; ]; Extend 'parler' replace * 'aux'/'a'/'à'/'au'/'avec' creature -> Parler * creature -> Parler * 'à'/'a'/'au'/'aux'/'avec' creature 'de'/'du'/'des'/'l^' topic -> Tell * creature 'de'/'du'/'des'/'l^' topic -> Tell * creature topic -> Tell; Verb "load" * -> Restore; Verb "Restore" * -> Restore; Verb 'music' 'musique' * -> MusicStop * 'stop' -> MusicStop * 'off' -> MusicOff * 'on' -> MusicOn * topic -> MusicPlay; [ RestartSub; L__M(##Restart,1); if (YesOrNo() ~= 0) { #ifdef TARGET_ZCODE; #ifnot; ! TARGET_GLULX; <MusicStop>; ! pour arrêter la musique if (modeimages ~= 0) { glk_window_close(gg_mapwin,0);} ! modeimages = 1 ; #endif; ! TARGET_ @restart; L__M(##Restart, 2); } ]; Extend 'recommencer' replace * -> Restart;