Jeu Risk Star Wars
Transcription
Jeu Risk Star Wars
Risk Star Wars Antoine BELIN (belin_a) Christopher TAUPIN (taupin_c) Patrice DE SAINT STEBAN (de-sai_p) Thibault ROBIN (robin_t) le 21 Juin 2005 1 Unbound Value Star Wars Risk Soutenance Finale Table des matières 1 Introduction 1.1 1.2 1.3 1.4 Présentation du groupe . . . . . . . . . . . . . . . . 1.1.1 Antoine, allias Send' . . . . . . . . . . . . . 1.1.2 Christopher, allias Lord Gunator . . . . . . 1.1.3 Patrice, allias Patou... . . . . . . . . . . . . 1.1.4 ...et le dernier de la liste : Thibault ! ! ! . . . Nos premiers pas . . . . . . . . . . . . . . . . . . . 1.2.1 Idée du Projet . . . . . . . . . . . . . . . . 1.2.2 Présentation du projet . . . . . . . . . . . . Les règles du jeu établis lors de la 1ière soutenance 1.3.1 Menu du Jeu . . . . . . . . . . . . . . . . . 1.3.2 Répartition et déplacement des Troupes . . 1.3.3 Phases de Combat . . . . . . . . . . . . . . 1.3.4 Fin de la Partie . . . . . . . . . . . . . . . . Les prévisions pour nos diérentes échéances . . . . 1.4.1 Découpage du projet . . . . . . . . . . . . . 1.4.2 Répartition . . . . . . . . . . . . . . . . . . 2 Elaboration des graphismes 2.1 2.2 2.3 2.4 Introduction . . . . . . . . . . . . . . . . La carte galactique . . . . . . . . . . . . 2.2.1 Pourquoi l'utilisation des calques Le panneau de lancement . . . . . . . . 2.3.1 Quitter . . . . . . . . . . . . . . 2.3.2 Options . . . . . . . . . . . . . . 2.3.3 Jouer . . . . . . . . . . . . . . . Création des sprites . . . . . . . . . . . . 2.4.1 Les sprites des planètes . . . . . 2.4.2 Les sprites d'unités . . . . . . . . 3 L'achage graphique 3.1 3.2 4.2 EPITA . . . . . . . . . . L'Achage Graphique [Direct X sous Direct 3.1.1 Les Classes et Objets Delphi . . . . 3.1.2 Procédures de la classe Graphique . Place à l'achage... . . . . . . . . . . . . . . 4 Le moteur de jeu 4.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Draw] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . L'éditeur XML . . . . . . . . . . . . . . . . . 4.1.1 Les procédures de lecture XML . . . . Les premières fonctions . . . . . . . . . . . . . 4.2.1 Constantes, Types et Enregistrements 4.2.2 Procédure de Distribution Planétaire . 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 4 4 5 5 5 5 5 5 6 6 7 7 8 8 8 9 11 11 11 11 11 12 12 14 15 15 16 18 18 18 19 24 32 32 33 38 38 39 InfoSup B2 Unbound Value 4.3 4.2.3 Fonction-Procédure d'Achage Planétaire 4.2.4 Génèse . . . . . . . . . . . . . . . . . . . . 4.2.5 Répartition des unités . . . . . . . . . . . 4.2.6 Procédure de combat . . . . . . . . . . . . 4.2.7 Finalité du Programme . . . . . . . . . . Le moteur de jeu . . . . . . . . . . . . . . . . . . 5 L'intelligence articiel 5.1 5.2 Star Wars Risk Soutenance Finale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Le déploiement des troupes La phase d'attaque 6 La mise en place du son 48 48 49 50 7 Notre site web 7.1 7.2 40 40 42 43 44 44 Site Publique (http ://risk.star.wars.free.fr/)) . . . . . . . . . Administration Interne (http ://risk.star.wars.free.fr/admin/) 52 52 52 8 L'installation et la désinstallation 53 9 Conclusion 54 EPITA 3 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale 1 Introduction Ce jeu, inspiré du célèbre jeu de société Risk, et de sa version dérivée du Seigneur des Anneaux , se déroulera dans l'univers mythique de Star Wars. Le but du jeu est d'accomplir la mission donnée en début de partie ou de conquérir par la force des armes la maîtrise de la galaxie toute entière.. L'issue des combats est déterminée par des jets de dés. La partie se joue entre 2,3 ou 4 joueurs, ces derniers pouvant d'aronter sur le même ordinateur, via réseau ou être gérés par un ordinateur. Ce cahier des charges représentera le groupe, les prévisions des diérentes phases du projet, ainsi que le matériel dont nous aurons besoin. Alors que la force soit avec nous ! 1.1 Présentation du groupe En apprennant que l'on devait réaliser un projet informatique, il nous a fallut constituer notre propre groupe ! Ce ne fut pas chose facile, car nous nous connaissions alors très peu. Cependant, après le séminaire, quelques anités s'étaient déjà opérées : deux groupes de deux se sont alors formés. Encore fallait-t'il que le sujet plaise à tous ! Lorsqu'il fut trouvé, le groupe nal se constitua. Depuis, les membres du groupe ont appris à mieux se connaître. Ce groupe est resté inchangé pour les exposés en méthodologie. Le nom du groupe nous posa problème, il fut résolu lors d'un TP d'algo. Une phrase s'achait beaucoup trop souvent à notre goût sur notre écran : Unbound Value ! ! D'abord synonyme de profond agacement, elle symbolisa par la suite la tentative de compréhension d'un problème dépassant l'entendement, puis un symbole de victoire lorsque ce message disparaissait enn. Le groupe fut ainsi dénitivement créé. 1.1.1 Antoine, allias Send' J'habite dans une petite ville située à la limite du 91 et du 78. Mes passions restent simples. J'aime lire et écouter toute sorte (ou presque) de musique. J'adore faire du sport, spécialement les sports de raquettes, qui demandent un esprit individuel assez développé, mais surtout les sports d'équipes, qui m'apportent beaucoup dans ma vie collective. Hélas, je ne peux plus en faire autant que je le souhaiterais, à cause du manque de temps et de la longueur des trajets. Enn, j'adore regarder de bons lms le soir et passer un peu de temps avec un groupe d'amis extraordinaire. EPITA 4 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale se produira sur un plateau représentant la galaxie, cette dernière étant découpée en diérentes régions. Diérentes phases de jeu rythmeront la partie. En eet, chaque joueur déploiera ses troupes pour écraser ses ennemis, mais il devra ensuite encaisser les phases de combat qui lui seront proposées par ses ennemis. L'issue des combats sera déterminée par des jets aléatoires de dés. Celui qui remporte sa mission ou qui conquiert la galaxie toute entière a gagné la partie. Le jeu se déroulera entre 2, 3, 4, 5 ou 6 joueurs, ces derniers pouvant joués sur un même ordinateur, en réseau ou être générés par l'ordinateur. 1.3 Les règles du jeu établis lors de la 1ière soutenance Nous avons cherché à harmoniser nos points de vue sur les aspects graphiques du jeu. Pour ce faire nous avons décidé de mettre au point un autre chier en format LaTeX. Antoine a rédigé ce chier sur lequel il a exprimé sa propre vision du jeux. Puis ce chier a été révisé par les autres membres du groupe et a été remanié en conséquence. Ce chier nous a permis d'éviter de se poser des questions inutiles et de pouvoir se concentrer sur les questions de fond. Il nous a également permis une progression plus rapide pour toute la partie graphique de notre jeux. Même si ce chier n'a pas été suivi à la lettre, les choix devant être décidés tout le long de l'année ont été rapidement mis en place grâce à ce travail préliminaire. 1.3.1 Menu du Jeu Après le lancement du jeu, le menu principal de commande du jeu s'achera. Il comportera diérents onglets, à savoir : : Lance la nouvelle partie ou la partie en cours que l'on souhaite charger. Quitter : Comme son nom l'indique, il permet de quitter proprement le jeux. Options : C'est l'une des plaques tournantes du jeux. Plusieurs paramètres sont à régler. Jouer - Le contrôle du volume. Cependant, le volume se règle également dans le jeu, il n'est donc pas utile de le régler dans le panneau d'options. - Donnée primordiale à règler : le choix du nombre de joueurs pour la partie, allant de 2 à 6 joueurs. - Le choix du nom de la partie. - Autre moment important : décider quel joueur sera humain ou articiel. EPITA 6 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale - Le choix du nom des joueurs - Le bouton "Charger", qui permet de revenir au moment où l'on a laissé la partie avant de quitter. - Le bouton "Réinitialisation" : Il n'apparaît qu'après l'utilisation du bouton "Charger", et permet de remettre à zéro les données établies. - Enn, le bouton "Retour" qui permet de revenir au menu principal. Deux joueurs seulement ont été prévus lors de la rédaction de ce chier. Néanmoins, la création d'autres joueurs pourrait se faire rapidement si nous avons terminé le jeux pour deux joueurs. 1.3.2 Répartition et déplacement des Troupes Après avoir cliquer sur le bouton jouer, le joueur peut alors se familiariser avec la répartition aléatoire des diérentes planètes lui ayant été attribuées. Une fois ceci eectué, il doit placer à sa guise sur ses planètes le nombre de troupes xées lui étant imparties. Il apparaît alors dans ce que l'on appellera par la suite le cadran 1, une zone comportant le nombre d'unités disponibles, un emplacement modiable par le joueur an d'ajouter, un bouton de validation local, validant une répartition du nombre souhaité d'unités, le transférant vers les planète souhaitées. Comment les répartir ? ? ? C'est très simple : les planètes que le joueur possède sont entourées par quatre triangles de couleur, permettant l'identication facile. De plus, les planètes adverses sont alors grisées. Enn, vous remarquerez que dans le coin à droite de chacune des planètes, amies comme ennemies, se trouvent un chire un rouge : il représente le nombre de ottes présentes sur la planète. Pour ce faire, il sut donc de cliquer sur les planètes où vous désirez poster vos troupes. Lorsque vous avez terminé, cliquez sur le bouton suivant pour commencer la phase tant attendu de conquête. Attention : même si vous n'avez pas placé TOUTES vos troupes, le bouton suivant engendrera un changement de phase : ne cliquez donc pas avant avoir vérié. Enn, il est nécessaire de laisser un intervalle de temps à chaque action de l'IA. 1.3.3 Phases de Combat Le système de combat sera similaire à la répartition, à ceci prêt qu'il visera une planète adverse. Au moins une unité devra toujours rester sur la planète de départ, alors que les autres seront engagées dans l'arène des combats. Au terme d'un combat de dés entre les deux eectifs en présence, la planète cible changera ou non d'appartenance. De petites scènes video ont été ajoutées, explicitant l'issue du combat. Par ailleurs, les phases de EPITA 7 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale combat et de déplacement pourront s'entrelacer, car seule la contrainte des unités gelées permettra de mettre un terme aux actions du joueur, lorsqu'il n'aura plus de possibilités de déplacement. L'enchaînement des phases se déroulera de la manière suivante : Déploiement des renforts alliés, Attaques alliées, Changement de Main, Déploiement des renforts adverses, Attaques adverses, Changement de Main . 1.3.4 Fin de la Partie La victoire est donnée à celui qui possèdera toutes les planètes de la galaxie. 1.4 Les prévisions pour nos diérentes échéances Voici le plan que nous avons établi lors de la rédaction du cahier de charge. Comme vous pourrez le constater, les prévisions ont été quelque peu modiées au cour des diérentes soutenances, ce qui ne nous a pas empéché de terminer de façon honorable notre projet... 1.4.1 Découpage du projet Nos Modules Nous avons tenté de scinder le plus possible notre projet en diérentes tâches, ce qui nous permettra de répartir plus aisément les rôles au cours de l'année. En voici la liste : Main Joueur Réseau Interface Homme/ Machine (IMH) IA Joueur IA Conguration Site Web Programme principal initialisant et gérant les autres programmes. Associe les modules de joueur. Protocole d'émission / réception d'informations entre joueurs. A faire en dernier lieu. Prend en compte les actions menées par le joueur et les traduit pour la machine.s Intelligence Articielle (Règles, déroulement du jeu). Joueur géré par la machine. Modie dynamiquement la conguration du jeu (comprend les sauvegardes) L'IHM comprend entre autres : la gestion des périphériques, la gestion des Achages à l'écran , le Menu du jeu (avec ses animations et ses commandes de menus), et le son. C'est donc une des plus grande parties de notre projet. EPITA 8 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale Diagramme des Modules Ci contre le diagramme de tous les principaux modules envisagés, ainsi que leurs interactions. 1.4.2 Répartition Nous avons décidé de travailler par groupe de deux, an, pensons nous, d'avancer plus rapidement lors de moments un peu plus délicat. Il est évident que le groupe se réunira une fois par semaine pour pouvoir suivre les avancées du second groupe. Les modules abordés par chacun au cours de ce projet sont : Antoine : Site web, Main, Joueur, IA, Joueur IA Christopher : IHM, Réseau, Conguration Patrice : Site web, Main, Joueur, IA, Joueur IA Thibaut : IHM, Réseau, Conguration 1re soutenance Delphi Documentation Site Web Librairie graphique Initiation graphique Boucle principale Patrice Antoine Thibault Christopher + + + + + + + + + + + - + + + - 2e soutenance Boucle principale Conguration Joueur Achage graphique Gestion des périphériques Patrice Antoine Thibault Christopher + + + - + + + - + + + + + + 3e soutenance IA Joueur IA Joueur IHM Joueur graphique EPITA Patrice Antoine Thibault Christopher + + - + + - 9 + + + + + + InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale Soutenance nale Réseau Test + débugage Menu Setup Animations + son EPITA Patrice Antoine Thibault Christopher + + - + + - 10 + + + + + + + + + + InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale 2 Elaboration des graphismes 2.1 Introduction Bienvenue dans l'univers mythique de Star Wars, alors que la célèbre carte s'ache devant vous, accompagnée des planètes les plus célèbres de la galaxie. La carte comprend une galaxie (pour information la galaxie voisine Andromeda m31x), image de base de 2048 pixels sur 2077. En calques ont été rajoutés les planètes (dont la disposition est tirée des sources ocielles de lucaslm), les secteurs, dessinés arbitrairement an de les faire concorder à l'esprit du jeu, quelques noms des régions les plus connues, les cinq grandes routes commerciales et enn les liaisons entre les planètes. 2.2 La carte galactique 2.2.1 Pourquoi l'utilisation des calques Les calques orent l'énorme avantage de modier rapidement et sans détérioration l'image générale : ils peuvent être supprimés, retouchés, ou insérés à volonté, ce qui a permis naturellement d'apporter quelques modications nécessaires dues à l'avancement du jeu. En l'occurrence, les planètes sont implémentées par le code sous forme de sprites, ainsi que leurs noms. De la sorte, ils ont pu être supprimées du fond de carte, et être placés dans des palettes de sprites. Il ne reste plus que de la carte les noms de régions, les limites de ces régions, ainsi que les liaisons : 2.3 Le panneau de lancement Le jeu commençant à se structurer, il nous a fallut naturellement implémenter le panneau de lancement de notre jeu. Ce dernier n'étant pas forcément très élaboré, trois boutons susent au bon déroulement de la partie. Le bouton "Quitter" ne devrait pas poser trop de problèmes : il permet seulement de quitter proprement le jeu. Le bouton "Lancer le Jeu" permet, vous l'avez compris, de débuter la partie. Cependant, avant de commencer la partie, il est nécessaire de procéder à quelques réglages, d'où le troisième bouton "Options". En cliquant sur ce dernier, un autre écran s'ache sous vos yeux, permettant de congurer à votre guise votre jeu. Tout d'abord, une fenêtre permet de déterminer en quelle résolution vous désirez lancez le jeu. C'est également ici que se trouve le bouton nécessaire au réglage du volume. Enn, on y sélectionne le nombre de joueurs nécessaires à la partie. Cela peut allez de 2 à 6 joueurs, qu'il soit EPITA 11 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale humain ou articiel. Les boutons ont été implémentés sous forme d'images TBitmap, à l'instar du template de la console. Ceci évidemment dans un but d'esthétisme et de confort pour le joueur. Le fait de mettre des images comme boutons n'a pas sut, ce pourquoi j'ai poussé le vice jusqu'à prendre en compte le clic sur le bouton, c'est à dire que l'image du bouton change pendant chaque clic (la classe...). 2.3.1 Quitter Ici, en partant du bas, premier bouton, le bouton quitter, qui comme dit précédemment ferme l'application à partir d'un simple "close" : procedure TForm1.Image10MouseDown(Sender : TObject ; Button : TMouseButton ; Shift: TShiftState; X, Y: Integer); begin Image11.Visible := true; end; procedure TForm1.Image10MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin Image11.Visible := false; sleep(50); close; end; 2.3.2 Options Le second bouton, options, envoie vers une nouvelle fenêtre, contenant la conguration de base du jeu : procedure TForm1.Image8MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin Image9.Visible := true; end; procedure TForm1.Image8MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin Image9.Visible := false; sleep(50); Form2.visible := true; end; EPITA 12 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale Ensuite à partir de la fenêtre de conguration des options de jeu, le bouton retour fonctionne exactement comme les autres, ferme cette fenêtre et revient à la fenêtre principale de la console. An de ne pas gérer de tests superus qui permettraient d'éviter que de fausses valeurs dans les menus déroulants (gérés par des ComboBox Delphi) soient prises en compte lors du lancement, comme par exemple un texte tapé par le joueur au lieu qu'une option du menu déroulant soit choisie, ou encore une option non choisie, il a déjà fallu mettre les champs des ComboBox style sur "csOwnerDrawVariable" (an de ne pas laisser l'édition activée), et Item Index sur une valeur par défaut, en entrant dans ce champ l'entier relatif à la valeur souhaitée. Aussi, pour éviter de gérer un champ supplémentaire dans les menus déroulants des joueurs (qui aurait été l'option "désactivé"), an de gérer les cas à moins de 6 joueurs, un test a été mis en place de la sorte : procedure TForm2.ComboBox8Change(Sender: TObject); begin if ComboBox8.Text = '2 Joueurs' then begin ComboBox2.Visible := true; J1.Visible := true; Edit1.Visible := true; ComboBox3.Visible := true; J2.Visible := true; Edit2.Visible := true; ComboBox4.Visible := false; J3.Visible := false; Edit3.Visible := false; ComboBox5.Visible := false; J4.Visible := false; Edit4.Visible := false; ComboBox6.Visible := false; J5.Visible := false; Edit5.Visible := false; ComboBox7.Visible := false; J6.Visible := false; Edit6.Visible := false; end; EPITA 13 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale {etc pour les autres nombres de joueurs...} 2.3.3 Jouer Enn, le bouton jouer, qui lance le jeu. C'est le bouton qui nous a posé le plus de soucis. Initialement, il était prévu de créer pour la console de lancement un exécutable à part, qui devait servir d'initialisation du jeu avant son réel lancement, ceci pour coller un peu à l'esprit de la séparation du programme en diverses classes d'objet, et de la séparation très méthodique des dossiers. Ainsi, la console devait comprendre un dossier à part... Seulement, c'était sans compter les problèmes qui ont suivi avec la procédure windows ShellExecute, qui lance un exécutable en prenant en compte un large panel de paramètres. Deux problèmes ont surgi, sur lesquels j'ai malheureusement bloqué pendant plusieurs semaines sans trouver de solution viable... Le premier de ces problèmes et sans doute le plus bloquant, consistait à générer une erreur à chaque lancement de l'exécutable principal ("riskstarwars.exe") , erreur d'exception windows suivie d'une erreur interne au programme. Etonnant étant donné que le programme se lance très bien manuellement... J'ai essayé de toucher aux paramètres de la procédure, entrer diérentes méthodes, mais rien n'y a fait. Le second problème résidait dans l'obligation de placer dans les paramètres de la fonction ShellExecute le chemin d'accès à l'exécutable riskstarwars.exe. Pour que la procédure prenne en compte correctement le chemin d'accès, j'étais obligé de placer le chemin complet par adressage total. Or il est logique de penser que l'utilisateur ne souhaitera pas obligatoirement installer le jeu dans le répertoire par défaut. J'ai donc bien tenté de placer des adresses locales, cherché comment placer ces adresses locales, mais pas moyen de trouver quoi que ce soit qui fonctionne (rien ne se lance). Donc pour palier à ce problème, nous avons résolu d'intégrer la console de lancement directement dans le programme principal. Le bouton jouer accompagné de ShellExecute : procedure TForm1.Image6MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin Image7.Visible := true; end; procedure TForm1.Image6MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin EPITA 14 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale Image7.Visible := false; sleep(50); //ShellExecute(0,'Open','D:\Archives\Autres Documents\Projet\Implémentation \Risk Star Wars\RiskStarWars\riskstarwars.exe',nil,Nil,SW_MAXIMIZE); end; 2.4 Création des sprites Les planètes seront comprises dans le chier racine sous forme de sprite, an d'être achées convenablement suivant les aléas du jeu. Lorsque par exemple une unité sera sélectionnée par le joueur, seules les planètes accessibles par l'unité seront achées dans leur forme normale, alors que les autres seront grisées. Les noms des planètes ont été crées sous plusieurs couleurs, mais nous avons plutôt décidé d'entourer les planètes de quatre triangles de couleur an de connaitre qui possède la dite planète Les liaisons sont intégrées au fond de carte, n'ayant pas besoin d'être modiées pour le bon fonctionnement du jeu. Il en va de même pour les limites de secteurs et les noms de régions . 2.4.1 Les sprites des planètes Les planètes sont donc mises sous forme de palettes de sprites, comme le reste des éléments achés ponctuellement. Le fait d'avoir choisi de ne pas laisser les planètes en images de fond est dû à notre décision de modier l'état des planètes suivant les sélections eectuées à l'écran par le joueur. En eet, les planètes pourront être achées à l'écran sous trois formes, à savoir : Etat normal, lorsque aucune planète n'est sélectionnée, ainsi qu'un quelconque autre élément. Etat Gelé : lors des diérentes phases de jeu, le joueur ne peut pas toujours cliquer sur toutes les planètes de la carte. Par exemple, lors de la distribution des renforts, il n'est pas utile de cliquer sur les planètes ennemies, vous le comprendrez aisément. Pour que le joueur ait une vision claire et rapide de ses capacités, les planètes inutiles seront en état dit gelé, c'est à dire plus terne que l'état normal. Etat Surbrillance : Est décrété lorsque le curseur passe au dessus d'une planète non "gelé". Cet état renforce donc, la visualisation des possibilités des joueurs en question. Un carré orangé se dessine alors autour de la dite planète. Dans la palette de sprites de planètes, ces dernières sont positionnées les unes après les autres à des intervalles de distance réguliers, formant de cette manière une sorte de tableau de sprites. Les cases dans ce cas précis font EPITA 15 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale 70 x 70 pixels. Les planètes sont classées par ordre alphabétique, et dans l'implémentation, il sura de créer une fonction mathématique linéaire de parcours de la palette, comme pour un tableau, et indiquer au programme à quelle planète correspond quelle distance de parcours. De plus, les planètes peuvent, comme vu plus haut , subir des modications d'état graphique (normales, gelées ou surbrillance), ce qui implique donc d'introduire également dans la palette les mêmes planètes, mais suivant les états états : l'état normal et l'état gelé. Deux tableaux de 7 colonnes chacune ont donc été réalisées. Une palette de sprites de noms a également été prévue, mais comme nous avons nalement trouvé le moyen d'acher directement du texte en partant des fonctions windows de Delphi, nous avons pu acher les noms ded planètes grâce à ce texte. Au départ les sprites sont tous séparés les uns des autres, c'est à dire qu'ils ont été créés séparément à partir des calques de la carte, donnant ainsi une image par planète, et deux images par nom de planète (pour deux joueurs). Mais charger à l'écran séparément chacun de ces sprites permet de ne pas implémenter de fonction de parcours d'une éventuelle palette, mais demande à Direct X de charger un grand nombre de sprites continuellement, alors qu'avec une palette, il n'aurait que celle-ci à charger. De plus, une fonction mathématique de parcours rend un code bien plus propre qu'une association de chier à chaque sprite (il y en aura 60 pour les planètes, et pour le moment 120 pour leurs noms de couleurs diérentes avec deux joueurs, soit 360 si on monte jusqu'à six joueurs, au total 420) deviendrait vite insupportable à gérer. D'où l'intérêt des palettes de sprites. Ce qui nous a posé beaucoup de soucis pendant quelques temps est l'achage du texte. En eet, nous ne trouvions pas le moyen d'acher du texte, car Direct X ne présente pas de fonction appropriée à cette eet. Il était prévu de créer une police en palette de sprites destinée à être achée, mais ce moyen en restait néanmoins très lourd, et peu adapté (sauf pour les noms de planètes). Nous avons tout de même ni par trouver le moyen d'acher ces polices de caractères en utilisant simplement les fonctions windows de Delphi. 2.4.2 Les sprites d'unités An de mieux visualiser le nombre d'unités présentes sur chaque planète, en plus du nombre qui sera aché à côté de la planète, sera aché un petit icône d'unité de la couleur du joueur à qui appartient les forces en présence, EPITA 16 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale qui pourra varier selon le nombre d'unités présentes. Pour le moment comme convenu : rouge pour l'Alliance Rebelle, et bleu pour l'Empire Galactique. EPITA 17 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale 3 L'achage graphique 3.1 L'Achage Graphique [Direct X sous Direct Draw] Il a été décidé an d'optimiser grandement les performances du jeu d'implémenter toute la partie code sous forme de classes d'objets Delphi. De cette manière, nous avons rudement et proprement découpé les pages de codes sous diérentes classes, à savoir : - Classe Graphique, qui regroupe tout ce qui a trait à DirectDraw, aux sprites et à leur achage. Toutes les surfaces DirectDraw sont implémentées à partir de cette classe, et en constituent les objets. - Classe Main, qui comprend les principales procédures du programme, procédures constituant son squelette et son initialisation. - Classe Cong, regroupant les procédures d'enregistrement et de gestion des paramètres de jeu. - Classe Joueur, comprenant les procédures liées à la gestion des périphériques, et donc constituant en grande partie l'interface joueur/machine (IHM). 3.1.1 Les Classes et Objets Delphi La Programmation Orientée Objets, mieux connue sous l'acronyme de P.O.O, est très largement répandue et utilisée actuellement. Les objets répondent à ce besoin, en apportant une foule de possibilités très puissantes qui en font un passage presque obligé dans la majorité des gros projets développés sous Delphi. Concrètement, un objet est une donnée possédant une structure complexe. On utilisera les objets en utilisant des variables Pascal Objet, comme cela se fait déjà avec les autres données classiques telles les chaînes de caractères ou les pointeurs. Ces variables contiennent, puisque c'est leur principe de base, des données et du code Pascal Objet permettant de les traiter. C'est une des nouveautés marquantes des objets : on peut y stocker des données mais aussi des instructions Pascal Objet. es données consistent en des variables de n'importe type (y compris des objets). Le code Pascal Objet est réparti dans des procédures et fonctions nommées méthodes (c'est leur dénomination dans le monde des objets). Une classe, un peu comme le fait un type pour une variable, détermine entièrement la structure des objets de cette classe. Plus précisément, c'est la classe qui dénit les méthodes et les données (mais pas leurs valeurs) contenues dans les futurs objets de cette classe. Elle dénit les noms et les types des données mais pas leurs valeurs. Chaque objet a ses données dont les noms et types sont déterminées par la classe, mais dont les valeurs sont indépendantes pour chaque objet. EPITA 18 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale 3.1.2 Procédures de la classe Graphique Au départ nous comptions acher de façon classique les sprites, c'est à dire en les collant sur fond de vert (0,255,0). Comme Direct Draw n'ache que des formes rectangulaires, il s'agit de colorier de cette couleur les zones à ne pas acher, et de l'indiquer dans les procédures d'achage. Les images bitmaps ont donc été crées sur fond de Vert 255. Mais il peut résulter quelques problèmes suivant les cartes graphiques et les conguration individuelles des utilisateurs, qui peuvent coder les couleurs diéremment, comme par exemple sur 15 bits, ou encore agir de façon non attendue sur ces mêmes couleurs, et donc sur ce Vert 255. Il en résulterait donc que Direct Draw ne les acherait pas, ce pourquoi nous avons utilisé de nouvelles procédures qui permettent de choisir la couleur de transparence diéremment : le moteur graphique choisit donc la couleur du premier pixel de la surface graphique à acher comme couleur de transparence pour le panneau de contrôle, sachant que ce pixel ne doit pas être aché. De même pour les autres éléments graphiques, en prenant des pixels qui ne doivent pas apparaître. Une grande partie des fonctions d'initialisation ont évidemment dues être trouvées sur la toile. Types et Variables de la classe : type Tgraphique = class private FDD: IDirectDraw; // Interface 1 de DirectDraw FDD2: IDirectDraw2; // Interface 2 de DirectDraw FPrimary: IDirectDrawSurface; // Surface Primaire representant l'ecran FBack: IDirectDrawSurface; // Surface representant le BackBuffer FCarte: IDirectDrawSurface; // Surface Hors-Ecran (Offscreen) contenant notre fond FPanneau: IDirectDrawSurface; FSprite: IDirectDrawSurface; // Surface Hors-Ecran contenant notre sprite public Pos_x, Pos_y, deplacement_x, deplacement_y: Integer; procedure DDInitilization(Handle : THandle); // Inialisation de DirectDraw procedure LoadBP(var surface : IDirectDrawSurface; chemin : String); procedure AfficheBP(var surface : IDirectDrawSurface; x, y, posx, posy, tailleh, taillev : Integer); EPITA 19 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale procedure DDDisplay; // Affichage procedure aff_text(str : String; x, y : Integer); procedure aff_rect(x1,y1,x2,y2 : Integer); procedure DDFinalization; // Libération des objets end; var graph : Tgraphique; Toute cette implémentation a servi à initialiser les diérents types que Direct Draw a besoin pour fonctionner. Ces types sont inscrits par défaut dans les headers directX à installer dans le répertoire lib de Delphi. procedure Tgraphique.DDInitilization(Handle : THandle); var DDSD: TDDSurfaceDesc; // variable de Description de Surface begin DirectDrawCreate(nil, FDD, nil); // Recupere l'interface 1 de DirectDraw dans FDD depuis la DLL FDD.QueryInterface(IID_IDirectDraw2, FDD2); //Recupere l'interface 2 de DirectDraw dans FDD2 depuis l'interface 1 FDD2.SetCooperativeLevel(Handle, DDSCL_EXCLUSIVE + DDSCL_FULLSCREEN + DDSCL_ALlOWREBOOT); // Mode Exclusif et Plein ecran FDD2.SetDisplayMode(WIDTH_SCREEN, HEIGHT_SCREEN, BIT_DEPTH, 0, 0); // Change de resolution DDSD.dwSize := SizeOf(TDDSUrfaceDesc); // Met a jour la taille du Record DDSD.dwFlags := DDSD_CAPS + DDSD_BACKBUFFERCOUNT; // indique les champs à consulter lors de la creation de la surface: CAPS et BACKBUFFERCOUNT DDSD.ddsCaps.dwCaps := DDSCAPS_COMPLEX + DDSCAPS_FLIP + DDSCAPS_PRIMARYSURFACE; // Capacités de la surface DDSD.dwBackBufferCount := 2; // Un seul backBuffer associe à la surface primaire FDD2.CreateSurface(DDSD, FPrimary, nil); // Creation de la surface primaire DDSD.ddsCaps.dwCaps := DDSCAPS_BACKBUFFER; // Capacite de Back Buffer FPrimary.GetAttachedSurface(DDSD.ddsCaps, FBack); // Creation du BackBuffer associé a la surface primaire EPITA 20 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale loadBP(FCarte, NAME_BACKGROUND); loadBP(FPanneau, PANNEAU_DE_CONTROLE); loadBP(FSprite, BOUTON_QUITTER); // Assignation d'une couleur de transparence (RGB 0 0 0)(Noir) Pos_x:=0; Pos_y:=0; end; Cette procédure est simplement la procédure qui permet d'initialiser tout ce qui aura trait à DirectX, an de permettre à DirectDraw d'accéder à tout ce qui lui sera nécessaire. procedure Tgraphique.LoadBP(var surface : IDirectDrawSurface; chemin : String); var wrkbmp:tbitmap; hret:hresult; ddsd:ddsurfacedesc; TempSurface: IDirectDrawSurface; begin wrkbmp := tbitmap.Create; surface := nil; if not FileExists(chemin) then exit; wrkbmp.LoadFromFile(chemin); //creating surface fillchar(ddsd,sizeof(ddsd),0); ddsd.dwSize := sizeof(ddsd); ddsd.dwFlags := ddsd_caps or ddsd_height or ddsd_width; ddsd.ddsCaps.dwCaps := ddscaps_offscreenplain or ddscaps_videomemory; ddsd.dwWidth := wrkbmp.Width; ddsd.dwHeight := wrkbmp.Height; hret := FDD2.CreateSurface(ddsd,TempSurface,nil); if hret<>dd_ok then begin //Erreur_msg('create particle surface'); exit; end; try {we can only get a direct draw surface 3 interface through the QueryInterface method of the direct draw surface object} TempSurface.QueryInterface(IID_IDirectDrawSurface, surface); finally {now that we have the direct draw surface 3 object, the EPITA 21 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale temporary direct draw surface object is no longer needed} TempSurface := nil; end; //loading bitmap //result := ddloadbitmap(FDD2,chemin,wrkbmp.Width,wrkbmp.Height); //if result = nil then Erreur_msg('ddloadbitmap'); CopyBitmap(surface, wrkbmp, 0, 0, wrkbmp.Width, wrkbmp.Height); //setting the color key for the offscreen surface (upper left corner pixel color) hret:=ddsetcolorkey(surface,rgb(getrvalue(wrkbmp.Canvas.Pixels[0,0]), getgvalue(wrkbmp.Canvas.Pixels[0,0]),getbvalue(wrkbmp.Canvas.Pixels[0,0]))); if failed(hret) then begin //Erreur_msg('ddsetcolorkey'); exit; end; //wrkint:=wrkbmp.Width div 16; wrkbmp.Free; end; Cette procédure permet de charger une image de type bitmap dans la mémoire attribuée par Direct Draw. procedure Tgraphique.DDDisplay; var R1,R2: TRect; begin AfficheBP(FSprite,10,10,0,0,64,64); aff_text('Salut', 10,10); R1 := Rect(Pos_x, Pos_y, WIDTH_SCREEN_IMG+Pos_x, HEIGHT_SCREEN_IMG-HEIGHT_PANNEAU_BAS+Pos_y); // Rectangle de selection faisant tout l'ecran R2 := Rect(0, 0,WIDTH_SCREEN , HEIGHT_SCREEN - HEIGHT_PANNEAU_BAS + 30); FBack.Blt(@R2, FCarte, @R1, DDBLT_WAIT, nil); // copie du contenu de la surface FBackground dans le BackBuffer R1 := Rect(0, 0, WIDTH_PANNEAU_BAS, HEIGHT_PANNEAU_BAS); // Rectangle de selection faisant tout l'ecran R2 := Rect(0, HEIGHT_SCREEN - HEIGHT_PANNEAU_BAS,WIDTH_SCREEN,HEIGHT_SCREEN); FBack.Blt(@R2, FPanneau, @R1, DDBLT_WAIT or DDBLT_KEYSRC , nil); aff_rect(700,60,750,90); EPITA 22 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale FPrimary.Flip(nil, DDFLIP_WAIT); // Basculement du contenu du BackBuffer dans la surface Primaire => affichage end; La procédure Tgraphique.DDDisplay copie les images entrées dans la mémoire (FBackground) par le biais de rectangles de sélection, vers le BackBuer (un intermédiaire qui permet de ne pas acher directement à l'écran, sans quoi il y aurait conit avec ses propres routines). Les images copiées sont ensuite achées à l'écran : ici le panneau de contrôle. procedure Tgraphique.aff_text(str : String; x, y : Integer); var h_DC : HDC; font : HFONT; TempSurface : IDirectDrawSurface; ddsd : ddsurfacedesc; fx:TDDBltFX; R : TRECT; begin fillchar(ddsd,sizeof(ddsd),0); ddsd.dwSize := sizeof(ddsd); ddsd.dwFlags := ddsd_caps or ddsd_height or ddsd_width; ddsd.ddsCaps.dwCaps := ddscaps_offscreenplain or ddscaps_videomemory; ddsd.dwWidth := WIDTH_SCREEN_IMG; ddsd.dwHeight := HEIGHT_SCREEN_IMG; FDD2.CreateSurface(ddsd,TempSurface,nil); FillChar(fx, SizeOf(fx), 0); // clear the structure fx.dwSize := SizeOf(fx); // and set its dwSize field fx.dwFillColor := RGB(0,255,0); // set the colour for our fill TempSurface.Blt(nil, nil, nil, DDBLT_WAIT or DDBLT_COLORFILL, @fx); ddsetcolorkey(TempSurface,rgb(0,255,0)); if TempSurface.GetDC(h_DC) = DD_OK then begin font := CreateFont (45, 0, 0, 0, FW_BOLD, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 'verdana'); SelectObject(h_DC,font); SetBkColor(h_DC, RGB(0, 255, 0));//RGB(150, 150, 150) SetTextColor(h_DC, RGB(0, 0, 0)); TextOut(h_DC, 0, 0,pchar(str),strlen(pchar(str))); DeleteObject(font); R := Rect(0,0,WIDTH_SCREEN_IMG,HEIGHT_SCREEN_IMG) ; FCarte.BltFast(x,y, TempSurface, @R, DDBLTFAST_SRCCOLORKEY); EPITA 23 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale TempSurface.ReleaseDC(H_DC); end; end; Procédure permettant d'acher du texte. procedure Tgraphique.DDFinalization; begin // incrementation du compteur de reference pour liberation (COM) if assigned(FCarte) then FCarte._AddRef; if assigned(FPanneau) then FPanneau._AddRef; if assigned(FSprite) then FSprite._AddRef; if assigned(FBack) then FBack._AddRef; if assigned(FPrimary) then FPrimary._AddRef; if assigned(FDD2) then FDD2._AddRef; if assigned(FDD) then FDD._AddRef; end; Enn, la procédure permettant de mettre n à la routine engrandée par l'initialisation de DirectX. Toute cette implémentation de base au niveau de l'achage par directX permet maintenant d'acher la carte en fond d'écran, de se déplacer sur cette même carte grâce aux fonctions exposées lors de la première soutenance, mais aussi d'acher un rectangle d'indication de position sur la minimap du panneau de contrôle. De plus, grâce aux procédures d'achage des images bitmap, nous pourrons maintenant acher tous les éléments graphiques disponibles. 3.2 Place à l'achage... Le troisième chier appelé est le chier graphique.pas. Comme son nom l'indique, cette partie du programme va gérer toute la partie achage du jeu. Tout d'abord, elle va enregistrer les coordonnées de la souris. Par de multiples opérations, elle va ensuite gérer le déplacement de la map, ainsi que toutes les actions qu'engendrent les cliques aux divers endroits de l'écran EPITA 24 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale à divers moments du jeu. Ce qui va suivre permet d'envoyer à l'écran les renseignements textuels dont le joueur a besoin. Une form de la taille de l'écran est entièrement remplis de noir. Puis, en fonction des besoins, la form va se remplir de pixels de couleurs formant le texte que l'on souhaite aché. Dernière étape : la superposition. La map est achée à l'écran, puis le panneau d'achage, et enn la form précédente, APRES UN LEGER CHANGEMENT. En eet, tous les pixels noirs de la form NE SONT PAS CALQUES SUR LE RESTE. Ainsi, seul s'ache le message que l'on souhaitait. Dernier élément à prendre en compte : L'achage est sujet à maintes erreurs, et les fonctions imbriquées nous empêchent de les détecter rapidement. Pour éviter cela, si une erreur survient, la fonction s'arrête avec une variable conservant l'identiant de l'erreur. Toutes les fonctions qui possédaient cette fonction à leur tour vont s'arrêter en conservant l'identiant. Ce dernier est ainsi conservé en remontant jusqu'à une fonction Erreur qui termine proprement le programme et ache l'identiant en question. Voici la partie hautement digeste ! ! ! Bon courage... : function Tgraphique.init_texte : Hresult; var ddbltfx : TDDBLTFX; ddsd: TDDSurfaceDesc; begin fillchar(ddsd,sizeof(ddsd),0); //creating surface ddsd.dwSize := sizeof(ddsd); ddsd.dwFlags := ddsd_caps or ddsd_height or ddsd_width; ddsd.ddsCaps.dwCaps := ddscaps_offscreenplain or ddscaps_videomemory; ddsd.dwWidth := WIDTH_SCREEN; ddsd.dwHeight := HEIGHT_SCREEN; result := FDD2.CreateSurface(ddsd,Ftexte,nil); if result<>dd_ok then exit; FillChar( ddbltfx, SizeOf( ddbltfx ), 0 ); //ZeroMemory( @ddbltfx, SizeOf( TDDBLTFX ) ); ddbltfx.dwSize := SizeOf( ddbltfx ); ddbltfx.dwFillColor := RGB(0,0,0);//dwColor; result := Ftexte.Blt( nil, nil, nil, DDBLT_COLORFILL, @ddbltfx ); if result<>dd_ok then EPITA 25 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale exit; result := DDSetColorKey(Ftexte, RGB(0,0,0)); if result<>dd_ok then exit; //Création des police d'affichages ! {CreateFont( int nHeight,// logical height of font int nWidth,// logical average character width int nEscapement,// angle of escapement int nOrientation,// base-line orientation angle int fnWeight,// font weight DWORD fdwItalic,// italic attribute flag DWORD fdwUnderline,// underline attribute flag DWORD fdwStrikeOut,// strikeout attribute flag DWORD fdwCharSet,// character set identifier DWORD fdwOutputPrecision,// output precision DWORD fdwClipPrecision,// clipping precision DWORD fdwQuality,// output quality DWORD fdwPitchAndFamily,// pitch and family LPCTSTR lpszFace // pointer to typeface name string );} //petite police rouge ptrg.font := CreateFont(13,11,0,0,0 ,0,0,0,0,OUT_CHARACTER_PRECIS ,0,0 ,FF_MODERN,'Courier New'); ptrg.transparent := True; ptrg.couleur := RGB(255,0,0); //Rouge //Grande police rouge grdrg.font := CreateFont(22,18,0,0, FW_BOLD,0,0,0,0,OUT_CHARACTER_PRECIS ,0,0 ,FF_MODERN,'Comic'); grdrg.transparent := True; grdrg.couleur := RGB(255,0,0); //Rouge //petite police verte ptgr.font := CreateFont(13,11,0,0,0 ,0,0,0,0,OUT_CHARACTER_PRECIS ,0,0 ,FF_MODERN,'Courier New'); ptgr.transparent := True; ptgr.couleur := RGB(0,255,0); //vert //Grande police verte grdgr.font := CreateFont(22,12,0,0,FW_BOLD ,0,0,0,0,OUT_CHARACTER_PRECIS ,0,0 ,FF_MODERN,'Courier New'); grdgr.transparent := True; grdgr.couleur := RGB(0,255,0); //vert //Info bulle EPITA 26 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale infbll.font := CreateFont(16,12,0,0,0 ,0,0,0,0,OUT_CHARACTER_PRECIS ,0,0 ,FF_MODERN,'Times new Roman'); infbll.transparent := False; infbll.couleur := RGB(2,2,2); //gris infbll.backcolor := RGB(200,200,0); //Jaune //, grdrg, ptgr, grdgr, infbll result := dd_ok; end; ___________________________________________________________________ function Tgraphique.verif_coord(px,py : Integer) : Boolean; begin result := (abs(px - coord.cxy.x) < 30) AND (abs(py - coord.cxy.y) < 30); end; ___________________________________________________________________ function Tgraphique.info_planete(var Planete : T_tplanete) : Hresult; var i : byte; begin for i := 1 to NB_PLANETE do if verif_coord(Planete[i].x, Planete[i].y) Then begin result := aff_text(Planete[i].nom, point(coord.xy.x + 32,coord.xy.y), infbll ); if result <> DD_OK Then exit; result := aff_text(inttostr(Planete[i].unite) + ' unités', point(coord.xy.x + 32,coord.xy.y + 14), infbll ); if result <> DD_OK Then exit; result := aff_text(gettypejoueur(Jeu.Joueur.joueur[Planete[i].joueur].typejoueur) + ' : ' + Jeu.Joueur.joueur[Planete[i].joueur].nom , point(coord.xy.x + 32,coord.xy.y + 28), infbll ); if result <> DD_OK Then exit; if coord.clic_souris then affich_pan_dr(i); end; if (coord.pxy.x > 2) and (coord.pxy.x < 14) and (coord.pxy.y > 42) EPITA 27 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale and (coord.pxy.y < 58) then result := aff_text(' Piste suivante ', point(coord.xy.x + 22,coord.xy.y), infbll ) else if (coord.pxy.x > 2) and (coord.pxy.x < 14) and (coord.pxy.y > 59) and (coord.pxy.y < 70) then result := aff_text(' Piste précédente ', point(coord.xy.x + 22,coord.xy.y), infbll ); if result <> DD_OK Then exit; result := DD_OK end; ___________________________________________________________________ function Tgraphique.affich_pan_dr(plan : byte) : Hresult; var i : integer; begin if (Jeu.Tour.Etape = DEPLOYEMENT) AND (Jeu.Tour.unite > 0) AND (Jeu.Tour.Joueur = Jeu.Planete[plan].Joueur) Then begin Jeu.Planete[plan].unite := Jeu.Planete[plan].unite + 1; Jeu.Tour.unite := Jeu.Tour.unite - 1; end else if (Jeu.Tour.Etape = COMBAT) AND (NOT Jeu.Tour.Attaque.IsComba) Then begin if (Jeu.Planete[plan].Joueur = Jeu.Tour.Joueur) AND (Jeu.Joueur.joueur[Jeu.Planete[plan].Joueur].typejoueur = HUMAIN) AND (Jeu.Tour.Attaque.planete_depart = 0) AND (Jeu.Planete[plan].unite > 1) Then Jeu.Tour.Attaque.planete_attaque := plan else if (Jeu.Tour.Attaque.planete_attaque <> 0) AND (Jeu.Planete[plan].Joueur <> Jeu.Tour.Joueur) then begin i:=0; while (i <= NB_LIAISONS) AND(Jeu.Planete[plan].liaisons[i]<>Jeu.Tour.Attaque.planete_attaque)do begin i := i+1; end; if i <= NB_LIAISONS then Jeu.Tour.Attaque.planete_defence := plan; EPITA 28 InfoSup B2 Unbound Value end; end; Star Wars Risk Soutenance Finale end ___________________________________________________________________ function Tgraphique.efface_texte : Hresult; var ddbltfx: TDDBLTFX; begin //Efface tous les texte !!! FillChar( ddbltfx, SizeOf( ddbltfx ), 0 ); //ZeroMemory( @ddbltfx, SizeOf( TDDBLTFX ) ); ddbltfx.dwSize := SizeOf( ddbltfx ); ddbltfx.dwFillColor := RGB(0,0,0); result := Ftexte.Blt( nil, nil, nil, DDBLT_COLORFILL, @ddbltfx ); if result<>dd_ok then begin //Erreur_msg('create particle surface'); exit; end; result := dd_ok; end; ___________________________________________________________________ procedure Tgraphique.mini_map; var x, y : word; begin x := coord.pxy.x; y := coord.pxy.y; if (x <> 0) AND (y <> 0) AND (x >= LONGUEUR_G) AND (x <= LONGUEUR_G + WIDTH_MINIMAP ) AND (y >= LONGUEUR_B) AND (y <= LONGUEUR_B + HEIGHT_MINIMAP ) Then //le curseur est sur la mini-map ! begin x := x - LONGUEUR_G; if x >= WIDTH_MINIMAP - WIDTH_SCREEN_MINIMAP Then x := WIDTH_MINIMAP - WIDTH_SCREEN_MINIMAP; pos_x := x*WIDTH_IMG div WIDTH_MINIMAP; y := y - LONGUEUR_B; if y >= HEIGHT_MINIMAP - HEIGHT_SCREEN_MINIMAP Then y := HEIGHT_MINIMAP - HEIGHT_SCREEN_MINIMAP; pos_y := y*HEIGHT_IMG div HEIGHT_MINIMAP; EPITA 29 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale end; end; ___________________________________________________________________ function Tgraphique.afficher : Hresult; begin //Gérer la souris result := getsouris; if result <> DI_OK Then exit; result := RestoreSurfaces; //restauration des surfaces Direct Draw si on a perdu les surfaces ! if result <> DD_OK Then exit; result := efface_texte; if result <> DD_OK Then exit; //modifier les coordonnée deplacement; if coord.clic_souris Then gestion_clic; //gérer l'affichage du texte aff_text(getetat(Jeu.Tour.Etape), conv_pxy_xy(point(190,50)),ptrg); //étape du jeu aff_text(Jeu.Joueur.joueur[Jeu.Tour.Joueur].nom, conv_pxy_xy(point(190,64)),ptrg); //étape du jeu if Jeu.Tour.unite > 0 Then aff_text(inttostr(Jeu.Tour.unite) + ' unité(s) à répartir', conv_pxy_xy(point(190,78)),ptgr); if Jeu.Tour.Attaque.planete_defence <> 0 Then aff_text(Jeu.Planete[Jeu.Tour.Attaque.planete_attaque].nom + ' attaque ' + Jeu.Planete[Jeu.Tour.Attaque.planete_defence].nom , conv_pxy_xy(point(190,78)),ptgr); if Jeu.Tour.Attaque.gain_attaque <> 0 Then aff_text('Attaquant perd ' + inttostr(Jeu.Tour.Attaque.gain_attaque) + ' unité(s)', conv_pxy_xy(point(190,92)),ptgr); if Jeu.Tour.Attaque.gain_defence <> 0 Then aff_text('Défenceur perd ' + inttostr(Jeu.Tour.Attaque.gain_defence) + ' unité(s)', conv_pxy_xy(point(190,106)),ptgr); aff_text('QUITTER', conv_pxy_xy(point(240,123)), grdgr); EPITA 30 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale aff_text(Music.sound_title, conv_pxy_xy(point(40,135)), ptrg); aff_text('>', conv_pxy_xy(point(4,45)), grdrg); aff_text('<', conv_pxy_xy(point(4,60)), grdrg); //aff_musique_position(Music.sound_get_pos); if (Jeu.Tour.Attaque.planete_attaque <> 0) then begin aff_text(Jeu.Planete[Jeu.Tour.Attaque.planete_attaque].nom , conv_pxy_xy(point(420,50)), ptgr); aff_text('vs', conv_pxy_xy(point(445,60)), ptgr); end; if (Jeu.Tour.Attaque.planete_defence <> 0) then aff_text(Jeu.Planete[Jeu.Tour.Attaque.planete_defence].nom, conv_pxy_xy(point(420,70)), ptgr); aff_planete(Jeu.Planete); info_planete(Jeu.Planete); result := flip; if result <> DD_OK Then exit; end; ___________________________________________________________________ function Tgraphique.conv_pxy_xy(xy : Tpoint) : Tpoint; begin result.x := WIDTH_SCREEN*xy.x div WIDTH_SCREEN_IMG; result.y := HEIGHT_SCREEN*xy.y div HEIGHT_SCREEN_IMG + (HEIGHT_SCREEN - HEIGHT_PANNEAU_BAS*HEIGHT_SCREEN div HEIGHT_SCREEN_IMG); end; //modifier cette fonction pour récuperer les coordonées de direct input function Tgraphique.getsouris : Hresult; begin result := Inp.coord(coord.xy, coord.clic_souris ); if result <> DI_OK Then exit; coord.pxy := conv_xy_pxy( coord.xy ); coord.cxy := conv_xy_cxy( coord.xy ); end EPITA 31 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale 4 Le moteur de jeu 4.1 L'éditeur XML Petit programme annexe créé par Patrice, l'éditeur XML de planètes permet de gérer un type spécique de chier XML, prédéni avec les balises adéquates, comprenant les soixante planètes elles aussi prédénies à partir des balises <planète> et </planète>, pouvant être associées de façon homologue aux balises <tr> et </tr>. A l'intérieur de ces balises, sont implémentées les balises <nom> et </nom>, <coords> et </coords>, ainsi que <liaisons> et </liaisons>, qui peuvent être associés aux balises standards HTML <td> et </td>, mais qui constituent en plus une sorte d'enregistrement dans un tableau : - <Planète>. Constitue la structure XML d'une planète, et englobe toutes les données internes telles que les coordonnées, le nom et les liaisons. - <Nom>. Introduit le nom de la planète, de type string, mais référé à un index de type entier (un nombre par planète). - <Coords>. Introduit les coordonées de la planète, de type entier. - <Liaisons>. Introduit un autre enregistrement de laisons pour la planète spéciée, de type enregistrement d'entiers (les index des autres noms de planètes). <nb_planete>60</nb_planete> <Planete> <nom>Abregado-Rae</nom> <coordx>739</coordx> <coordy>1186</coordy> <liaisons> <liaison>4</liaison> <liaison>14</liaison> <liaison>15</liaison> <liaison>25</liaison> <liaison>51</liaison> </liaisons> </Planete> <Planete> <nom>Alderaan</nom> <coordx>1209</coordx> <coordy>894</coordy> <liaisons> <liaison>11</liaison> <liaison>12</liaison> <liaison>37</liaison> EPITA 32 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale </liaisons> </Planete> ... ... L'éditeur XML de planètes est capable de lire ce chier, mais ensuite de le transformer une fois les modications eectuées. Il comprend donc une sous-fenêtre achant les diérentes planètes (à partir de la balise <planète>). Une autre sous-fenêtre ache une case modiable, qui est le nom de la planète. De même ensuite pour chaque coordonnée x et y. La troisième sous-fenêtre est en fait légèrement diérente, comportant un menu déroulant. Ce menu déroulant permet de choisir un nombre égal à cinq liaisons vers d'autres planètes. Ce menu déroulant ache donc les noms de toutes les planètes entrées pour le moment, an de créer des liaisons (le chier XML de départ est xé à 60 planètes et donc 60 balises <planète> dénies, an que la première sous-fenêtre ache 60 possibilités modiables, même vides). De la sorte, chaque planète se voit associée ses caractéristiques propres et nécessaires pour le jeu (ici déterminées par les programmeurs). Ainsi, La base de données concernant les planètes est prête à être utilisée. Aucun problème majeur n'a par ailleurs été rencontré lors de l'implémentation de cet éditeur, sauf peut-être un petit bug qui faisait que les liaisons étaient mal acceptées et souvent "remodelées" à la convenance de l'éditeur... Le bug fut bien sûr corrigé... 4.1.1 Les procédures de lecture XML Les procédures qui gèrent la lecture des chiers XML, entre autres le chier planétaire, ou encore les chiers de sauvegarde des données, utilisateur et logicielles, ou bien d'autres achages comme pour les planètes, seront implémentées très bientôt, c'est à dire dès la seconde soutenance passée. Voici quelques extraits du code de l'éditeur : T_planete = record //type de donnée pour mettre toutres nom : String; //les informations sur les planetes x : Integer; y : Integer; liaisons : array[1..NB_LIAISONS] of Integer; end; T_tplanete = array[1..NB_PLANETE] of T_planete; var Formxml : TFormxml; EPITA 33 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale Planete : T_tplanete; tab_liaison : array[1..NB_LIAISONS] of Integer; tab_liaison_nb, item_modif : Integer; Il s'agit ici de l'implémentation des types dénis par l'éditeur XML. Ces types se réfèrent à ce qui a été dit plus haut au niveau des balises. procedure get_balise(var xml, nom, contenu : String); //renvoie le nom de la balise courante (1ere Trouvée) var bal : String; pos, posf : Integer; begin while (length(xml) <> 0) AND (xml[1] <> '<') do delete(xml,1,1); pos := 2; nom := ''; while (length(xml) <> 0) AND (xml[pos] <> '>') do //recupère le nom de la balise begin nom := nom + xml[pos]; inc(pos); end; inc(pos); bal := '</'+nom+'>'; posf := AnsiPos(bal, xml); //posf := posf - length(bal); contenu := MidStr(xml, pos, posf - length(bal)); xml := rightStr(xml, length(xml) - posf - length(bal) + 1); end; La procédure principale get balise permet simplement de lire les strings xml, d'enlever les balises et de retourner l'intérieur de ces balises. procedure load_planete(var Planete : T_tplanete; xml : String); var contenu, nom, planetecontenu : String; nb, i : Integer; begin nb := 1; while xml <> '' do begin get_balise(xml, nom, planetecontenu); //writeln(xml); while planetecontenu <> '' do begin get_balise(planetecontenu, nom, contenu); EPITA 34 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale //writeln(contenu); get_planete(Planete, nb, nom, contenu); end; nb := nb + 1; end; end; Cette procédure permet de charger les informations liées à une planète dans la forme graphique. procedure readxml_planete(name : String); var filexml : TextFile; lig, xml, contenu, nom, planetecontenu : String; nb, i : Integer; begin AssignFile(filexml, name); reset(filexml); xml := ''; while not eof(filexml) do begin //cette boucle répètera la lecture d'une ligne tant que nous n'aurons pas atteint la fin du document EOF readln(filexml,lig);//à chaque fois que l'on utilisera readln on passera à la ligne suivante. xml := xml + supespas(lig); // ajoute notre ligne au memo end; closefile(filexml); try get_balise(xml, nom, contenu); if nom = 'nb_planete' then begin load_planete(Planete, xml); end else MessageDlg('ERREUR DE FORMAT DU XML !', mtInformation,[mbOk],0); except MessageDlg('ERREUR DE FORMAT DU XML !', mtInformation,[mbOk],0); end; end; Procédure achant les propriétés d'un planète. procedure get_planete (var Planete : T_tPlanete; nb : Integer; nom, contenu : String); var EPITA 35 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale liai : String; i : Byte; begin if nom = 'nom' Then Planete[nb].nom := contenu; if nom = 'coordx' Then Try Planete[nb].x := StrToInt(contenu); Except Planete[nb].x := 0; end; if nom = 'coordy' Then Try Planete[nb].y := StrToInt(contenu); Except Planete[nb].y := 0; end; if nom = 'liaisons' Then for i := 1 to NB_LIAISONS do begin Try liai := ''; if (contenu <> '') Then get_balise(contenu, nom, liai); if (liai <> '') Then Planete[nb].liaisons[i] := StrToInt(liai) else Planete[nb].liaisons[i] := 0; Except on EConvertError do Planete[nb].liaisons[i] := 0; else Planete[nb].liaisons[i] := 0; end; end; end; Get planète permet de mémoriser les informations entrées pour la planète sélectionnée. function set_balise(nom, contenu : String) : String; begin result := '<' + nom + '>' + contenu + '</' + nom + '>'; end; EPITA 36 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale function set_planete(var Planete : T_tplanete; nb : Byte) : String; var str : String; i : Byte; begin result := #13#10#9 + set_balise('nom', Planete[nb].nom) + #13#10#9; result := result + set_balise('coordx', inttostr(Planete[nb].x)) + #13#10#9; result := result + set_balise('coordy', inttostr(Planete[nb].y)) + #13#10#9; str := #13#10#9; for i := 1 to NB_LIAISONS do if (Planete[nb].liaisons[i] <> 0 ) Then str := str + #9 + set_balise('liaison', inttostr(Planete[nb].liaisons[i])) + #13#10#9; result := result + set_balise('liaisons', str) + #13#10#9; end; Procédure permettant de régler les balises dans le XML. procedure savexml_planete(var Planete : T_tplanete; name : String); var filexml : TextFile; lig, xml, contenu, nom, planetecontenu : String; i : Byte; begin AssignFile(filexml, name); Rewrite(filexml); writeln(filexml, set_balise('nb_planete', IntToStr(NB_PLANETE))); for i := 1 to NB_PLANETE do begin writeln(filexml, set_balise('Planete', set_planete(Planete, i))); end; closeFile(filexml); end; Sauvegarde du chier XML. Procedure reset_planete(var Planete : T_tplanete); var i, j : Integer; begin for i := 1 to NB_PLANETE do begin Planete[i].nom := ''; Planete[i].x := 0; Planete[i].y := 0; EPITA 37 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale for j := 1 to NB_LIAISONS do Planete[i].liaisons[j] := 0; end; end; Remet les paramètres d'une planète par défaut. 4.2 Les premières fonctions An de visualiser l'allure générale du code règlant les règles du jeux, nous avons réalisé une ébauche de jeu fonctionnant sous mode console. Ainsi, le traitement et la mise en place des fonctions de base (exposées par la suite dans l'ordre d'implémentation sous Delphi 7) ont été facilités et le travail en a été restreint à l'implémentation du code même. De cette manière, le travail a pu être centré sur les diérentes fonctions de départ du jeu sans prendre en compte une quelconque interface graphique inutile au tout début du projet. Il faut préciser que ces fonctions ont été créées indépendamment des fonctions d'initialisation graphique, à partir de DirectX, qui, elles, permettront de lancer notre propre achage et de le lier par la suite aux autres fonctions du jeu. 4.2.1 Constantes, Types et Enregistrements Le nombre total de planètes sera xé à 60, car 60 est divisible par 2, 3, 4, 5 et 6 entraînant la possibilité d'une répartition des planètes entre 2, 3, 4, 5 ou 6 joueurs, ce qui conduit naturellement le nombre de joueurs maximum à 6(oui oui,on est très fort en calcul mental). Le nombre de liaisons par planète est xé à cinq, car au delà, la carte devient une véritable toile d'araignée, et en dessous, le jeu risque de se bloquer, notamment dans le noyau galactique, où la concentration de planètes est conséquente. Un type enregistrement a été mis en place pour le moment, qui est le type T-planete. De la sorte, chaque planète se voit associer plusieurs variables, soit plus précisément 6 : - La première concerne le nom de la planète, de type string. - La seconde prend en compte l'abscisse de la planète sur la carte (utile à la sélection de la planète). Type integer. - La troisième prend en compte l'ordonnée de la planète sur la carte (complément à l'abscisse, utile à la sélection de la planète). Type integer. - La quatrième prend en compte un tableau des liaisons vers les planètes voisines désignées. C'est cette variable que suivra la fonction de déplaEPITA 38 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale cement des unités, an de savoir vers quelle planètes elles peuvent être transférées. - La cinquième annonce soit le nom du joueur, de type string, ou le numéro du joueur, de type integer. - La sixième variable annonce le nombre d'unités présentes sur la planète. Un tableau regroupant les soixante planètes a également été conçu pour la suite du projet (Array of T-planete). T_planete = record //type de donnée pour mettre toutes nom : String; // les information sur les planete x : Integer; y : Integer; liaisons : array[1..NB_LIAISONS] of Integer; joueur : Byte; unite : Byte; end; T_tplanete = array[1..NB_PLANETE] of T_planete; 4.2.2 Procédure de Distribution Planétaire La procédure de distribution répartit les planètes entre les joueurs. Elle se lance donc dès le début d'une partie, et associe chaque planète à un joueur spécique. Elle compte le nombre de joueurs, puis passe au joueur 1, lui associe une planète de manière aléatoire, puis passe au joueur 2, fait de même, et recommence jusqu'à ce qu'il ne reste plus de planète à associer. Cette procédure utilise deux boucles : l'une fait alterner les deux joueurs, l'autre, imbriquée, décompte les planètes attribuées. Procedure joueur_init(var Planete: T_tplanete; joueur: Byte); var n: Byte; i: Byte; m: Byte; p: Byte; begin Randomize(); p:= NB_PLANETE div joueur; for n:= 1 to joueur do //boucle sur les joueurs for i:=1 to p do // cette boucle choisis a chaque boucle une planete begin repeat // boucle jusqu'a trouver une planete m:= Random(NB_PLANETE)+1; until Planete[m].joueur=0; EPITA 39 InfoSup B2 Unbound Value end; Star Wars Risk Soutenance Finale Planete[m].joueur:= n; end; 4.2.3 Fonction-Procédure d'Achage Planétaire Cette fonction ache les planètes appartenant au joueur juste après la répartition. Elle boucle les planètes et regarde à quel joueur elles appartiennent, une par une, et les ache le cas échéant. Elle propose ensuite par une procédure au joueur de sélectionner une des planètes lui appartenant. Procedure IHM(var Planete: T_tplanete); var i: Byte; begin writeln('Repartition des planetes'); for i:= 1 to NB_PLANETE do begin writeln(i,') ',Planete[i].nom,'(Joueur ',Planete[i].joueur,')'); end; end; 4.2.4 Génèse Cette dernière procédure, qui est en fait la première à agir, crée les planètes et leur associe une place dans le tableau de planètes (T-tab). Ainsi, elle passe la main à la fonction de distribution. Procedure init(); var Planete : T_tplanete; //variable qui contient toutes les infos i: Byte; //variable de boucle begin Writeln(''); Writeln(''); writeln(' ------------------'); Writeln(' - Risk Star Wars -'); writeln(' ------------------'); Planete[1].nom := 'Coruscant'; //initialisation de toutes les Planete[1].liaisons[1] := 2; //Planettes avec leurs liasons Planete[1].liaisons[2] := 3; Planete[1].liaisons[3] := 7; Planete[1].liaisons[4] := 8; EPITA 40 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale Planete[2].nom := 'Tatoine'; Planete[2].liaisons[1] := 1; Planete[2].liaisons[2] := 3; Planete[2].liaisons[3] := 10; Planete[2].liaisons[4] := 5; Planete[3].nom := 'Endor'; Planete[3].liaisons[1] := 1; Planete[3].liaisons[2] := 2; Planete[3].liaisons[3] := 6; Planete[3].liaisons[4] := 12; Planete[4].nom := 'Bakura'; Planete[4].liaisons[1] := 9; Planete[4].liaisons[2] := 11; Planete[4].liaisons[3] := 5; Planete[4].liaisons[4] := 2; Planete[5].nom := 'Dagobah'; Planete[5].liaisons[1] := 3; Planete[5].liaisons[2] := 2; Planete[5].liaisons[3] := 8; Planete[5].liaisons[4] := 6; Planete[6].nom := 'Géonosis'; Planete[6].liaisons[1] := 3; Planete[6].liaisons[2] := 5; Planete[6].liaisons[3] := 9; Planete[6].liaisons[4] := 11; Planete[7].nom := 'Agamar'; Planete[7].liaisons[1] := 1; Planete[7].liaisons[2] := 10; Planete[7].liaisons[3] := 11; Planete[7].liaisons[4] := 12; Planete[8].nom := 'Yavin'; Planete[8].liaisons[1] := 1; Planete[8].liaisons[2] := 5; Planete[8].liaisons[3] := 9; Planete[8].liaisons[4] := 11; Planete[9].nom := 'Naboo'; Planete[9].liaisons[1] := 4; Planete[9].liaisons[2] := 6; Planete[9].liaisons[3] := 8; Planete[9].liaisons[4] := 10; Planete[10].nom := 'Dathomir'; Planete[10].liaisons[1] := 2; Planete[10].liaisons[2] := 7; Planete[10].liaisons[3] := 9; EPITA 41 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale Planete[10].liaisons[4] := 12; Planete[11].nom := 'Dantoine'; Planete[11].liaisons[1] := 4; Planete[11].liaisons[2] := 6; Planete[11].liaisons[3] := 7; Planete[11].liaisons[4] := 8; Planete[12].nom := 'Dath'; Planete[12].liaisons[1] := 3; Planete[12].liaisons[2] := 7; Planete[12].liaisons[3] := 10; Planete[12].liaisons[4] := 0; for i:= 0 to NB_PLANETE do begin Planete[i].joueur := 0; Planete[i].unite := 1; end; joueur_init(Planete,NB_JOUEUR); IHM(Planete); readln; Writeln('Répartition des troupes'); for i:=1 to NB_JOUEUR do begin writeln('Joueur ',i); joueur_unite(Planete,10,i); //repartition initial des troupes end; main(Planete); //passe la main à la fonction main end; 4.2.5 Répartition des unités Cette procédure est naturellement nécessaire pour la partie renfort. Elle nous indique le nombre de troupes disponible, puis passe en revue chaque planète en demandant au joueur combien d'unités il désire positionner sur cette planète. De plus, le nombre d'unités restantes est à chaque fois réafcher, et l'on ne peut mettre plus d'unités que ce qui nous est permis. On ne pouvais revenir sur une planète que nous avions déjà "visitée" et on ne pouvait choisir la planète désirée : elle nous était obligatoirement desservie. Elle sera donc bien évidemment révisée pour les besoins du jeu. Procedure joueur_unite(var Planete : T_tplanete; unite , joueur : Word); var i, num : Byte; EPITA 42 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale begin writeln('-------------- Distribution des unite -------------'); for i:= 1 to NB_PLANETE do begin if (Planete[i].joueur = joueur) and (unite > 0) Then begin writeln('Il vous reste ', unite, ' unites a repartir.'); write(Planete[i].nom, ' :',Planete[i].unite,' unites + '); readln(num); if unite <= num then begin Planete[i].unite := Planete[i].unite + unite; unite := 0; end else begin Planete[i].unite := Planete[i].unite + num; unite := unite - num end; end; end; Writeln('Fin de la répartition'); end; 4.2.6 Procédure de combat Elle permet d'attaquer une planète adverse grâce à un eet de randomisation, ce qui remplacera les dés dans le jeu de plateau. procedure joueur_comba(var Planete: T_tplanete; plan_att, plan_def: Byte); var de, unite_att, inute_def : Byte; annul : Boolean; begin Randomize(); annul := True; repeat writeln('Joueur ', Planete[plan_att].joueur, ' : Combien d_unite voulait vous utilisé (1-', Planete[plan_def].unite - 1, ') ?'); readln(unite_att); until unite_att < Planete[plan_att].unite; unite_def := Planete[plan_def].unite; Planete[plan_att].unite := Planete[plan_att].unite - unite_att; EPITA 43 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale while annul AND (unite_att > 0) AND (unite_def > 0) do begin writeln('Attaque de la planete ', Planete[plan_def].nom); de := Random(3); if de = 0 Then begin unite_def := unite_def - 2; writln('Joueur ', Planete[plan_def].joueur, ' pert 2 unite'); end; else if de = 1 Then begin unite_att := unite_att - 2; writln('Joueur ', Planete[plan_att].joueur, ' pert 2 unite'); end; else begin unite_att := unite_att - 1; unite_def := unite_def - 1; writeln('Chacun joueur pert une unite'); end; if unite_att=0 then begin writeln('Joueur ', Planete[plan_att].joueur, ' pert 2 a gagne !'); Planete[plan_def] end; end; end; 4.2.7 Finalité du Programme Ce petit programme nous a donc permis une approche relative du problème, mais néanmoins signicative. Bien que cette application soit extrêmement limitée, elle nous a permis de poser de solides bases et d'avoir un très net aperçu de ce qui servira de ciment à toute l'implémentation découlant de ces fonctions de base. Ce programme peut s'approcher de la procédure ultérieure qui initialisera le début d'une partie de notre jeu. 4.3 Le moteur de jeu Premier chier appelé : le chier joueur.pass, où sont réunis toutes les fonctions et procédures relatives aux règles du jeu. C'est dans ce chier que sont répertoriées et déterminées toutes les actions. Par exemple, c'est grâce à ce chier que l'on peut déployer ces troupes, attaquer les planètes ennemis, etc... EPITA 44 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale Ce chier consiste lui aussi en une boucle qui va déterminer dans quelle phase de jeu le joueur ce trouve et le rediriger vers les actions possibles. Lorsque la phase est terminée, le programme reboucle et redirigeant le joueur vers la phase qui s'ensuit. Tant que la phase n'est pas terminée, le programme va réaccompagner le joueur jusqu'à ce qu'il juge avoir terminer la phase. Un élément à tenir compte : tous les joueurs sont incrémentés d'un chire, allant, cela va de soit, de 1 à 6. De plus, une variable est attribuée à chaque joueur. Cette variable est un booléen qui permet de déterminer si un joueur joue ou pas. Pour être plus claire, si des joueurs ne sont pas présents au début de la partie (exemple : la partie se déroule à trois joueurs, donc les joueurs 4,5 et 6 ne jouent pas), ce booléen est vraie. Les autres joueurs ont leur variable à faux. Si un joueur perd en cour de partie, sa variable passe également à vrai. Ainsi, à chaque fois que le joueur actif signale que ça phase de redéploiement est terminée, le programme va vérier si le joueur suivant, c'est à dire le joueur comportant le chire suivant, a sa variable à faux. Dans ce cas, le programme reboucle avec ce joueur qui démarre alors sa phase de déploiement. Dans le cas contraire, la boucle recommence avec le joueur suivant. Remarques : Si c'était le joueur possédant le chire le plus haut des joueurs encore en lice, la boucle reboucle avec le "premier" de la liste. De plus, si le joueur laissant sa place est redésigné car tout les autres ont leur booléen a vraie, le joueur a donc ni la partie. Voici quelques fonctions revêtant une importance extrême : procedure TJeu.Tourjeu; begin if Tour.Etape = DEPLOYEMENT Then jeu_deployement else if Tour.Etape = COMBAT Then jeu_combat else jeu_deplacement; if Tour.Fin then begin if Tour.Etape = DEPLOYEMENT Then fin_deployement else if Tour.Etape = COMBAT Then EPITA 45 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale fin_combat else begin fin_deplacement; repeat Tour.Joueur := Tour.Joueur + 1; if (Tour.Joueur > 6) then Tour.Joueur := 1; until not Jeu.Joueur.joueur[Tour.Joueur].perdu; Inc(Tour.Tour); reinitialise; end; Tour.Fin := False; inc_etape(Tour.Etape); finjeu; end; appeljoueur; end; ___________________________________________________________________ procedure TJeu.definir_de(var de_a, de_a1, de_a2, de_a3, de_d, de_d1, de_d2 : Byte); begin randomize(); de_a := 3; if Planete[Tour.Attaque.planete_attaque].unite - 1 < 3 Then de_a := Planete[Tour.Attaque.planete_attaque].unite - 1; if de_a <= 0 then //pas assez d'unité pour jouer begin Tour.Attaque.planete_attaque := 0; Tour.Attaque.planete_defence := 0; Tour.Attaque.resultat := PLUS_DUNITE; Tour.Attaque.continuer := False; end; de_d := 2; if Planete[Tour.Attaque.planete_defence].unite < 2 then de_d := 1; de_a1 := de; de_d1 := de; if de_a > 1 Then de_a2 := de else de_a2 := 0; if de_a > 2 Then EPITA 46 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale de_a3 := de else de_a3 := 0; de_d1 := de; if de_d > 1 Then de_d2 := de else de_d2 := 0; end; ___________________________________________________________________ procedure TJeu.faire_comba; var de_a, de_a1, de_a2, de_a3, de_d, de_d1, de_d2 : Byte; begin Tour.Attaque.tour := Tour.Attaque.tour + 1; Tour.Attaque.resultat := COMBAT_NON_FINI; Tour.Attaque.IsComba := True; definir_de(de_a, de_a1, de_a2, de_a3, de_d, de_d1, de_d2); if max(de_d1, de_d2) >= max3(de_a1, de_a2, de_a3) Then //le defenceur gagne une unité ! comba_repartir(tour.attaque.gain_defence, tour.attaque.gain_attaque, Tour.Attaque.planete_defence, Tour.Attaque.planete_attaque) else //sinon c'est l'attaquand ! comba_repartir(tour.attaque.gain_attaque, tour.attaque.gain_defence, Tour.Attaque.planete_attaque, Tour.Attaque.planete_defence); if (de_d > 1) AND (de_a > 1) Then //Si chaque joueur à + de 2 dé ! begin if min(de_d1, de_d2) >= moy3(de_a1, de_a2, de_a3) Then //le defenceur gagne une unité ! comba_repartir(tour.attaque.gain_defence, tour.attaque.gain_attaque, Tour.Attaque.planete_defence, Tour.Attaque.planete_attaque) else //sinon c'est l'attaquant ! comba_repartir(tour.attaque.gain_attaque, tour.attaque.gain_defence, Tour.Attaque.planete_attaque, Tour.Attaque.planete_defence); end; Tour.Attaque.continuer := False; end; EPITA 47 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale 5 L'intelligence articiel Jouer avec des amis à se casser la gure dans toute la galaxie pendant 2 ou 3 heures, c'est bien ! ! ! Encore faut-il en trouver un... Et c'est là que l'idée géniale nous éclaira ! Jouer avec son PC, s'en faire un ami ! ! ! Bon d'accord, je vais un peu trop loin... Donc, place à l'IA. 5.1 Le déploiement des troupes Tout d'abord, un bref récapitulatif : notre jeu se déroule en fonction de tours de jeu, qui lui même est scindé en diérentes parties : la répartition des troupes nouvellement créées et la phase d'attaque. Nous n'avons pas eu le temps matériel de nous occuper de la phase de redéploiement, même si nous pensons que l'impllémentation aurait été rapide. En réalité, tout se décide durant la phase de déploiement. La première phase consiste à lister l'ensemble des planètes qu'il possède. Ensuite, et c'est la partie la plus importante, il va calculer pour chacune d'elle un Heuristique, un coecient qui sera basé sur plusieurs critères, et qui permettra de savoir quelle planète est susceptible d'être la plus intéressante pour lancer une oensive. Puis les planètes sont trièes selon leur coecient heuristique. Ainsi, celle qui a le plus fort coecient est celle qui OBLIGATOIREMENT attaquera lors de ce tour... Chacune des planètes possédées par l'ordinateur se trouvant en contact de ces planètes ennemies et ne possèdant qu'une unité recevra automatiquement une deuxième unité lors de la phase de déploiement. Les autres unités alouées lors du début du tour seront réparties en fonction des planètes qui attaqueront lors de ce tour. Ex : la planète possédant le plus fort coecient possédera 5 troupes sur son territoire si la planète ennemie en possède 3, an attaquer dans des conditions agréables... Il est évident de l'IA posséde un nombre limité de recrue, ce qui nous donne une chance de gagner... EPITA 48 InfoSup B2 Unbound Value 5.2 Star Wars Risk Soutenance Finale La phase d'attaque La phase d'attaque est plus facile à gérer. Le choix de la ou des planètes allant attaquer ayant déja été réalisé, l'IA va directement passer à l'attaque. Il va tout de même s'arranger pour laisser des troupes an de défendre proprement la planète. A l'issue du premier tour de combat, il va vérier s'il est intéressant de réattaquer la planète. Il ne s'arrêtera que lorsque la planète sera prise ou lorsqu'il ne jugera plus utile de l'attaquer. Il est évident qu'il ne va pas s'acharner s'il a moins de troupes sur sa planète que sur la planète ennemie... Au niveau même de l'attaque, l'IA est obligée d'attaquer avec le plus grand nombre de troupes possible, à savoir 3 s'il peut attaquer avec 3 troupes ou plus, 2 s'il n'en a que 2, et 1 s'il décide que cela en vaut la peine... Si vous connaissez le jeu en version plateau, vous savez que l'issue du combat dépend du résultat d'un jet de la part des diérents partis. A votre grand étonnement, j'en suis sûr, cela se passera de la même manière dans notre jeu ! ! ! Les dès seront simulés par la sortie d'un nombre aléatoire. Si le défenseur à deux troupes ou plus, deux dès imaginaires sont jetés, dont chaque résultat est conservé. L'attaquant lui, oppose soit deux unités, auquel cas il "lancera" deux dés, soit trois unités ou plus, et un 3ème dé est ajouté. Le maximum des résultats du défenseur est comparé au maximum des résultats de l'attaquant, et le minimum du défenseur est opposé au médium ou au minimum selon que l'attaquant ait placé trois troupes ou deux. Lorsque le défenseur n'a qu'un dè à opposer, la deuxième phase ne se réalise pas. Pour chacune des comparaison, le défenseur perd une unité si son jet est STRICTEMENT inférieur au jet adverse. Dans le cas contraire, c'est l'attaquant qui perd une unité. EPITA 49 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale 6 La mise en place du son Que serait un jeu Star Wars sans sa musique incontournable ? ? ? Cette musique que tout le monde fredonne après avoir regardé un des lms mythiques. Nous nous devions d'insérer dans notre jeu les chefs d'oeuvre de John Williams. Cependant, cela nous ce t pas sans diculté... Pour ce faire, nous avons utilisé fmod, un logiciel qui permet de lire en screan tous les mp3 que nous décidons d'insérer dans sa bibliothèque. Il permet la lecture de beaucoup de format de musique et facilite grandement la gérance de la musique. De plus, de nombreux services annexes sont disponibles, telles que la récupération du spectres des diérentes musique. Cela nous a permis une très grande liberté dans le choix et dans l'installation de la musique. La musique nous paraissant décidément incontournable, nous avons décidé d'insérer un juke box dans la partie droite de notre boite de commande. Plusieurs fonctions sont prévues et implémentées, telles que l'initialisation de la musique, le passage à la musique précédente ou suivante, la mise en pause ou l'arrêt de la musique, et la visualisation du titre de la musique sur le moment écoutée. Cependant, nous avons été confronté à beaucoup de problèmes quand à la mise en place dans le jeu même. Tout d'abord, nous avons fait en sorte d'ouvrir un canal à chaque fois que la musique précédente devait s'arrêter, ce canal recevant la musique suivante. Petit problème : à chaque boucle, le code était fait en sorte que la musique précédente était soit disant terminé à chaque boucle, ce qui signiait qu'à chaque boucle du jeu, un canal était ouvert, SANS REFERMER LE CANAL PRECEDENT. Conclusion : beaucoup de résultats forts désagréables. De plus en plus de canaux s'ouvraient, produisant en quelques instants un grésillement. Mais ce n'était pas le plus dramatique. Car lorsqu'un canal s'ouvre, la musique est charger dans la mémoire virtuelle du jeu. Conclusion nale : la mémoire nissait par être surchargée, entrainant une erreur critique (reboot). Le problème a été résolu en lançant la musique suivante sur le même canal(simple, mais ecace). Un autre problème qui nous a dérangé pendant plusieurs heures était en réalité une condition toujours réalisée, entrainant ainsi un passage à la chanson suivante à chaque tour de boucle : dicile dans ces conditions d'écouter proprement une musique ! ! ! EPITA 50 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale Pour la soutenance nale, le spectre orne désermais le tableau de commande du jeu, et des bruitages tels que des paroles de R2D2 ont été rajoutés. EPITA 51 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale 7 Notre site web Notre site comporte deux parties principales : une est réservée au public, l'autre à l'administration du site. Voici sans plus attendre la brève récapitulation du travail fait. 7.1 Site Publique (http ://risk.star.wars.free.fr/)) Le site publique est maintenant en ligne, à l'adresse url décrite dans le sous-titre. Il est optimisé pour une résolution de 1024 par 728, et connaît actuellement quelques problèmes avec le navigateur Internet Explorer au niveau d'un mauvais dimensionnement des cellules vis à vis des images de fond (dans la barre de titre). Il fonctionne parfaitement sous le navigateur Firefox, et n'a pas été testé sous d'autres. Le site est séparé en plusieurs sections : - L'onglet Accueil, où les nouvelles sont éditées à partir de l'éditeur de News interne. - L'onglet Groupe, où le groupe de projet est présenté. - L'onglet Projet, où le projet est présenté. - l'onglet Tuto DirectX, qui est un tutorial en construction sur DirectX, et plus particulièrement DirectDraw. - L'onglet Téléchargement, où les diérentes versions de nos exécutables et de nos sources sont disponibles, ainsi que les rapports de soutenance et le cahier des charges, plus d'autres documents écrits. - L'onglet de Liens, qui apporte tout simplement des liens utiles vers d'autres projets précédents ou actuels, des sites complets sur DirectX ou Delphi, le site de l'EPITA (si si...), etc... 7.2 Administration Interne (http ://risk.star.wars.free.fr/admin/) Le site publique est entièrement géré par une structure php d'administration, qui permet de garder une seule page htlm qui concerne le squelette du site (Header, barre de séparation et autres objets graphiques), sans avoir à le modier pour chaque page. L'administration permet également de gérer tout le contenu du site, grâce à tout un système d'édition php de news et d'articles. Chaque article peut être gérer séparément par deux options distinctes : supprimer et modier. Un nouvel article peut être introduit avec une option supplémentaire : créer. De plus, chaque article correspond à l'un des onglets du site publique. Il est également possible à partir de cette interface d'administration depuis la première soutenance de correspondre par messages entre les membres inscrits sur cette même interface. EPITA 52 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale 8 L'installation et la désinstallation Nous avons utilisé le logiciel inno setup 5, nous permettant de créer facilement des programmes d'instalation et de désinstalation. De plus, il représente l'énorme avantage d'être gratuit... EPITA 53 InfoSup B2 Unbound Value Star Wars Risk Soutenance Finale 9 Conclusion Voilà, nous arrivons nalement à l'échéance nale. Nous avons eu la chance que chacun, avec ses possiblités, aide jusqu'au bout pour la réalisation du projet. Concrètement, qu'est ce que ce travail de groupe nous a apporté durant le long de l'année ? Et bien justement, réaliser ce qu'est un travail de groupe. Il nous a fallut apprendre à tenir des échéances, à accepter nos retards et à rebondir pour le rattraper. Lorsque nous nous heurtions à des dicultés, il nous a fallut prendre notre mal en patience puis, une fois le problème résolu, mettre les bouchées doubles pour rattraper ce que l'on avait prévu de faire. Nous avons appris également à nous mobiliser entièrement sur un projet. Pour preuve, alors que le projet peinait à avancer lors de la première et deuxième soutenances, la cadence et les résultats se sont grandement accrus lors des deux dernières soutenances, nous stimulant encore plus dans notre démarche. Notre façon de penser a peu à peu changer au cours des mois, demandant de plus en plus de résultats concrets. Enn, nous avons appris à discuter et à trouver des solutions intermédiaires à des divergences d'opinion entre les membres du groupe, chose que, pauvres français, avons bien du mal à mettre en pratique... Concrétement, comment a évoluer notre projet ? Nous avons pris beaucoup de retard en début d'année, aussi avons nous dû abandonner quelques idées, notamment la possibilité de jouer en réseau, la troisième étape de redéploiement, ou bien encore le fait de choisir le nombre d'unités conquérant une planète ennemie. En revanche, la perception de notre jeu ayant légérement changer vers l'esthétisme, nous avons rajouté des lms, des clips et des musiques. L'un dans l'autre, nous pensons avoir tout de même fourni un projet d'assez bonne qualité, même si malheureusement il ne répondait pas tout à fait au cahier des charges initial. EPITA 54 InfoSup B2