Part 39: Monsters part 1: Groups in the editor

Downloads

Source code
Executable for the map editor - temporary version (Windows 32bits)
Executable for the game - exactly the same as in part 38 (Windows 32bits)

Before you try to compile the code go to the "Projects" tab on the left menu, select the "Run" settings for your kit,
and set the "Working directory" to the path of "editor\data" for the editor or "game\data" for the game.

Monster graphics

Here are the graphics for the monsters:


Each monster can have an image for each direction - front, side, back - and an attack image.
The only exception is the Materializer who has a 3 frames animation.
But some monsters will not have all these frames. I.e. the screamer has 2 images because it is symetrical.
We will see the use of these images later. At the moment they will allow us to create a monsters' database.

		<?xml version="1.0" encoding="utf-8"?>
		<monsters>
			<!-- 00 None -->
			<monster name = MONSTER00">
			</monster>

			<!-- 01 Giant Scorpion -->
			<monster name = MONSTER01">
				<img_front>01_Giant_Scorpion_front.png</img_front>
				<img_side>01_Giant_Scorpion_side.png</img_side>
				<img_back>01_Giant_Scorpion_back.png</img_back>
				<img_attack>01_Giant_Scorpion_attack.png</img_attack>
			</monster>

			<!-- 02 Swamp Slime -->
			<monster name = MONSTER02">
				<img_front>02_Swamp_Slime_front.png</img_front>
				<img_attack>02_Swamp_Slime_attack.png</img_attack>
			</monster>
			
			[...]
		</monsters>
				
Monsters appears in the game as groups of 1 to 4 on a tile.


When there is only one monster it could be on one of these 4 positions, but after a while it will get to the center
of the tile:


Later in the game we will see bigger monsters that are 2 or 4 times the size of these ones. The maximum number of
monsters on the tile will then be reduced accordingly.
But we will talk about that later, for now we will begin to include these groups in the editor.

The monsters tab

The database is read in the editor and we use it to display a new tab.


This tab is added in "MainForm.ui.qml".

        Tab {
            id:monstersTab
            title: qsTr("Monsters")
            source:"/MonstersTab.qml"
            clip: true
        }
				
"MonsterTab.qml" is nearly the same as the files used for the other tabs.

		import QtQuick 2.5
		import QtQuick.Controls 1.4
		import QtQuick.Layouts 1.2

		ScrollView
		{
			ListView
			{
				model:monstersList
				currentIndex: bridge.monsterType
				highlightMoveDuration: 0

				delegate: Item {
					width: parent.width - 12
					height: 25

					Text {
						x: 6
						width: parent.width - 12
						anchors.verticalCenter: parent.verticalCenter
						text: modelData
					}

					MouseArea {
						anchors.fill: parent
						onClicked: bridge.monsterType = index
					}
				}

				highlight: Rectangle { color: "#70c0ff" }
			}
		}
				
"MainForm.ui.qml" also contains the selection info part that appears below the tab.

        ScrollView {
            id: monsterSelScroll
            visible: bridge.monsterSelScrollVis
            anchors.fill: parent

            GridLayout {
                id: mSelGrid
                objectName: "oSelGrid"
                rowSpacing: 4
                columnSpacing: 4
                rows: 3
                columns: 2
                anchors.fill: parent

                Label {
                    id: mPosLabel
                    height: 20
                    text: qsTr("Position:")
                    verticalAlignment: Text.AlignVCenter
                    renderType: Text.QtRendering
                }

                Label {
                    id: mPosValue
                    height: 20
                    text: bridge.selPosition
                    verticalAlignment: Text.AlignVCenter
                }

                Label {
                    id: mTypeLabel
                    height: 20
                    text: qsTr("Type:")
                    verticalAlignment: Text.AlignVCenter
                    horizontalAlignment: Text.AlignLeft
                }

                ComboBox {
                    id: mTypeValue
                    model: monstersList
                    currentIndex: bridge.selMonsterType
                }
            }
		}
				

Storing the monsters

The monsters groups will be stored as a separate list like the objects.

		class CMonsterGroup
		{
		public:
			CMonsterGroup();
			CMonsterGroup(const CMonsterGroup& rhs);
			virtual ~CMonsterGroup();

			void    setType(uint8_t type);
			uint8_t getType();
			CMonsterGroup&  operator=(const CMonsterGroup& rhs);
			void    load(FILE* handle);
			void    save(FILE* handle);

			CParam*     findParamByName(QString name);

			CVec2       mPos;
			std::vector<CParam*>    mParams;

		private:
			void    deleteParams();

			uint8_t mType;
		};

		//---------------------------------------------------------------------------------------------
		class CMap
		{
			[...]
			CMonsterGroup*  addMonsterGroup(CVec2 pos);
			CMonsterGroup*  findMonsterGroup(CVec2 pos);
			void            removeMonsterGroup(CVec2 pos);

			[...]
			std::vector<CMonsterGroup>  mMonsterGroups;
			[...]
				
For now the groups only hold a type. We'll add more parameters later.

The tools

There is a new drawMonster tool in "tools.h"

		class drawMonsterTool : public QUndoCommand
		{
		public:
			explicit drawMonsterTool(QUndoCommand *parent = 0);

			bool init(CVec2 pos, int type);
			void undo() override;
			void redo() override;

		private:
			CVec2       mPos;
			int         mType;
			CMonsterGroup   mOldMonster;
		};
				
This tool is a kind of mix between the drawTile and the addObject ones.

		drawMonsterTool::drawMonsterTool(QUndoCommand *parent)
			: QUndoCommand(parent)
		{
		}

		bool drawMonsterTool::init(CVec2 pos, int type)
		{
			mPos = pos / TILE_SIZE;

			if (mPos.x < 0 || mPos.x >= map.mSize.x ||
				mPos.y < 0 || mPos.y >= map.mSize.y)
				return false;

			CMonsterGroup*  group = map.findMonsterGroup(mPos);
			if (group != NULL)
				mOldMonster = *group;
			else
				mOldMonster.setType(0);

			if (mOldMonster.getType() == type)
				return false;

			mType = type;
			return true;
		}

		void drawMonsterTool::undo()
		{
			if (mOldMonster.getType() == 0)
			{
				map.removeMonsterGroup(mPos);
			}
			else
			{
				CMonsterGroup*  group = map.addMonsterGroup(mPos);
				*group = mOldMonster;
			}
			bridge.updateQMLImage();
		}

		void drawMonsterTool::redo()
		{
			CMonsterGroup*  group = map.addMonsterGroup(mPos);
			group->setType(mType);
			bridge.updateQMLImage();
		}
				
The copy, cut and paste tools needed some modifications too.
As well as the selection rectangles drawing.

As I said earlier, the monsters groups in this version of the editor only holds a type.
We will need to add some parameters to them, like the number of monsters in the group.
So this version of the editor is only temporary, and I did not convert the maps to the new format.
We will continue that in the next part.