Quellcode durchsuchen

prompt user for adding required mod

- when object is placed to map and its mod is not in the map's required mod list -> show question messagebox
- fix code in modAssessmentAll function
MichalZr6 vor 7 Monaten
Ursprung
Commit
87326ce9d9

+ 26 - 3
mapeditor/mainwindow.cpp

@@ -45,6 +45,7 @@
 #include "inspector/inspector.h"
 #include "mapsettings/mapsettings.h"
 #include "mapsettings/translations.h"
+#include "mapsettings/modsettings.h"
 #include "playersettings.h"
 #include "validator.h"
 #include "helper.h"
@@ -371,6 +372,10 @@ void MainWindow::initializeMap(bool isNew)
 	initialScale = ui->mapView->mapToScene(ui->mapView->viewport()->geometry()).boundingRect();
 	
 	//enable settings
+	mapSettings = new MapSettings(controller, this);
+	connect(&controller, &MapController::requestModsUpdate,
+		mapSettings->getModSettings(), &ModSettings::updateModWidgetBasedOnMods);
+
 	ui->actionMapSettings->setEnabled(true);
 	ui->actionPlayers_settings->setEnabled(true);
 	ui->actionTranslations->setEnabled(true);
@@ -604,7 +609,14 @@ void MainWindow::on_actionCampaignEditor_triggered()
 void MainWindow::on_actionNew_triggered()
 {
 	if(getAnswerAboutUnsavedChanges())
+	{
 		new WindowNewMap(this);
+		//mapSettings = new MapSettings(controller, this);
+		//connect(&controller, &MapController::requestModsUpdate,
+		//	mapSettings->getModSettings(), &ModSettings::updateModWidgetBasedOnMods);
+
+		//controller.requestModsUpdate({}, true);
+	}
 }
 
 void MainWindow::on_actionSave_triggered()
@@ -1124,9 +1136,20 @@ void MainWindow::on_inspectorWidget_itemChanged(QTableWidgetItem *item)
 
 void MainWindow::on_actionMapSettings_triggered()
 {
-	auto settingsDialog = new MapSettings(controller, this);
-	settingsDialog->setWindowModality(Qt::WindowModal);
-	settingsDialog->setModal(true);
+	if(!mapSettings)
+		return;
+
+	if(mapSettings->isVisible())
+	{
+		mapSettings->raise();
+		mapSettings->activateWindow();
+	}
+	else
+	{
+		mapSettings->setWindowModality(Qt::WindowModal);
+		mapSettings->setModal(true);
+		mapSettings->show();
+	}
 }
 
 

+ 2 - 0
mapeditor/mainwindow.h

@@ -8,6 +8,7 @@
 
 class ObjectBrowser;
 class ObjectBrowserProxyModel;
+class MapSettings;
 
 VCMI_LIB_NAMESPACE_BEGIN
 class CConsoleHandler;
@@ -189,6 +190,7 @@ private:
     Ui::MainWindow * ui;
 	ObjectBrowserProxyModel * objectBrowser = nullptr;
 	QGraphicsScene * scenePreview;
+	MapSettings * mapSettings = nullptr;
 	
 	QString filename;
 	QString lastSavingDir;

+ 85 - 28
mapeditor/mapcontroller.cpp

@@ -29,6 +29,7 @@
 #include "../lib/spells/CSpellHandler.h"
 #include "../lib/CRandomGenerator.h"
 #include "../lib/serializer/CMemorySerializer.h"
+#include "mapsettings/modsettings.h"
 #include "mapview.h"
 #include "scenelayer.h"
 #include "maphandler.h"
@@ -36,6 +37,11 @@
 #include "inspector/inspector.h"
 #include "GameLibrary.h"
 
+MapController::MapController(QObject * parent)
+	: QObject(parent)
+{
+}
+
 MapController::MapController(MainWindow * m): main(m)
 {
 	for(int i : {0, 1})
@@ -562,6 +568,36 @@ bool MapController::canPlaceObject(int level, CGObjectInstance * newObj, QString
 		error = QObject::tr("Hero %1 cannot be created as NEUTRAL.").arg(QString::fromStdString(newObj->instanceName));
 		return false;
 	}
+
+	// check if object's mod is in required mods in map settings
+	ModCompatibilityInfo modsInfo;
+	modAssessmentObject(newObj, modsInfo);
+
+	for(auto & mod : modsInfo)
+	{
+		if(!_map->mods.count(mod.first))
+		{
+			QString submod;
+			if(!mod.second.parent.empty())
+				submod = " (" + tr("submod of") + " " + QString::fromStdString(mod.second.parent) + ")";
+
+			auto reply = QMessageBox::question(main,
+				tr("Required Mod Missing"),
+				tr("This object is from the mod '%1'%2.\n"
+					"The mod is currently not in the map's required modifications list.\n"
+					"Do you want to add this mod to the required modifications ?")
+				.arg(QString::fromStdString(LIBRARY->modh->getModInfo(mod.first).getVerificationInfo().name), submod),
+				QMessageBox::Yes | QMessageBox::No);
+
+			if(reply == QMessageBox::Yes)
+				requestModsUpdate(modsInfo, true);		// emit signal for MapSettings
+			else
+			{
+				error = tr("The object's mod is mandatory for map to remain valid.");
+				return false;
+			}
+		}
+	}
 	
 	return true;
 }
@@ -590,49 +626,39 @@ ModCompatibilityInfo MapController::modAssessmentAll()
 		for(auto secondaryID : LIBRARY->objtypeh->knownSubObjects(primaryID))
 		{
 			auto handler = LIBRARY->objtypeh->getHandlerFor(primaryID, secondaryID);
-			auto modName = QString::fromStdString(handler->getJsonKey()).split(":").at(0).toStdString();
-			if(modName != "core")
-				result[modName] = LIBRARY->modh->getModInfo(modName).getVerificationInfo();
+			auto modScope = handler->getModScope();
+			if(modScope != "core")
+				result[modScope] = LIBRARY->modh->getModInfo(modScope).getVerificationInfo();
 		}
 	}
 	return result;
 }
 
-ModCompatibilityInfo MapController::modAssessmentMap(const CMap & map)
+void MapController::modAssessmentObject(CGObjectInstance * obj, ModCompatibilityInfo & result)
 {
-	ModCompatibilityInfo result;
-
-	auto extractEntityMod = [&result](const auto & entity) 
+	auto extractEntityMod = [&result](const auto & entity)
 	{
 		auto modScope = entity->getModScope();
 		if(modScope != "core")
 			result[modScope] = LIBRARY->modh->getModInfo(modScope).getVerificationInfo();
 	};
 
-	for(auto obj : map.objects)
-	{
-		auto handler = obj->getObjectHandler();
-		auto modScope = handler->getModScope();
-		if(modScope != "core")
-			result[modScope] = LIBRARY->modh->getModInfo(modScope).getVerificationInfo();
+	auto handler = obj->getObjectHandler();
+	auto modScope = handler->getModScope();
+	if(modScope != "core")
+		result[modScope] = LIBRARY->modh->getModInfo(modScope).getVerificationInfo();
 
-		if(obj->ID == Obj::TOWN || obj->ID == Obj::RANDOM_TOWN)
+	if(obj->ID == Obj::TOWN || obj->ID == Obj::RANDOM_TOWN)
+	{
+		auto town = dynamic_cast<CGTownInstance *>(obj);
+		for(const auto & spellID : town->possibleSpells)
 		{
-			auto town = dynamic_cast<CGTownInstance *>(obj.get());
-			for(const auto & spellID : town->possibleSpells)
-			{
-				if(spellID == SpellID::PRESET)
-					continue;
-				extractEntityMod(spellID.toEntity(LIBRARY));
-			}
-
-			for(const auto & spellID : town->obligatorySpells)
-			{
-				extractEntityMod(spellID.toEntity(LIBRARY));
-			}
+			if(spellID == SpellID::PRESET)
+				continue;
+			extractEntityMod(spellID.toEntity(LIBRARY));
 		}
 
-		if(obj->ID == Obj::HERO || obj->ID == Obj::RANDOM_HERO)
+		for(const auto & spellID : town->obligatorySpells)
 		{
 			auto hero = dynamic_cast<CGHeroInstance *>(obj.get());
 			for(const auto & spellID : hero->getSpellsInSpellbook())
@@ -654,6 +680,37 @@ ModCompatibilityInfo MapController::modAssessmentMap(const CMap & map)
 		}
 	}
 
-	//TODO: terrains?
+	if(obj->ID == Obj::HERO || obj->ID == Obj::RANDOM_HERO)
+	{
+		auto hero = dynamic_cast<CGHeroInstance *>(obj);
+		for(const auto & spellID : hero->getSpellsInSpellbook())
+		{
+			if(spellID == SpellID::PRESET || spellID == SpellID::SPELLBOOK_PRESET)
+				continue;
+			extractEntityMod(spellID.toEntity(LIBRARY));
+		}
+
+		for(const auto & [_, slotInfo] : hero->artifactsWorn)
+		{
+			extractEntityMod(slotInfo.artifact->getTypeId().toEntity(LIBRARY));
+		}
+
+		for(const auto & art : hero->artifactsInBackpack)
+		{
+			extractEntityMod(art.artifact->getTypeId().toEntity(LIBRARY));
+		}
+	}
+
+//TODO: terrains?
+}
+
+ModCompatibilityInfo MapController::modAssessmentMap(const CMap & map)
+{
+	ModCompatibilityInfo result;
+
+	for(auto obj : map.objects)
+	{
+		modAssessmentObject(obj.get(), result);
+	}
 	return result;
 }

+ 8 - 1
mapeditor/mapcontroller.h

@@ -20,9 +20,12 @@ class EditorObstaclePlacer;
 VCMI_LIB_NAMESPACE_END
 
 class MainWindow;
-class MapController
+class MapController : public QObject
 {
+	Q_OBJECT
+
 public:
+	explicit MapController(QObject * parent = nullptr);
 	MapController(MainWindow *);
 	MapController(const MapController &) = delete;
 	MapController(const MapController &&) = delete;
@@ -62,6 +65,7 @@ public:
 	void createObject(int level, std::shared_ptr<CGObjectInstance> obj) const;
 	bool canPlaceObject(int level, CGObjectInstance * obj, QString & error) const;
 	
+	static void modAssessmentObject(CGObjectInstance * obj, ModCompatibilityInfo & result);
 	static ModCompatibilityInfo modAssessmentAll();
 	static ModCompatibilityInfo modAssessmentMap(const CMap & map);
 
@@ -70,6 +74,9 @@ public:
 	
 	PlayerColor defaultPlayer;
 	QDialog * settingsDialog = nullptr;
+
+signals:
+	void requestModsUpdate(const ModCompatibilityInfo & mods, bool leaveCheckedUnchanged) const;
 	
 private:
 	std::unique_ptr<CMap> _map;

+ 5 - 1
mapeditor/mapsettings/mapsettings.cpp

@@ -30,7 +30,6 @@ MapSettings::MapSettings(MapController & ctrl, QWidget *parent) :
 	
 	assert(controller.map());
 	controller.settingsDialog = this;
-	show();
 
 	for(auto const & objectPtr : LIBRARY->skillh->objects)
 	{
@@ -79,6 +78,11 @@ MapSettings::~MapSettings()
 	delete ui;
 }
 
+ModSettings * MapSettings::getModSettings()
+{
+	return ui->mods;
+}
+
 void MapSettings::on_pushButton_clicked()
 {	
 	auto updateMapArray = [](const QListWidget * widget, auto & arr)

+ 4 - 0
mapeditor/mapsettings/mapsettings.h

@@ -18,6 +18,8 @@ namespace Ui {
 class MapSettings;
 }
 
+class ModSettings;
+
 class MapSettings : public QDialog
 {
 	Q_OBJECT
@@ -26,6 +28,8 @@ public:
 	explicit MapSettings(MapController & controller, QWidget *parent = nullptr);
 	~MapSettings();
 
+	ModSettings * getModSettings();
+
 private slots:
 	void on_pushButton_clicked();
 

+ 14 - 2
mapeditor/mapsettings/modsettings.cpp

@@ -44,6 +44,8 @@ void ModSettings::initialize(MapController & c)
 	QMap<QString, QTreeWidgetItem*> addedMods;
 	QSet<QString> modsToProcess;
 	ui->treeMods->blockSignals(true);
+	ui->treeMods->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
+	ui->treeMods->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
 
 	auto createModTreeWidgetItem = [&](QTreeWidgetItem * parent, const ModDescription & modInfo)
 	{
@@ -59,6 +61,8 @@ void ModSettings::initialize(MapController & c)
 
 	for(const auto & modName : LIBRARY->modh->getActiveMods())
 	{
+		if(modName == "core")
+			continue;
 		QString qmodName = QString::fromStdString(modName);
 		if(qmodName.split(".").size() == 1)
 		{
@@ -115,13 +119,21 @@ void ModSettings::update()
 	}
 }
 
-void ModSettings::updateModWidgetBasedOnMods(const ModCompatibilityInfo & mods)
+void ModSettings::updateModWidgetBasedOnMods(const ModCompatibilityInfo & mods, bool leaveCheckedUnchanged)
 {
 	//Mod management
 	auto widgetAction = [&](QTreeWidgetItem * item)
 	{
 		auto modName = item->data(0, Qt::UserRole).toString().toStdString();
-		item->setCheckState(0, mods.count(modName) ? Qt::Checked : Qt::Unchecked);
+		if(leaveCheckedUnchanged)
+		{
+			if(mods.count(modName))
+				item->setCheckState(0, Qt::Checked);
+		}
+		else
+		{
+			item->setCheckState(0, mods.count(modName) ? Qt::Checked : Qt::Unchecked);
+		}
 	};
 
 	for (int i = 0; i < ui->treeMods->topLevelItemCount(); ++i)

+ 2 - 3
mapeditor/mapsettings/modsettings.h

@@ -26,6 +26,8 @@ public:
 	void initialize(MapController & map) override;
 	void update() override;
 
+	void updateModWidgetBasedOnMods(const ModCompatibilityInfo & mods, bool leaveCheckedUnchanged = false);
+
 private slots:
 	void on_modResolution_map_clicked();
 
@@ -33,9 +35,6 @@ private slots:
 
 	void on_treeMods_itemChanged(QTreeWidgetItem *item, int column);
 
-private:
-	void updateModWidgetBasedOnMods(const ModCompatibilityInfo & mods);
-
 private:
 	Ui::ModSettings *ui;
 };

+ 1 - 1
mapeditor/windownewmap.cpp

@@ -311,7 +311,7 @@ void WindowNewMap::on_okButton_clicked()
 		nmap = f.get();
 	}
 	
-	nmap->mods = MapController::modAssessmentAll();
+	nmap->mods = MapController::modAssessmentMap(*nmap);
 	static_cast<MainWindow*>(parent())->controller.setMap(std::move(nmap));
 	static_cast<MainWindow*>(parent())->initializeMap(true);
 	close();