Partie 15: Choisissez vos champions

Téléchargements

Code source
Exécutable de l'éditeur de niveaux (Windows 32bits)
Exécutable du jeu (Windows 32bits)

Avant d'essayer de compiler le code, allez dans l'onglet "Projets" dans le menu de gauche, séléctionnez l'onglet "Run" pour votre kit,
et mettez dans "Working directory" le chemin de "editor\data" pour l'éditeur ou "game\data" pour le jeu.

Les portraits dans l'éditeur

Maintenant on va ajouter les portraits qui nous permettront de choisir nos champions.
Du point de vue de l'éditeur, ça sera un nouveau type de mur, avec de nouveaux paramètres, qui devrait ressembler à ça:


Il y a un entier qui nous donne le numéro du portrait du champion. Par convention j'ai choisi d'utiliser l'ordre dans lequel
ils apparaissent dans la planche de sprite des portraits que l'on a vu dans la dernière partie.
Le 2ième paramètre est un booléen qui sera utilisé en interne dans le jeu pour dire si on a cliqué sur ce portrait et ajouté
ce champion à notre équipe.
Comme il n'est utilisé que dans le jeu, j'aurais pu le cacher dans l'éditeur (par ex. en utilisant une convention de nommage
comme ajouter un "_" au début de son nom). Mais ça peut être utile de changer la valeur initiale d'une variable interne
pour faire des tests.

Donc dans "walls.xml", ça ressemble à ça:

		<wall name="Portrait">
			<image>Portrait.png</image>
			<param type="int">Champion</param>
			<param type="bool">estVide</param>
		</wall>
				
Comme pour les décors, on utilise 2 modèles QML, "ParamTextField.qml" et "ParamCheckBox.qml" pour l'apparence des paramètres
dans la boîte de sélection.

Les nouveaux paramètres sont définis dans "map.h" comme ça:

		enum EParamType
		{
			eParamOrnate = 0,
			eParamInt,
			eParamBool
		};

		[...]
		class CParamInt : public CParam
		{
		public:
			CParamInt();
			int32_t mValue;
		};

		class CParamBool : public CParam
		{
		public:
			CParamBool();
			bool mValue;
		};
				
Ensuite c'est plus ou moins un travail de copier/coller/modifier de toutes les fonctions où "eParamOrnate" ou "CParamOrnate"
apparaissent, en prêtant une attention particulière aux fonction de chargement et de sauvegarde.

Enfin j'ai ajouté une nouvelle image pour différencier ces murs des murs classiques. Alors notre premier niveau ressemble à ça
dans l'éditeur:


Le décor dans le jeu



Le décor du miroir est affiché comme les autres décors que nous avons vu dans les parties précédentes.
La tête du champion est seulement dessinée quand le mur est juste devant nous, et une zone souris est ajoutée à ce moment là.
Bien sur on teste aussi le booléen "estVide" pour voir si on a déjà cliqué sur ce portrait pour l'ajouter à notre équipe.
Regardons comment c'est fait dans la fonction drawWall() de game.cpp:

        void CGame::drawWall(QImage* image, CVec2 mapPos, CVec2 tablePos, EWallSide side, bool flip)
        {
                [...]

                // dessine les décors
                CWall*  wall = &tile->mWalls[playerSide];

                if (wall->getType() == eWallSimple)
                {
                    //------------------------------------------------------------------------------
                    // mur simple
                    int ornate = wall->getOrnateParam("Ornate");
                    walls.drawOrnate(image, tablePos, side, ornate);
                }
                else if (wall->getType() == eWallPortrait)
                {
                    //------------------------------------------------------------------------------
                    // miroir de champion
                    walls.drawOrnate(image, tablePos, side, ORNATE_MIRROR);

                    if (tablePos == CVec2(2, 3) && side == eWallSideUp)
                    {
                        // si le miroir est just en facede nous, on dessine la tête du champion
                        int champion = wall->getIntParam("Champion");
                        bool isEmpty = wall->getBoolParam("estVide");

                        if (isEmpty == false)
                        {
                            CRect   rect = interface.getChampionPortraitRect(champion);
                            QImage  portraits = fileCache.getImage("gfx/interface/ChampPortraits.png");
                            CVec2   pos(96, 68);
                            graph2D.drawImageAtlas(image, pos, portraits, rect);

                            int lastChampChosed;
                            for (lastChampChosed = 0; lastChampChosed < 4; ++lastChampChosed)
                                if (characters[lastChampChosed].portrait == -1)
                                    break;

                            if (lastChampChosed != 4)
                            {
                                CRect   mouseRect(pos, CVec2(pos.x + CHAMP_PORTRAIT_WIDTH - 1, pos.y + CHAMP_PORTRAIT_HEIGHT - 1));
                                mouse.addArea(eMouseAreaG_Champion, mouseRect, (void*)wall);
                            }
                        }
                    }
                }
                [...]
        }
				
Vous pouvez voir que j'ai ajouté des fonctions pour récupérer la valeur d'un paramètre à partir de son nom, comme on
l'a lu dans walls.xml.
C'est principalement pour la lisibilité.
Dans le jeu d'origine, le format des paramètres des murs était hard-codé, car c'est plus efficace (pas de comparaison
de chaîne) et plus sur car il y a moins d'information pour faire de la rétro-ingénierie sur le format de la map.

Résurrection

J'ai apporté beaucoup de changements à l'interface dans cette partie.
D'abord, quand vous cliquez sur un miroir, l'inventaire s'ouvre et une fenêtre apparait pour vous permettre de ressusciter
ou réincarner le champion:


Remarque: J'ai seulement fait la résurrection dans cette version du jeu. Parce qu'on ne gère pas encore toutes les stats du
champion qui sont modifiées par la réincarnation. Et il faudra que je regarde les sources du jeu d'origine pour voir comment
c'était fait exactement.

Dans le jeu d'origine la plupart des interactions étaient désactivées dans cet écran.
Les seules choses que vous pouviez faire c'était cliquer sur les boutons ressusciter, réincarner ou annuler. Et peut-être regarder
un objet ou voir les stats du champion en cliquant sur l'oeil.

Pour voir tous les changements que ça implique dans le code, cherchez la variable "isResurrecting".

La base de données des champions

Maintenant que l'on peut ressusciter un champion, ça serait intéressant de lui mettre les vraies valeurs de ses statistiques
(au moins de celles qui sont visibles, car il y a aussi beaucoup de stats et de talents cachés dans le jeu d'origine).
Donc j'ai commencé une base de données de champions appelée "champions.xml":

		<?xml version="1.0" encoding="ISO-8859-1"?>
		<champions>
			<champion firstName="CHAMP_FNAME00" lastName="CHAMP_LNAME00">
				<portrait>0</portrait>
				<health>60</health>
				<stamina>58</stamina>
				<mana>22</mana>
				<strength>42</strength>
				<dexterity>40</dexterity>
				<wisdom>42</wisdom>
				<vitality>36</vitality>
				<anti_magic>53</anti_magic>
				<anti_fire>40</anti_fire>
				<load>44</load>
			</champion>

			[...]
		</champions>
				
Comme avant, j'ai suivi l'ordre des portraits des champions tels qu'ils appparaissent dans la planche de sprites.

Cette base de données, comme les autres, est simplement chargée au début du jeu, et ses données sont copiées dans la structure
correspondante de "characters" quand un champion est ressuscité.

Vous pouvez voir que les noms des champions n'apparaissent pas ici. A la place, il y a un identifiant. On va voir pourquoi
tout de suite.

La base de données des textes

Tous les textes du jeu se trouveront dans le fichier "texts.xml":

		<?xml version="1.0" encoding="ISO-8859-1"?>
		<texts>
			<text id="CHAMP_FNAME00">ELIJA</text>
			<text id="CHAMP_LNAME00">LE LION DE YAITOPYA</text>
			<text id="CHAMP_FNAME01">HALK</text>
			<text id="CHAMP_LNAME01">LE BARBARE</text>
			[...]

			<text id="STATS00">SANTE</text>
			<text id="STATS01">VIGUEUR</text>
			[...]
		</texts>
				
De cette façon ça sera plus facile de traduire le jeu dans une autre langue.
Les seules exceptions sont quelques textes qui sont dessinées dans les sprites ("Game Over", "Provisions", "Eau"...).

Dans les jeux qui sont traduits en plusieurs langues, les textes sont généralement stockés dans un tableau Excel, puis exportés
dans le format spécifique au jeu.
Comme Dungeon Master a seulement été traduit en 3 langues dans les dernières versions, les textes n'étaient pas aussi bien gérés,
et ils étaient même partagés entre plusieurs fichiers dans différents formats.

Cette base de donnéest est gérée par 2 fonctions dans "interface.cpp":

Afficher les stats

Comme vous avez pu le remarquer, j'affiche déja le nom complet d'un champion et ses stats de santé, vigueur, mana et charge dans
l'inventaire.
Mais j'ai aussi fait la boîte d'informations qui apparait quand on clique sur l'oeil:


Charchez "isPressingEye" dans le code pour plus d'infos.