瀏覽代碼

map editor: Add possibility to customize hero artifacts

godric3 10 月之前
父節點
當前提交
896a148a7a

+ 6 - 0
mapeditor/CMakeLists.txt

@@ -34,6 +34,8 @@ set(editor_SRCS
 		inspector/messagewidget.cpp
 		inspector/rewardswidget.cpp
 		inspector/questwidget.cpp
+		inspector/heroartifactswidget.cpp
+		inspector/artifactwidget.cpp
 		inspector/heroskillswidget.cpp
 		inspector/herospellwidget.cpp
 		inspector/PickObjectDelegate.cpp
@@ -76,6 +78,8 @@ set(editor_HEADERS
 		inspector/messagewidget.h
 		inspector/rewardswidget.h
 		inspector/questwidget.h
+		inspector/heroartifactswidget.h
+		inspector/artifactwidget.h
 		inspector/heroskillswidget.h
 		inspector/herospellwidget.h
 		inspector/PickObjectDelegate.h
@@ -108,6 +112,8 @@ set(editor_FORMS
 		inspector/messagewidget.ui
 		inspector/rewardswidget.ui
 		inspector/questwidget.ui
+		inspector/heroartifactswidget.ui
+		inspector/artifactwidget.ui
 		inspector/heroskillswidget.ui
 		inspector/herospellwidget.ui
 		inspector/portraitwidget.ui

+ 63 - 0
mapeditor/inspector/artifactwidget.cpp

@@ -0,0 +1,63 @@
+/*
+ * herosspellwidget.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+#include "artifactWidget.h"
+#include "ui_artifactWidget.h"
+#include "inspector.h"
+#include "../../lib/ArtifactUtils.h"
+#include "../../lib/constants/StringConstants.h"
+
+ArtifactWidget::ArtifactWidget(CArtifactFittingSet & fittingSet, QWidget * parent) :
+	QDialog(parent),
+	ui(new Ui::ArtifactWidget),
+	fittingSet(fittingSet)
+{
+	ui->setupUi(this);
+
+	connect(ui->saveButton, &QPushButton::clicked, this, [this]() 
+	{
+		emit saveArtifact(ui->artifact->currentData().toInt(), ArtifactPosition(ui->possiblePositions->currentData().toInt()));
+		close();
+	});
+	connect(ui->cancelButton, &QPushButton::clicked, this, &ArtifactWidget::close);
+	connect(ui->possiblePositions, static_cast<void(QComboBox::*) (int)> (&QComboBox::currentIndexChanged), this, &ArtifactWidget::fillArtifacts);
+	
+	std::vector<ArtifactPosition> possiblePositions;
+	for(const auto & slot : ArtifactUtils::allWornSlots())
+	{
+		if(fittingSet.isPositionFree(slot))
+		{
+			ui->possiblePositions->addItem(QString::fromStdString(NArtifactPosition::namesHero[slot.num]), slot.num);
+		}
+	}
+	ui->possiblePositions->addItem(QString::fromStdString(NArtifactPosition::backpack), ArtifactPosition::BACKPACK_START);
+	fillArtifacts();
+
+
+}
+
+void ArtifactWidget::fillArtifacts()
+{
+	ui->artifact->clear();
+	auto currentSlot = ui->possiblePositions->currentData().toInt();
+	for (const auto& art : VLC->arth->getDefaultAllowed())
+	{
+		auto artifact = art.toArtifact();
+		// forbid spell scroll for now as require special handling
+		if (artifact->canBePutAt(&fittingSet, currentSlot, true) && artifact->getId() != ArtifactID::SPELL_SCROLL) {
+			ui->artifact->addItem(QString::fromStdString(artifact->getNameTranslated()), QVariant::fromValue(artifact->getIndex()));
+		}
+	}
+}
+
+ArtifactWidget::~ArtifactWidget()
+{
+	delete ui;
+}

+ 35 - 0
mapeditor/inspector/artifactwidget.h

@@ -0,0 +1,35 @@
+/*
+ * ArtifactWidget.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include <QDialog>
+#include "../../lib/mapObjects/CGHeroInstance.h"
+
+namespace Ui {
+class ArtifactWidget;
+}
+
+class ArtifactWidget : public QDialog
+{
+	Q_OBJECT
+
+public:
+	explicit ArtifactWidget(CArtifactFittingSet & fittingSet, QWidget * parent = nullptr);
+	~ArtifactWidget();
+	
+signals:
+	void saveArtifact(int32_t artifactIndex, ArtifactPosition slot);
+ private slots:
+	void fillArtifacts();
+
+private:
+	Ui::ArtifactWidget * ui;
+	CArtifactFittingSet & fittingSet;
+};

+ 92 - 0
mapeditor/inspector/artifactwidget.ui

@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ArtifactWidget</class>
+ <widget class="QDialog" name="ArtifactWidget">
+  <property name="windowModality">
+   <enum>Qt::WindowModal</enum>
+  </property>
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>150</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>400</width>
+    <height>150</height>
+   </size>
+  </property>
+  <property name="maximumSize">
+   <size>
+    <width>400</width>
+    <height>150</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Artifact</string>
+  </property>
+  <widget class="QWidget" name="gridLayoutWidget">
+   <property name="geometry">
+    <rect>
+     <x>10</x>
+     <y>10</y>
+     <width>381</width>
+     <height>80</height>
+    </rect>
+   </property>
+   <layout class="QGridLayout" name="gridLayout" columnstretch="1,5">
+    <item row="1" column="0">
+     <widget class="QLabel" name="label_2">
+      <property name="text">
+       <string>Artifact</string>
+      </property>
+     </widget>
+    </item>
+    <item row="1" column="1">
+     <widget class="QComboBox" name="artifact"/>
+    </item>
+    <item row="0" column="1">
+     <widget class="QComboBox" name="possiblePositions"/>
+    </item>
+    <item row="0" column="0">
+     <widget class="QLabel" name="label">
+      <property name="text">
+       <string>Equip where:</string>
+      </property>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QPushButton" name="saveButton">
+   <property name="geometry">
+    <rect>
+     <x>190</x>
+     <y>100</y>
+     <width>93</width>
+     <height>28</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Save</string>
+   </property>
+  </widget>
+  <widget class="QPushButton" name="cancelButton">
+   <property name="geometry">
+    <rect>
+     <x>290</x>
+     <y>100</y>
+     <width>93</width>
+     <height>28</height>
+    </rect>
+   </property>
+   <property name="text">
+    <string>Cancel</string>
+   </property>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 144 - 0
mapeditor/inspector/heroartifactswidget.cpp

@@ -0,0 +1,144 @@
+/*
+ * herosspellwidget.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+#include "artifactwidget.h"
+#include "heroartifactswidget.h"
+#include "ui_heroartifactswidget.h"
+#include "inspector.h"
+#include "mapeditorroles.h"
+#include "../../lib/ArtifactUtils.h"
+#include "../../lib/constants/StringConstants.h"
+
+HeroArtifactsWidget::HeroArtifactsWidget(CGHeroInstance & h, QWidget * parent) :
+	QDialog(parent),
+	ui(new Ui::HeroArtifactsWidget),
+	hero(h),
+	fittingSet(CArtifactFittingSet(h))
+{
+	ui->setupUi(this);
+}
+
+HeroArtifactsWidget::~HeroArtifactsWidget()
+{
+	delete ui;
+}
+
+void HeroArtifactsWidget::on_addButton_clicked()
+{
+	ArtifactWidget artifactWidget{ fittingSet, this };
+	connect(&artifactWidget, &ArtifactWidget::saveArtifact, this, &HeroArtifactsWidget::onSaveArtifact);
+	artifactWidget.exec();
+}
+
+void HeroArtifactsWidget::on_removeButton_clicked()
+{
+	auto row = ui->artifacts->currentRow();
+	if (row == -1)
+	{
+		return;
+	}
+
+	auto slot = ui->artifacts->item(row, Column::SLOT)->data(MapEditorRoles::ArtifactSlotRole).toInt();
+	fittingSet.removeArtifact(ArtifactPosition(slot));
+	ui->artifacts->removeRow(row);
+}
+
+void HeroArtifactsWidget::onSaveArtifact(int32_t artifactIndex, ArtifactPosition slot) 
+{
+	auto artifact = ArtifactUtils::createArtifact(VLC->arth->getByIndex(artifactIndex)->getId());
+	fittingSet.putArtifact(slot, artifact);
+	addArtifactToTable(artifactIndex, slot);
+}
+
+void HeroArtifactsWidget::addArtifactToTable(int32_t artifactIndex, ArtifactPosition slot)
+{
+	auto artifact = VLC->arth->getByIndex(artifactIndex);
+	auto * itemArtifact = new QTableWidgetItem;
+	itemArtifact->setText(QString::fromStdString(artifact->getNameTranslated()));
+	itemArtifact->setData(MapEditorRoles::ArtifactIDRole, QVariant::fromValue(artifact->getIndex()));
+
+	auto * itemSlot = new QTableWidgetItem;
+	auto slotText = ArtifactUtils::isSlotBackpack(slot) ? NArtifactPosition::backpack : NArtifactPosition::namesHero[slot.num];
+	itemSlot->setData(MapEditorRoles::ArtifactSlotRole, QVariant::fromValue(slot.num));
+	itemSlot->setText(QString::fromStdString(slotText));
+
+	ui->artifacts->insertRow(ui->artifacts->rowCount());
+	ui->artifacts->setItem(ui->artifacts->rowCount() - 1, Column::ARTIFACT, itemArtifact);
+	ui->artifacts->setItem(ui->artifacts->rowCount() - 1, Column::SLOT, itemSlot);
+}
+
+void HeroArtifactsWidget::obtainData()
+{
+	std::vector<const CArtifact *> combinedArtifactsParts;
+	for (const auto & [artPosition, artSlotInfo] : fittingSet.artifactsWorn)
+	{
+		addArtifactToTable(VLC->arth->getById(artSlotInfo.artifact->getTypeId())->getIndex(), artPosition);
+	}
+	for (const auto & art : hero.artifactsInBackpack)
+	{
+		addArtifactToTable(VLC->arth->getById(art.artifact->getTypeId())->getIndex(), ArtifactPosition::BACKPACK_START);
+	}
+}
+
+void HeroArtifactsWidget::commitChanges()
+{
+	while(!hero.artifactsWorn.empty())
+	{
+		hero.removeArtifact(hero.artifactsWorn.begin()->first);
+	}
+
+	while(!hero.artifactsInBackpack.empty())
+	{
+		hero.removeArtifact(ArtifactPosition::BACKPACK_START + static_cast<int>(hero.artifactsInBackpack.size()) - 1);
+	}
+
+	for(const auto & [artPosition, artSlotInfo] : fittingSet.artifactsWorn)
+	{
+		hero.putArtifact(artPosition, artSlotInfo.artifact);
+	}
+
+	for(const auto & art : fittingSet.artifactsInBackpack)
+	{
+		hero.putArtifact(ArtifactPosition::BACKPACK_START + static_cast<int>(hero.artifactsInBackpack.size()), art.artifact);
+	}
+}
+
+HeroArtifactsDelegate::HeroArtifactsDelegate(CGHeroInstance & h) : QStyledItemDelegate(), hero(h)
+{
+}
+
+QWidget * HeroArtifactsDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const
+{
+	return new HeroArtifactsWidget(hero, parent);
+}
+
+void HeroArtifactsDelegate::setEditorData(QWidget * editor, const QModelIndex & index) const
+{
+	if (auto * ed = qobject_cast<HeroArtifactsWidget *>(editor))
+	{
+		ed->obtainData();
+	}
+	else
+	{
+		QStyledItemDelegate::setEditorData(editor, index);
+	}
+}
+
+void HeroArtifactsDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const
+{
+	if (auto * ed = qobject_cast<HeroArtifactsWidget *>(editor))
+	{
+		ed->commitChanges();
+	}
+	else
+	{
+		QStyledItemDelegate::setModelData(editor, model, index);
+	}
+}

+ 65 - 0
mapeditor/inspector/heroartifactswidget.h

@@ -0,0 +1,65 @@
+/*
+ * heroartifactswidget.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include <QDialog>
+#include "../../lib/mapObjects/CGHeroInstance.h"
+
+namespace Ui {
+class HeroArtifactsWidget;
+}
+
+class HeroArtifactsWidget : public QDialog
+{
+	Q_OBJECT
+
+public:
+	explicit HeroArtifactsWidget(CGHeroInstance &, QWidget *parent = nullptr);
+	~HeroArtifactsWidget();
+	
+	void obtainData();
+	void commitChanges();
+
+private slots:
+	void onSaveArtifact(int32_t artifactIndex, ArtifactPosition slot);
+
+ 	void on_addButton_clicked();
+
+	void on_removeButton_clicked();
+
+private:
+	enum Column
+	{
+		SLOT, ARTIFACT
+	};
+	Ui::HeroArtifactsWidget * ui;
+	
+	CGHeroInstance & hero;
+	CArtifactFittingSet fittingSet;
+
+	void addArtifactToTable(int32_t artifactIndex, ArtifactPosition slot);
+	
+};
+
+class HeroArtifactsDelegate : public QStyledItemDelegate
+{
+	Q_OBJECT
+public:
+	using QStyledItemDelegate::QStyledItemDelegate;
+	
+	HeroArtifactsDelegate(CGHeroInstance &);
+	
+	QWidget * createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const override;
+	void setEditorData(QWidget * editor, const QModelIndex & index) const override;
+	void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const override;
+	
+private:
+	CGHeroInstance & hero;
+};

+ 144 - 0
mapeditor/inspector/heroartifactswidget.ui

@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>HeroArtifactsWidget</class>
+ <widget class="QDialog" name="HeroArtifactsWidget">
+  <property name="windowModality">
+   <enum>Qt::NonModal</enum>
+  </property>
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>480</width>
+    <height>635</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>480</width>
+    <height>480</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Artifacts</string>
+  </property>
+  <property name="modal">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <property name="spacing">
+    <number>10</number>
+   </property>
+   <property name="topMargin">
+    <number>5</number>
+   </property>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <property name="topMargin">
+      <number>0</number>
+     </property>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="addButton">
+       <property name="enabled">
+        <bool>true</bool>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>90</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>Add</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="removeButton">
+       <property name="enabled">
+        <bool>true</bool>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>90</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>Remove</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QTableWidget" name="artifacts">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="horizontalScrollBarPolicy">
+      <enum>Qt::ScrollBarAlwaysOff</enum>
+     </property>
+     <property name="sizeAdjustPolicy">
+      <enum>QAbstractScrollArea::AdjustToContents</enum>
+     </property>
+     <property name="editTriggers">
+      <set>QAbstractItemView::NoEditTriggers</set>
+     </property>
+     <property name="selectionMode">
+      <enum>QAbstractItemView::SingleSelection</enum>
+     </property>
+     <property name="selectionBehavior">
+      <enum>QAbstractItemView::SelectRows</enum>
+     </property>
+     <attribute name="horizontalHeaderVisible">
+      <bool>true</bool>
+     </attribute>
+     <attribute name="horizontalHeaderDefaultSectionSize">
+      <number>120</number>
+     </attribute>
+     <attribute name="horizontalHeaderStretchLastSection">
+      <bool>true</bool>
+     </attribute>
+     <attribute name="verticalHeaderVisible">
+      <bool>false</bool>
+     </attribute>
+     <attribute name="verticalHeaderDefaultSectionSize">
+      <number>26</number>
+     </attribute>
+     <column>
+      <property name="text">
+       <string>Slot</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string>Artifact</string>
+      </property>
+     </column>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 2 - 0
mapeditor/inspector/inspector.cpp

@@ -28,6 +28,7 @@
 #include "messagewidget.h"
 #include "rewardswidget.h"
 #include "questwidget.h"
+#include "heroartifactswidget.h"
 #include "heroskillswidget.h"
 #include "herospellwidget.h"
 #include "portraitwidget.h"
@@ -333,6 +334,7 @@ void Inspector::updateProperties(CGHeroInstance * o)
 	auto * delegate = new HeroSkillsDelegate(*o);
 	addProperty("Skills", PropertyEditorPlaceholder(), delegate, false);
 	addProperty("Spells", PropertyEditorPlaceholder(), new HeroSpellDelegate(*o), false);
+	addProperty("Artifacts", PropertyEditorPlaceholder(), new HeroArtifactsDelegate(*o), false);
 	
 	if(o->getHeroTypeID().hasValue() || o->ID == Obj::PRISON)
 	{ //Hero type

+ 3 - 1
mapeditor/mapeditorroles.h

@@ -16,5 +16,7 @@ enum MapEditorRoles
 	TownEventRole = Qt::UserRole + 1,
 	PlayerIDRole,
 	BuildingIDRole,
-	SpellIDRole
+	SpellIDRole,
+	ArtifactIDRole,
+	ArtifactSlotRole
 };