diff --git a/mapeditor/StdInc.h b/mapeditor/StdInc.h index dcaee354c..8443e7820 100644 --- a/mapeditor/StdInc.h +++ b/mapeditor/StdInc.h @@ -2,6 +2,9 @@ #include "../Global.h" +#define VCMI_EDITOR_VERSION "0.1" +#define VCMI_EDITOR_NAME "VCMI Map Editor" + #include #include #include diff --git a/mapeditor/inspector.cpp b/mapeditor/inspector.cpp index e93be2e24..df7adf7f1 100644 --- a/mapeditor/inspector.cpp +++ b/mapeditor/inspector.cpp @@ -1,45 +1,32 @@ #include "StdInc.h" #include "inspector.h" -#include "../lib/mapObjects/CObjectHandler.h" -#include "../lib/mapObjects/CObjectClassesHandler.h" -#include "../lib/mapObjects/CGTownInstance.h" -#include "../lib/mapObjects/MiscObjects.h" #include "../lib/CArtHandler.h" #include "../lib/spells/CSpellHandler.h" #include "../lib/CRandomGenerator.h" +#include "../lib/mapObjects/CObjectClassesHandler.h" -void Inspector::setProperty(const QString & key, const QVariant & value) +//===============IMPLEMENT OBJECT INITIALIZATION FUNCTIONS================ +Initializer::Initializer(CGObjectInstance * o) { - if(!obj) - return; - - setProperty(dynamic_cast(obj), key, value); - //updateProperties(); +///IMPORTANT! initialize order should be from base objects to derived objects + INIT_OBJ_TYPE(CGResource); + INIT_OBJ_TYPE(CGArtifact); + INIT_OBJ_TYPE(CArmedInstance); + INIT_OBJ_TYPE(CGMine); + INIT_OBJ_TYPE(CGTownInstance); } -void Inspector::setProperty(CGTownInstance * object, const QString & key, const QVariant & value) +void initialize(CArmedInstance * o) { - if(!object) - return; - - if(key == "Owner") - { - PlayerColor owner(value.toString().toInt()); - if(value == "NEUTRAL") - owner = PlayerColor::NEUTRAL; - if(value == "UNFLAGGABLE") - owner = PlayerColor::UNFLAGGABLE; - object->tempOwner = owner; - return; - } -} - -CGTownInstance * initialize(CGTownInstance * o) -{ - if(!o) - return nullptr; - + if(!o) return; + o->tempOwner = PlayerColor::NEUTRAL; +} + +void initialize(CGTownInstance * o) +{ + if(!o) return; + o->builtBuildings.insert(BuildingID::FORT); o->builtBuildings.insert(BuildingID::DEFAULT); @@ -48,13 +35,11 @@ CGTownInstance * initialize(CGTownInstance * o) if(!spell->isSpecial() && !spell->isCreatureAbility()) o->possibleSpells.push_back(spell->id); } - return o; } -CGArtifact * initialize(CGArtifact * o) +void initialize(CGArtifact * o) { - if(!o) - return nullptr; + if(!o) return; if(o->ID == Obj::SPELL_SCROLL) { @@ -69,66 +54,161 @@ CGArtifact * initialize(CGArtifact * o) auto a = CArtifactInstance::createScroll(*RandomGeneratorUtil::nextItem(out, CRandomGenerator::getDefault())); o->storedArtifact = a; } +} + +void initialize(CGMine * o) +{ + if(!o) return; - return o; + o->producedResource = Res::ERes(o->subID); + o->producedQuantity = o->defaultResProduction(); } -Initializer::Initializer(CGObjectInstance * o) +void initialize(CGResource * o) { - initialize(dynamic_cast(o)); - initialize(dynamic_cast(o)); + if(!o) return; + + o->amount = CGResource::RANDOM_AMOUNT; } -Inspector::Inspector(CGObjectInstance * o, QTableWidget * t): obj(o), table(t) +//===============IMPLEMENT PROPERTIES SETUP=============================== +void Inspector::updateProperties(CArmedInstance * o) { - /* - /// Position of bottom-right corner of object on map - int3 pos; - /// Type of object, e.g. town, hero, creature. - Obj ID; - /// Subtype of object, depends on type - si32 subID; - /// Current owner of an object (when below PLAYER_LIMIT) - PlayerColor tempOwner; - /// Index of object in map's list of objects - ObjectInstanceID id; - /// Defines appearance of object on map (animation, blocked tiles, blit order, etc) - ObjectTemplate appearance; - /// If true hero can visit this object only from neighbouring tiles and can't stand on this object - bool blockVisit; + if(!o) return; + + addProperty("Owner", o->tempOwner); +} - std::string instanceName; - std::string typeName; - std::string subTypeName;*/ +void Inspector::updateProperties(CGTownInstance * o) +{ + if(!o) return; + + addProperty("Owner", o->tempOwner, false); + addProperty("Town name", o->name, false); +} + +void Inspector::updateProperties(CGArtifact * o) +{ + if(!o) return; +} + +void Inspector::updateProperties(CGMine * o) +{ + if(!o) return; + + addProperty("Owner", o->tempOwner, false); + addProperty("Resource", o->producedResource); + addProperty("Productivity", o->producedQuantity, false); +} + +void Inspector::updateProperties(CGResource * o) +{ + if(!o) return; + + addProperty("Amount", o->amount, false); } void Inspector::updateProperties() { if(!obj) return; - + table->setRowCount(0); //cleanup table + addProperty("Indentifier", obj); addProperty("ID", obj->ID.getNum()); addProperty("SubID", obj->subID); addProperty("InstanceName", obj->instanceName); addProperty("TypeName", obj->typeName); addProperty("SubTypeName", obj->subTypeName); - addProperty("Owner", obj->tempOwner, false); - auto factory = VLC->objtypeh->getHandlerFor(obj->ID, obj->subID); addProperty("IsStatic", factory->isStaticObject()); - + + UPDATE_OBJ_PROPERTIES(CArmedInstance); + UPDATE_OBJ_PROPERTIES(CGTownInstance); + UPDATE_OBJ_PROPERTIES(CGArtifact); + UPDATE_OBJ_PROPERTIES(CGMine); + UPDATE_OBJ_PROPERTIES(CGResource); + table->show(); } +//===============IMPLEMENT PROPERTY UPDATE================================ +void Inspector::setProperty(const QString & key, const QVariant & value) +{ + if(!obj) + return; + + SET_PROPERTIES(CArmedInstance); + SET_PROPERTIES(CGTownInstance); + SET_PROPERTIES(CGArtifact); + SET_PROPERTIES(CGMine); + SET_PROPERTIES(CGResource); +} + +void Inspector::setProperty(CArmedInstance * object, const QString & key, const QVariant & value) +{ + if(!object) + return; + + if(key == "Owner") + { + PlayerColor owner(value.toString().toInt()); + if(value == "NEUTRAL") + owner = PlayerColor::NEUTRAL; + if(value == "UNFLAGGABLE") + owner = PlayerColor::UNFLAGGABLE; + object->tempOwner = owner; + return; + } +} + +void Inspector::setProperty(CGTownInstance * object, const QString & key, const QVariant & value) +{ + if(!object) + return; + + if(key == "Town name") + object->name = value.toString().toStdString(); +} + +void Inspector::setProperty(CGMine * object, const QString & key, const QVariant & value) +{ + if(!object) + return; + + if(key == "Productivity") + object->producedQuantity = value.toString().toInt(); +} + +void Inspector::setProperty(CGArtifact * object, const QString & key, const QVariant & value) +{ + if(!object) + return; +} + +void Inspector::setProperty(CGResource * object, const QString & key, const QVariant & value) +{ + if(!object) + return; + + if(key == "Amount") + object->amount = value.toString().toInt(); +} + +//===============IMPLEMENT PROPERTY VALUE TYPE============================ QTableWidgetItem * Inspector::addProperty(CGObjectInstance * value) { using NumericPointer = unsigned long long; static_assert(sizeof(CGObjectInstance *) == sizeof(NumericPointer), - "Compilied for 64 bit arcitecture. Use NumericPointer = unsigned int"); + "Compilied for 64 bit arcitecture. Use NumericPointer = unsigned int"); return new QTableWidgetItem(QString::number(reinterpret_cast(value))); } +QTableWidgetItem * Inspector::addProperty(unsigned int value) +{ + return new QTableWidgetItem(QString::number(value)); +} + QTableWidgetItem * Inspector::addProperty(int value) { return new QTableWidgetItem(QString::number(value)); @@ -165,6 +245,47 @@ QTableWidgetItem * Inspector::addProperty(const PlayerColor & value) return new QTableWidgetItem(str); } +QTableWidgetItem * Inspector::addProperty(const Res::ERes & value) +{ + QString str; + switch (value) { + case Res::ERes::WOOD: + str = "WOOD"; + break; + case Res::ERes::ORE: + str = "ORE"; + break; + case Res::ERes::SULFUR: + str = "SULFUR"; + break; + case Res::ERes::GEMS: + str = "GEMS"; + break; + case Res::ERes::MERCURY: + str = "MERCURY"; + break; + case Res::ERes::CRYSTAL: + str = "CRYSTAL"; + break; + case Res::ERes::GOLD: + str = "GOLD"; + break; + default: + break; + } + return new QTableWidgetItem(str); +} + +//======================================================================== + +Inspector::Inspector(CGObjectInstance * o, QTableWidget * t): obj(o), table(t) +{ +} + +/* + * Delegates + */ + QWidget * PlayerColorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.data().canConvert()) diff --git a/mapeditor/inspector.h b/mapeditor/inspector.h index a18611d07..206c7c83c 100644 --- a/mapeditor/inspector.h +++ b/mapeditor/inspector.h @@ -6,56 +6,94 @@ #include #include "../lib/int3.h" #include "../lib/GameConstants.h" +#include "../lib/mapObjects/MapObjects.h" +#include "../lib/ResourceSet.h" -class CGObjectInstance; -class CGTownInstance; +#define DECLARE_OBJ_TYPE(x) void initialize(x*); +#define DECLARE_OBJ_PROPERTY_METHODS(x) \ +void updateProperties(x*); \ +void setProperty(x*, const QString &, const QVariant &); -class Initializer -{ -public: - Initializer(CGObjectInstance *); -}; +#define INIT_OBJ_TYPE(x) initialize(dynamic_cast(o)) +#define UPDATE_OBJ_PROPERTIES(x) updateProperties(dynamic_cast(obj)) +#define SET_PROPERTIES(x) setProperty(dynamic_cast(obj), key, value) + +//===============DECLARE MAP OBJECTS====================================== +DECLARE_OBJ_TYPE(CArmedInstance); +DECLARE_OBJ_TYPE(CGTownInstance); +DECLARE_OBJ_TYPE(CGArtifact); +DECLARE_OBJ_TYPE(CGMine); +DECLARE_OBJ_TYPE(CGResource); class Inspector { +protected: +//===============DECLARE PROPERTIES SETUP================================= + DECLARE_OBJ_PROPERTY_METHODS(CArmedInstance); + DECLARE_OBJ_PROPERTY_METHODS(CGTownInstance); + DECLARE_OBJ_PROPERTY_METHODS(CGArtifact); + DECLARE_OBJ_PROPERTY_METHODS(CGMine); + DECLARE_OBJ_PROPERTY_METHODS(CGResource); + +//===============DECLARE PROPERTY VALUE TYPE============================== + QTableWidgetItem * addProperty(unsigned int value); + QTableWidgetItem * addProperty(int value); + QTableWidgetItem * addProperty(const std::string & value); + QTableWidgetItem * addProperty(const QString & value); + QTableWidgetItem * addProperty(const int3 & value); + QTableWidgetItem * addProperty(const PlayerColor & value); + QTableWidgetItem * addProperty(const Res::ERes & value); + QTableWidgetItem * addProperty(bool value); + QTableWidgetItem * addProperty(CGObjectInstance * value); + +//===============END OF DECLARATION======================================= public: Inspector(CGObjectInstance *, QTableWidget *); void setProperty(const QString & key, const QVariant & value); void updateProperties(); - + protected: - QTableWidgetItem * addProperty(int value); - QTableWidgetItem * addProperty(const std::string & value); - QTableWidgetItem * addProperty(const QString & value); - QTableWidgetItem * addProperty(const int3 & value); - QTableWidgetItem * addProperty(const PlayerColor & value); - QTableWidgetItem * addProperty(bool value); - QTableWidgetItem * addProperty(CGObjectInstance * value); - - void setProperty(CGTownInstance * obj, const QString & key, const QVariant & value); template void addProperty(const QString & key, const T & value, bool restricted = true) { - auto * itemKey = new QTableWidgetItem(key); auto * itemValue = addProperty(value); - itemKey->setFlags(Qt::NoItemFlags); if(restricted) itemValue->setFlags(Qt::NoItemFlags); - - if(table->rowCount() < row + 1) + + QTableWidgetItem * itemKey = nullptr; + if(keyItems.contains(key)) + { + itemKey = keyItems[key]; + table->setItem(table->row(itemKey), 1, itemValue); + } + else + { + itemKey = new QTableWidgetItem(key); + itemKey->setFlags(Qt::NoItemFlags); + keyItems[key] = itemKey; + table->setRowCount(row + 1); - table->setItem(row, 0, itemKey); - table->setItem(row, 1, itemValue); - ++row; + table->setItem(row, 0, itemKey); + table->setItem(row, 1, itemValue); + ++row; + } } protected: int row = 0; QTableWidget * table; CGObjectInstance * obj; + QMap keyItems; +}; + + +class Initializer +{ +public: + Initializer(CGObjectInstance *); }; class PlayerColorDelegate : public QStyledItemDelegate diff --git a/mapeditor/mainwindow.cpp b/mapeditor/mainwindow.cpp index 9e32d9a98..ae62fbf6c 100644 --- a/mapeditor/mainwindow.cpp +++ b/mapeditor/mainwindow.cpp @@ -62,6 +62,7 @@ MainWindow::MainWindow(QWidget *parent) : controller(this) { ui->setupUi(this); + setTitle(); // Set current working dir to executable folder. // This is important on Mac for relative paths to work inside DMG. @@ -158,22 +159,24 @@ void MainWindow::reloadMap(int level) //sceneMini->addPixmap(minimap); } +void MainWindow::setTitle() +{ + QString title = QString("%1%2 - %3 (v%4)").arg(filename, unsaved ? "*" : "", VCMI_EDITOR_NAME, VCMI_EDITOR_VERSION); + setWindowTitle(title); +} + void MainWindow::mapChanged() { unsaved = true; - setWindowTitle(filename + "* - VCMI Map Editor"); + setTitle(); } void MainWindow::initializeMap(bool isNew) { unsaved = isNew; if(isNew) - { filename.clear(); - setWindowTitle("* - VCMI Map Editor"); - } - else - setWindowTitle(filename + " - VCMI Map Editor"); + setTitle(); mapLevel = 0; ui->mapView->setScene(controller.scene(mapLevel)); @@ -239,7 +242,7 @@ void MainWindow::saveMap() } unsaved = false; - setWindowTitle(filename + " - VCMI Map Editor"); + setTitle(); } void MainWindow::on_actionSave_as_triggered() @@ -793,7 +796,7 @@ void MainWindow::on_actionMapSettings_triggered() void MainWindow::on_actionPlayers_settings_triggered() { - auto settingsDialog = new PlayerSettings(*controller.map(), this); + auto settingsDialog = new PlayerSettings(controller, this); settingsDialog->setWindowModality(Qt::WindowModal); settingsDialog->setModal(true); } diff --git a/mapeditor/mainwindow.h b/mapeditor/mainwindow.h index b1774f700..85e895093 100644 --- a/mapeditor/mainwindow.h +++ b/mapeditor/mainwindow.h @@ -97,6 +97,7 @@ private: void addGroupIntoCatalog(const std::string & groupName, bool staticOnly, int ID); void changeBrushState(int idx); + void setTitle(); private: Ui::MainWindow *ui; diff --git a/mapeditor/playerparams.cpp b/mapeditor/playerparams.cpp index 1af1c4a61..2b5457133 100644 --- a/mapeditor/playerparams.cpp +++ b/mapeditor/playerparams.cpp @@ -1,21 +1,68 @@ #include "StdInc.h" #include "playerparams.h" #include "ui_playerparams.h" +#include "../lib/CTownHandler.h" -PlayerParams::PlayerParams(const CMapHeader & mapHeader, int playerId, QWidget *parent) : +PlayerParams::PlayerParams(MapController & ctrl, int playerId, QWidget *parent) : QWidget(parent), - ui(new Ui::PlayerParams) + ui(new Ui::PlayerParams), + controller(ctrl) { ui->setupUi(this); playerColor = playerId; - assert(mapHeader.players.size() > playerColor); - playerInfo = mapHeader.players[playerColor]; + assert(controller.map()->players.size() > playerColor); + playerInfo = controller.map()->players[playerColor]; + + //load factions + for(auto idx : VLC->townh->getAllowedFactions()) + { + CFaction * faction = VLC->townh->objects.at(idx); + auto * item = new QListWidgetItem(QString::fromStdString(faction->name)); + item->setData(Qt::UserRole, QVariant::fromValue(idx)); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + ui->allowedFactions->addItem(item); + if(playerInfo.allowedFactions.count(idx)) + item->setCheckState(Qt::Checked); + else + item->setCheckState(Qt::Unchecked); + } + QObject::connect(ui->allowedFactions, SIGNAL(itemChanged(QListWidgetItem*)), + this, SLOT(allowedFactionsCheck(QListWidgetItem*))); - if(playerInfo.canComputerPlay) - ui->radioCpu->setChecked(true); - if(playerInfo.canHumanPlay) + //load checks + bool canHumanPlay = playerInfo.canHumanPlay; //need variable to restore after signal received + playerInfo.canComputerPlay = true; //computer always can play + ui->radioCpu->setChecked(true); + if(canHumanPlay) ui->radioHuman->setChecked(true); + if(playerInfo.isFactionRandom) + ui->randomFaction->setChecked(true); + if(playerInfo.generateHeroAtMainTown) + ui->generateHero->setChecked(true); + + //load towns + int foundMainTown = -1; + for(int i = 0; i < controller.map()->towns.size(); ++i) + { + auto town = controller.map()->towns[i]; + if(town->getOwner().getNum() == playerColor) + { + if(playerInfo.hasMainTown && playerInfo.posOfMainTown == town->pos) + foundMainTown = i; + ui->mainTown->addItem(QString::fromStdString(town->getObjectName()), QVariant::fromValue(i)); + } + } + + if(foundMainTown > -1) + { + ui->mainTown->setCurrentIndex(foundMainTown + 1); + } + else + { + playerInfo.hasMainTown = false; + playerInfo.posOfMainTown = int3(-1, -1, -1); + } ui->playerColor->setTitle(QString("PlayerID: %1").arg(playerId)); show(); @@ -39,3 +86,40 @@ void PlayerParams::on_radioCpu_toggled(bool checked) playerInfo.canHumanPlay = false; } + +void PlayerParams::on_generateHero_stateChanged(int arg1) +{ + playerInfo.generateHeroAtMainTown = ui->generateHero->isChecked(); +} + + +void PlayerParams::on_randomFaction_stateChanged(int arg1) +{ + playerInfo.isFactionRandom = ui->randomFaction->isChecked(); +} + + +void PlayerParams::allowedFactionsCheck(QListWidgetItem * item) +{ + if(item->checkState() == Qt::Checked) + playerInfo.allowedFactions.insert(item->data(Qt::UserRole).toInt()); + else + playerInfo.allowedFactions.erase(item->data(Qt::UserRole).toInt()); +} + + +void PlayerParams::on_mainTown_activated(int index) +{ + if(index == 0) //default + { + playerInfo.hasMainTown = false; + playerInfo.posOfMainTown = int3(-1, -1, -1); + } + else + { + auto town = controller.map()->towns.at(ui->mainTown->currentData().toInt()); + playerInfo.hasMainTown = true; + playerInfo.posOfMainTown = town->pos; + } +} + diff --git a/mapeditor/playerparams.h b/mapeditor/playerparams.h index ee62c75f7..4219bdafb 100644 --- a/mapeditor/playerparams.h +++ b/mapeditor/playerparams.h @@ -3,6 +3,7 @@ #include #include "../lib/mapping/CMap.h" +#include "mapcontroller.h" namespace Ui { class PlayerParams; @@ -13,7 +14,7 @@ class PlayerParams : public QWidget Q_OBJECT public: - explicit PlayerParams(const CMapHeader & mapHeader, int playerId, QWidget *parent = nullptr); + explicit PlayerParams(MapController & controller, int playerId, QWidget *parent = nullptr); ~PlayerParams(); PlayerInfo playerInfo; @@ -24,8 +25,18 @@ private slots: void on_radioCpu_toggled(bool checked); + void on_mainTown_activated(int index); + + void on_generateHero_stateChanged(int arg1); + + void on_randomFaction_stateChanged(int arg1); + + void allowedFactionsCheck(QListWidgetItem *); + private: Ui::PlayerParams *ui; + + MapController & controller; }; #endif // PLAYERPARAMS_H diff --git a/mapeditor/playerparams.ui b/mapeditor/playerparams.ui index 24a84b047..bd2444b8f 100644 --- a/mapeditor/playerparams.ui +++ b/mapeditor/playerparams.ui @@ -7,7 +7,7 @@ 0 0 505 - 146 + 160 @@ -101,23 +101,34 @@ - + + + + + (default) + + + + + + + true + 0 0 - - - - - - - - - All factions allowed + + Qt::ClickFocus + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::NoSelection diff --git a/mapeditor/playersettings.cpp b/mapeditor/playersettings.cpp index 2a49e14b3..b19068123 100644 --- a/mapeditor/playersettings.cpp +++ b/mapeditor/playersettings.cpp @@ -4,20 +4,20 @@ #include "playerparams.h" #include "mainwindow.h" -PlayerSettings::PlayerSettings(CMapHeader & mapHeader, QWidget *parent) : +PlayerSettings::PlayerSettings(MapController & ctrl, QWidget *parent) : QDialog(parent), ui(new Ui::PlayerSettings), - header(mapHeader) + controller(ctrl) { ui->setupUi(this); show(); int players = 0; - for(auto & p : header.players) + for(auto & p : controller.map()->players) { if(p.canAnyonePlay()) { - paramWidgets.push_back(new PlayerParams(header, players)); + paramWidgets.push_back(new PlayerParams(controller, players)); ui->playersLayout->addWidget(paramWidgets.back()); ++players; } @@ -36,23 +36,23 @@ PlayerSettings::~PlayerSettings() void PlayerSettings::on_playersCount_currentIndexChanged(int index) { - assert(index + 2 <= header.players.size()); + assert(index + 2 <= controller.map()->players.size()); for(int i = 0; i < index + 2; ++i) { if(i < paramWidgets.size()) continue; - auto & p = header.players[i]; + auto & p = controller.map()->players[i]; p.canComputerPlay = true; - paramWidgets.push_back(new PlayerParams(header, i)); + paramWidgets.push_back(new PlayerParams(controller, i)); ui->playersLayout->addWidget(paramWidgets.back()); } assert(!paramWidgets.empty()); for(int i = paramWidgets.size() - 1; i >= index + 2; --i) { - auto & p = header.players[i]; + auto & p = controller.map()->players[i]; p.canComputerPlay = false; p.canHumanPlay = false; ui->playersLayout->removeWidget(paramWidgets[i]); @@ -66,10 +66,10 @@ void PlayerSettings::on_pushButton_clicked() { for(auto * w : paramWidgets) { - header.players[w->playerColor] = w->playerInfo; + controller.map()->players[w->playerColor] = w->playerInfo; } - static_cast(parent())->controller.commitChangeWithoutRedraw(); + controller.commitChangeWithoutRedraw(); close(); } diff --git a/mapeditor/playersettings.h b/mapeditor/playersettings.h index bf6b13dac..d6fab335f 100644 --- a/mapeditor/playersettings.h +++ b/mapeditor/playersettings.h @@ -14,7 +14,7 @@ class PlayerSettings : public QDialog Q_OBJECT public: - explicit PlayerSettings(CMapHeader & mapHeader, QWidget *parent = nullptr); + explicit PlayerSettings(MapController & controller, QWidget *parent = nullptr); ~PlayerSettings(); private slots: @@ -27,8 +27,8 @@ private: Ui::PlayerSettings *ui; std::vector paramWidgets; - - CMapHeader & header; + + MapController & controller; }; #endif // PLAYERSETTINGS_H