Partie 40: Les monstres partie 2: Paramètres et affichage
Téléchargements
Paramètres
 
				
		<!-- 01 Scorpion Géant -->
		<monster name = "MONSTER01">
			[...]
			<param type="int">nbMonstres</param>
			<param type="enum" values="Haut;Bas;Gauche;Droite">Dir</param>
			<param type="stack"/>
		</monster>
		<!-- 02 Démon Baveux -->
		<monster name = "MONSTER02">
			[...]
			<param type="int">nbMonstres</param>
			<param type="enum" values="Haut;Bas;Gauche;Droite">Dir</param>
			<param type="stack"/>
		</monster>
				
		enum EWallSide
		{
			eWallSideUp = 0,
			eWallSideDown,
			eWallSideLeft,
			eWallSideRight,
			eWallSideMax
		};
		#define eWallSideMonster (EWallSide)(eWallSideMax + 1)
				
Charger et initialiser les monstres
		void CMonsters::readMonstersDB()
		{
			QDomDocument doc;
			QFile f("databases/monsters.xml");
			f.open(QIODevice::ReadOnly);
			doc.setContent(&f);
			f.close();
			QDomElement root = doc.documentElement();
			QDomNode    monsterNode = root.firstChild();
			while(!monsterNode.isNull())
			{
				if (monsterNode.isElement())
				{
					QDomElement monster = monsterNode.toElement();
					std::vector<CParamType> paramsTypesList;
					if (monster.tagName() == "monster")
					{
						QDomElement monsterInfo = monster.firstChild().toElement();
						SMonsterData    monsterData;
						while(!monsterInfo.isNull())
						{
							if (monsterInfo.tagName() == "img_front")
							{
								monsterData.frontImage = QString("gfx/3DView/monsters/") + monsterInfo.text();
							}
							else if (monsterInfo.tagName() == "img_side")
							{
								monsterData.sideImage = QString("gfx/3DView/monsters/") + monsterInfo.text();
							}
							else if (monsterInfo.tagName() == "img_back")
							{
								monsterData.backImage = QString("gfx/3DView/monsters/") + monsterInfo.text();
							}
							else if (monsterInfo.tagName() == "img_attack")
							{
								monsterData.attackImage = QString("gfx/3DView/monsters/") + monsterInfo.text();
							}
							else if (monsterInfo.tagName() == "param")
							{
								QString paramType = monsterInfo.attribute("type");
								CParamType  newParamType;
								if (paramType == "int")
								{
									newParamType.mType = eParamInt;
									newParamType.mName = monsterInfo.text();
									paramsTypesList.push_back(newParamType);
								}
								else if (paramType == "enum")
								{
									newParamType.mType = eParamEnum;
									newParamType.mName = monsterInfo.text();
									newParamType.mValues = monsterInfo.attribute("values").split(";");
									paramsTypesList.push_back(newParamType);
								}
								else if (paramType == "stack")
								{
									newParamType.mType = eParamStack;
									paramsTypesList.push_back(newParamType);
								}
							}
							monsterInfo = monsterInfo.nextSibling().toElement();
						}
						CMap::mMonstersParams.push_back(paramsTypesList);
						monstersDatas.push_back(monsterData);
					}
				}
				monsterNode = monsterNode.nextSibling();
			}
		}
				
		class CMonsters
		{
		public:
			[...]
			struct SMonsterData
			{
				QString frontImage;
				QString sideImage;
				QString backImage;
				QString attackImage;
			};
			[...]
		private:
			[...]
			std::vector<SMonsterData>   monstersDatas;
		};
				
		class CMonsters
		{
		public:
			[...]
			struct SMonsterGroup
			{
				SMonster    monsters[4];
				EMonsterDir dir;
			};
			[...]
		private:
			[...]
			std::vector<SMonsterGroup>  monsterGroups;
		};
				
		class CMonsters
		{
		public:
			enum EMonsterPos
			{
				eMonsterPosNone,    // monstre vide
				eMonsterPosNorthWest,
				eMonsterPosNorthEast,
				eMonsterPosSouthWest,
				eMonsterPosSouthEast,
				eMonsterPosCenter
			};
			enum EMonsterDir
			{
				eMonsterDirUp,
				eMonsterDirDown,
				eMonsterDirLeft,
				eMonsterDirRight
			};
			struct SMonsterData
			{
				QString frontImage;
				QString sideImage;
				QString backImage;
				QString attackImage;
			};
			struct SMonster
			{
				EMonsterPos pos;
				EMonsterDir dir;
				int life;
			};
				
		void CMonsters::init()
		{
			for (size_t i = 0; i < map.mMonsterGroups.size(); ++i)
			{
				CMonsterGroup&  mapGroup = map.mMonsterGroups[i];
				SMonsterGroup   group;
				int nbMonsters = mapGroup.getIntParam("nbMonstres");
				group.dir = (EMonsterDir)mapGroup.getEnumParam("Dir");
				for (int j = 0; j < nbMonsters; j++)
					group.monsters[j].dir = group.dir;
				if (nbMonsters == 1)
				{
					group.monsters[0].pos = eMonsterPosCenter;
					group.monsters[0].life = 3;
					for (int j = 1; j < 4; j++)
					{
						group.monsters[j].pos = eMonsterPosNone;
						group.monsters[j].life = 0;
					}
				}
				else
				{
					// positions des monstres pour une direction donnée
					static const EMonsterPos startingPos[4][4] =
					{
						{eMonsterPosNorthWest, eMonsterPosNorthEast, eMonsterPosSouthWest, eMonsterPosSouthEast},   // up
						{eMonsterPosSouthEast, eMonsterPosSouthWest, eMonsterPosNorthEast, eMonsterPosNorthWest},   // down
						{eMonsterPosNorthWest, eMonsterPosSouthWest, eMonsterPosNorthEast, eMonsterPosSouthEast},   // left
						{eMonsterPosSouthEast, eMonsterPosNorthEast, eMonsterPosSouthWest, eMonsterPosNorthWest}    // right
					};
					for (int j = 0; j < 4; j++)
					{
						if (j < nbMonsters)
						{
							group.monsters[j].pos = startingPos[group.dir][j];
							group.monsters[j].life = 3;
						}
						else
						{
							group.monsters[j].pos = eMonsterPosNone;
							group.monsters[j].life = 0;
						}
					}
				}
				monsterGroups.push_back(group);
			}
		}
				
Dessiner les monstres
 
				 
				
		void CMonsters::draw(QImage* image, CVec2 mapPos, CVec2 tablePos)
		{
			if (tablePos.y == WALL_TABLE_HEIGHT - 1)
				return;
			int groupIndex = map.findMonsterGroupIndex(mapPos);
			if (groupIndex != -1)
			{
				CMonsterGroup&  mapGroup = map.mMonsterGroups[groupIndex];
				SMonsterGroup&  group = monsterGroups[groupIndex];
				// position des monstres en fonction de la position du joueur
				static const float monsterPosDir[4][5][2] =
				{
					//   NW          NE          SW          SE           C
					{{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0},  {0.5, 0.5}},   // haut
					{{1.0, 0.0}, {1.0, 1.0}, {0.0, 0.0}, {0.0, 1.0},  {0.5, 0.5}},   // gauche
					{{1.0, 1.0}, {0.0, 1.0}, {1.0, 0.0}, {0.0, 0.0},  {0.5, 0.5}},   // bas
					{{0.0, 1.0}, {0.0, 0.0}, {1.0, 1.0}, {1.0, 0.0},  {0.5, 0.5}}    // droite
				};
				// direction des monstres en fonction de la direction du joueur
				static const EMonsterDir monsterDirDir[4][4] =
				{
					{eMonsterDirUp,    eMonsterDirDown,  eMonsterDirLeft,  eMonsterDirRight}, // haut
					{eMonsterDirRight, eMonsterDirLeft,  eMonsterDirUp,    eMonsterDirDown},  // gauche
					{eMonsterDirDown,  eMonsterDirUp,    eMonsterDirRight, eMonsterDirLeft},  // bas
					{eMonsterDirLeft,  eMonsterDirRight, eMonsterDirDown,  eMonsterDirUp}     // droite
				};
				// on dessine la ligne arrière des monstres avant ceux de devant
				for (int row = 0; row < 3; ++row)
				{
				
					for (int i = 0; i < 4; ++i)
					{
						SMonster&   monster = group.monsters[i];
						if (monster.pos != eMonsterPosNone)
						{
							// direction relative
							EMonsterDir tableDir = monsterDirDir[player.dir][monster.dir];
							// sprite
							int type = mapGroup.getType();
							QString spriteName;
							bool    flip = false;
							if (tableDir == eMonsterDirDown)
							{
								spriteName = monstersDatas[type].frontImage;
							}
							else if (tableDir == eMonsterDirUp)
							{
								spriteName = monstersDatas[type].backImage;
								if (spriteName.isEmpty() == true)
									spriteName = monstersDatas[type].frontImage;
							}
							else
							{
								spriteName = monstersDatas[type].sideImage;
								if (spriteName.isEmpty() == true)
									spriteName = monstersDatas[type].frontImage;
								else if (tableDir == eMonsterDirLeft)
									flip = true;
							}
				
							if (monsterPosDir[player.dir][monster.pos - eMonsterPosNorthWest][1] == (float)row / 2.0)
							{
								// position dans la table
								float tablePosX = tablePos.x * 2 + monsterPosDir[player.dir][monster.pos - eMonsterPosNorthWest][0];
								float tablePosY = tablePos.y * 2 + monsterPosDir[player.dir][monster.pos - eMonsterPosNorthWest][1];
								// position à l'écran
								QImage  sprite = fileCache.getImage(spriteName.toStdString());
								CVec2   pos = getMonsterPos(tablePosX, tablePosY);
				
								// calcule la taille par rapport à la position de référence (la plus proche)
								CVec2   pos0, pos1;
								float   scale;
								pos0 = getMonsterPos(WALL_TABLE_WIDTH, (WALL_TABLE_HEIGHT - 1) * 2 + 1);
								pos1 = getMonsterPos(WALL_TABLE_WIDTH, tablePosY);
								scale = 2.37 * (float)(pos1.x - 112) / (float)(pos0.x - 112);
								// redimensionne le monstre dans une image temporaire
								QImage  scaledMonster(sprite.size() * scale, QImage::Format_ARGB32);
								scaledMonster.fill(TRANSPARENT);
								graph2D.drawImageScaled(&scaledMonster, CVec2(), sprite, scale);
								// assombrit le monstre en fonction de la distance
								float shadow = ((WALL_TABLE_HEIGHT - 1) * 2 - tablePosY - 1) * 0.13f;
								graph2D.darken(&scaledMonster, shadow);
				
								// dessine le monstre (position relative à ses pieds)
								pos.x -= scaledMonster.width() / 2;
								pos.y -= scaledMonster.height() - 9 * scale;
								graph2D.drawImage(image, pos, scaledMonster, 0, flip, QRect(0, 33, MAINVIEW_WIDTH, MAINVIEW_HEIGHT));
							}
						}
					}
				}
			}
		}