mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-28 08:48:48 +02:00
Add mod management into map editor
This commit is contained in:
parent
0c87d0a26c
commit
e4c147db16
@ -333,7 +333,23 @@ bool MainWindow::openMap(const QString & filenameSelect)
|
||||
CMapService mapService;
|
||||
try
|
||||
{
|
||||
controller.setMap(mapService.loadMap(resId));
|
||||
if(auto header = mapService.loadMapHeader(resId))
|
||||
{
|
||||
auto missingMods = CMapService::verifyMapHeaderMods(*header);
|
||||
CModHandler::Incompatibility::ModList modList;
|
||||
for(const auto & m : missingMods)
|
||||
modList.push_back({m.first, m.second.toString()});
|
||||
|
||||
if(!modList.empty())
|
||||
throw CModHandler::Incompatibility(std::move(modList));
|
||||
|
||||
controller.setMap(mapService.loadMap(resId));
|
||||
}
|
||||
}
|
||||
catch(const CModHandler::Incompatibility & e)
|
||||
{
|
||||
QMessageBox::warning(this, "Mods requiered", e.what());
|
||||
return false;
|
||||
}
|
||||
catch(const std::exception & e)
|
||||
{
|
||||
|
@ -554,3 +554,33 @@ void MapController::redo()
|
||||
sceneForceUpdate(); //TODO: use smart invalidation (setDirty)
|
||||
main->mapChanged();
|
||||
}
|
||||
|
||||
ModCompatibilityInfo MapController::modAssessmentAll()
|
||||
{
|
||||
ModCompatibilityInfo result;
|
||||
for(auto primaryID : VLC->objtypeh->knownObjects())
|
||||
{
|
||||
for(auto secondaryID : VLC->objtypeh->knownSubObjects(primaryID))
|
||||
{
|
||||
auto handler = VLC->objtypeh->getHandlerFor(primaryID, secondaryID);
|
||||
auto modName = QString::fromStdString(handler->getJsonKey()).split(":").at(0).toStdString();
|
||||
if(modName != "core")
|
||||
result[modName] = VLC->modh->getModInfo(modName).version;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ModCompatibilityInfo MapController::modAssessmentMap(const CMap & map)
|
||||
{
|
||||
ModCompatibilityInfo result;
|
||||
for(auto obj : map.objects)
|
||||
{
|
||||
auto handler = VLC->objtypeh->getHandlerFor(obj->ID, obj->subID);
|
||||
auto modName = QString::fromStdString(handler->getJsonKey()).split(":").at(0).toStdString();
|
||||
if(modName != "core")
|
||||
result[modName] = VLC->modh->getModInfo(modName).version;
|
||||
}
|
||||
//TODO: terrains?
|
||||
return result;
|
||||
}
|
||||
|
@ -54,6 +54,9 @@ public:
|
||||
bool discardObject(int level) const;
|
||||
void createObject(int level, CGObjectInstance * obj) const;
|
||||
bool canPlaceObject(int level, CGObjectInstance * obj, QString & error) const;
|
||||
|
||||
static ModCompatibilityInfo modAssessmentAll();
|
||||
static ModCompatibilityInfo modAssessmentMap(const CMap & map);
|
||||
|
||||
void undo();
|
||||
void redo();
|
||||
|
@ -17,8 +17,10 @@
|
||||
#include "../lib/CArtHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "../lib/CModHandler.h"
|
||||
#include "../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../lib/mapObjects/MiscObjects.h"
|
||||
#include "../lib/mapping/CMapService.h"
|
||||
#include "../lib/StringConstants.h"
|
||||
#include "inspector/townbulidingswidget.h" //to convert BuildingID to string
|
||||
|
||||
@ -82,6 +84,14 @@ std::vector<JsonNode> linearJsonArray(const JsonNode & json)
|
||||
return result;
|
||||
}
|
||||
|
||||
void traverseNode(QTreeWidgetItem * item, std::function<void(QTreeWidgetItem*)> action)
|
||||
{
|
||||
// Do something with item
|
||||
action(item);
|
||||
for (int i = 0; i < item->childCount(); ++i)
|
||||
traverseNode(item->child(i), action);
|
||||
}
|
||||
|
||||
MapSettings::MapSettings(MapController & ctrl, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::MapSettings),
|
||||
@ -98,7 +108,6 @@ MapSettings::MapSettings(MapController & ctrl, QWidget *parent) :
|
||||
|
||||
show();
|
||||
|
||||
|
||||
for(int i = 0; i < controller.map()->allowedAbilities.size(); ++i)
|
||||
{
|
||||
auto * item = new QListWidgetItem(QString::fromStdString(VLC->skillh->objects[i]->getNameTranslated()));
|
||||
@ -387,6 +396,61 @@ MapSettings::MapSettings(MapController & ctrl, QWidget *parent) :
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//mods management
|
||||
//collect all active mods
|
||||
QMap<QString, QTreeWidgetItem*> addedMods;
|
||||
QSet<QString> modsToProcess;
|
||||
ui->treeMods->blockSignals(true);
|
||||
|
||||
auto createModTreeWidgetItem = [&](QTreeWidgetItem * parent, const CModInfo & modInfo)
|
||||
{
|
||||
auto item = new QTreeWidgetItem(parent, {QString::fromStdString(modInfo.name), QString::fromStdString(modInfo.version.toString())});
|
||||
item->setData(0, Qt::UserRole, QVariant(QString::fromStdString(modInfo.identifier)));
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(0, controller.map()->mods.count(modInfo.identifier) ? Qt::Checked : Qt::Unchecked);
|
||||
//set parent check
|
||||
if(parent && item->checkState(0) == Qt::Checked)
|
||||
parent->setCheckState(0, Qt::Checked);
|
||||
return item;
|
||||
};
|
||||
|
||||
for(const auto & modName : VLC->modh->getActiveMods())
|
||||
{
|
||||
QString qmodName = QString::fromStdString(modName);
|
||||
if(qmodName.split(".").size() == 1)
|
||||
{
|
||||
const auto & modInfo = VLC->modh->getModInfo(modName);
|
||||
addedMods[qmodName] = createModTreeWidgetItem(nullptr, modInfo);
|
||||
ui->treeMods->addTopLevelItem(addedMods[qmodName]);
|
||||
}
|
||||
else
|
||||
{
|
||||
modsToProcess.insert(qmodName);
|
||||
}
|
||||
}
|
||||
|
||||
for(auto qmodIter = modsToProcess.begin(); qmodIter != modsToProcess.end();)
|
||||
{
|
||||
auto qmodName = *qmodIter;
|
||||
auto pieces = qmodName.split(".");
|
||||
assert(pieces.size() > 1);
|
||||
|
||||
QString qs;
|
||||
for(int i = 0; i < pieces.size() - 1; ++i)
|
||||
qs += pieces[i];
|
||||
|
||||
if(addedMods.count(qs))
|
||||
{
|
||||
const auto & modInfo = VLC->modh->getModInfo(qmodName.toStdString());
|
||||
addedMods[qmodName] = createModTreeWidgetItem(addedMods[qs], modInfo);
|
||||
modsToProcess.erase(qmodIter);
|
||||
qmodIter = modsToProcess.begin();
|
||||
}
|
||||
else
|
||||
++qmodIter;
|
||||
}
|
||||
ui->treeMods->blockSignals(false);
|
||||
}
|
||||
|
||||
MapSettings::~MapSettings()
|
||||
@ -430,6 +494,22 @@ std::string MapSettings::getMonsterName(int monsterObjectIdx)
|
||||
return name;
|
||||
}
|
||||
|
||||
void MapSettings::updateModWidgetBasedOnMods(const ModCompatibilityInfo & mods)
|
||||
{
|
||||
//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);
|
||||
};
|
||||
|
||||
for (int i = 0; i < ui->treeMods->topLevelItemCount(); ++i)
|
||||
{
|
||||
QTreeWidgetItem *item = ui->treeMods->topLevelItem(i);
|
||||
traverseNode(item, widgetAction);
|
||||
}
|
||||
}
|
||||
|
||||
void MapSettings::on_pushButton_clicked()
|
||||
{
|
||||
controller.map()->name = ui->mapNameEdit->text().toStdString();
|
||||
@ -705,6 +785,23 @@ void MapSettings::on_pushButton_clicked()
|
||||
controller.map()->triggeredEvents.push_back(specialDefeat);
|
||||
}
|
||||
|
||||
//Mod management
|
||||
auto widgetAction = [&](QTreeWidgetItem * item)
|
||||
{
|
||||
if(item->checkState(0) == Qt::Checked)
|
||||
{
|
||||
auto modName = item->data(0, Qt::UserRole).toString().toStdString();
|
||||
controller.map()->mods[modName] = VLC->modh->getModInfo(modName).version;
|
||||
}
|
||||
};
|
||||
|
||||
controller.map()->mods.clear();
|
||||
for (int i = 0; i < ui->treeMods->topLevelItemCount(); ++i)
|
||||
{
|
||||
QTreeWidgetItem *item = ui->treeMods->topLevelItem(i);
|
||||
traverseNode(item, widgetAction);
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
@ -887,3 +984,33 @@ void MapSettings::on_heroLevelLimitCheck_toggled(bool checked)
|
||||
ui->heroLevelLimit->setEnabled(checked);
|
||||
}
|
||||
|
||||
void MapSettings::on_modResolution_map_clicked()
|
||||
{
|
||||
updateModWidgetBasedOnMods(MapController::modAssessmentMap(*controller.map()));
|
||||
}
|
||||
|
||||
|
||||
void MapSettings::on_modResolution_full_clicked()
|
||||
{
|
||||
updateModWidgetBasedOnMods(MapController::modAssessmentAll());
|
||||
}
|
||||
|
||||
void MapSettings::on_treeMods_itemChanged(QTreeWidgetItem *item, int column)
|
||||
{
|
||||
//set state for children
|
||||
for (int i = 0; i < item->childCount(); ++i)
|
||||
item->child(i)->setCheckState(0, item->checkState(0));
|
||||
|
||||
//set state for parent
|
||||
ui->treeMods->blockSignals(true);
|
||||
if(item->checkState(0) == Qt::Checked)
|
||||
{
|
||||
while(item->parent())
|
||||
{
|
||||
item->parent()->setCheckState(0, Qt::Checked);
|
||||
item = item->parent();
|
||||
}
|
||||
}
|
||||
ui->treeMods->blockSignals(false);
|
||||
}
|
||||
|
||||
|
@ -34,12 +34,20 @@ private slots:
|
||||
|
||||
void on_heroLevelLimitCheck_toggled(bool checked);
|
||||
|
||||
void on_modResolution_map_clicked();
|
||||
|
||||
void on_modResolution_full_clicked();
|
||||
|
||||
void on_treeMods_itemChanged(QTreeWidgetItem *item, int column);
|
||||
|
||||
private:
|
||||
|
||||
std::string getTownName(int townObjectIdx);
|
||||
std::string getHeroName(int townObjectIdx);
|
||||
std::string getMonsterName(int townObjectIdx);
|
||||
|
||||
void updateModWidgetBasedOnMods(const ModCompatibilityInfo & mods);
|
||||
|
||||
template<class T>
|
||||
std::vector<int> getObjectIndexes() const
|
||||
{
|
||||
|
@ -9,7 +9,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>470</width>
|
||||
<width>543</width>
|
||||
<height>494</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -23,14 +23,7 @@
|
||||
<string>Map settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="pushButton">
|
||||
<property name="text">
|
||||
<string>Ok</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<item row="3" column="1">
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
@ -139,6 +132,77 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_9">
|
||||
<attribute name="title">
|
||||
<string>Mods</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Mandatory mods for playing this map</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="treeMods">
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustIgnored</enum>
|
||||
</property>
|
||||
<attribute name="headerDefaultSectionSize">
|
||||
<number>320</number>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Mod name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Version</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Automatic assignment</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="modResolution_map">
|
||||
<property name="toolTip">
|
||||
<string>Set required mods based on objects placed on the map. This method may cause problems if you have customized rewards, garrisons, etc from mods</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Map objects mods</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="modResolution_full">
|
||||
<property name="toolTip">
|
||||
<string>Set all mods having a game content as mandatory</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Full content mods</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_6">
|
||||
<attribute name="title">
|
||||
<string>Events</string>
|
||||
@ -369,6 +433,13 @@
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QPushButton" name="pushButton">
|
||||
<property name="text">
|
||||
<string>Ok</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "validator.h"
|
||||
#include "mapcontroller.h"
|
||||
#include "ui_validator.h"
|
||||
#include "../lib/mapObjects/MapObjects.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
@ -158,6 +159,15 @@ std::list<Validator::Issue> Validator::validate(const CMap * map)
|
||||
issues.emplace_back("Map name is not specified", false);
|
||||
if(map->description.empty())
|
||||
issues.emplace_back("Map description is not specified", false);
|
||||
|
||||
//verificationfor mods
|
||||
for(auto & mod : MapController::modAssessmentMap(*map))
|
||||
{
|
||||
if(!map->mods.count(mod.first))
|
||||
{
|
||||
issues.emplace_back(QString("Map contains object from mod \"%1\", but doesn't require it").arg(QString::fromStdString(VLC->modh->getModInfo(mod.first).name)), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(const std::exception & e)
|
||||
{
|
||||
|
@ -264,7 +264,7 @@ void WindowNewMap::on_okButton_clicked()
|
||||
nmap = f.get();
|
||||
}
|
||||
|
||||
|
||||
nmap->mods = MapController::modAssessmentAll();
|
||||
static_cast<MainWindow*>(parent())->controller.setMap(std::move(nmap));
|
||||
static_cast<MainWindow*>(parent())->initializeMap(true);
|
||||
close();
|
||||
|
Loading…
Reference in New Issue
Block a user